Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for systemd creds encrypt/decrypt #9383

Merged
merged 23 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/BOTMETA.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,10 @@ files:
maintainers: precurse
$modules/sysrc.py:
maintainers: dlundgren
$modules/systemd_creds_decrypt.py:
maintainers: konstruktoid
$modules/systemd_creds_encrypt.py:
maintainers: konstruktoid
$modules/sysupgrade.py:
maintainers: precurse
$modules/taiga_issue.py:
Expand Down
136 changes: 136 additions & 0 deletions plugins/modules/systemd_creds_decrypt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2024, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = """
felixfontein marked this conversation as resolved.
Show resolved Hide resolved
module: systemd_creds_decrypt
short_description: Systemd's systemd-creds decrypt plugin
description:
- This module decrypts input using systemd's systemd-creds decrypt.
author:
- Thomas Sjögren (@konstruktoid)
version_added: '10.2.0'
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
options:
name:
description:
- The credential name to validate the embedded credential name.
type: str
required: false
newline:
description:
- Whether to add a trailing newline character to the end of the output,
if not present.
type: bool
required: false
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
secret:
description:
- The secret to decrypt.
type: str
required: true
timestamp:
description:
- The timestamp to use to validate the V(not-after) timestamp that
was used during encryption.
- Takes a timestamp specification in the format described in
V(systemd.time(7\\)).
type: str
required: false
transcode:
description:
- Whether to transcode the output before showing it.
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
type: str
choices: [ base64, unbase64, hex, unhex ]
required: false
user:
description:
- A user name or numeric UID when decrypting from a specific user context.
- If set to the special string V(self) it sets the user to the user
of the calling process.
- Requires C(systemd) 256 or later.
type: str
required: false
notes:
- C(systemd-creds) requires C(systemd) 250 or later.
"""

EXAMPLES = """
- name: Decrypt secret
community.general.systemd_creds_decrypt:
name: db
secret: "WhQZht+JQJax1aZemmGLxmAAAA..."
register: decrypted_secret

- name: Print the decrypted secret
ansible.builtin.debug:
msg: "{{ decrypted_secret }}"
"""


konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
from ansible.module_utils.basic import AnsibleModule


def main():
"""Encrypt secret using systemd-creds."""
module = AnsibleModule(
argument_spec=dict(
name=dict(type="str", required=False),
newline=dict(type="bool", required=False),
secret=dict(type="str", required=True, no_log=True),
timestamp=dict(type="str", required=False),
transcode=dict(
type="str",
choices=["base64", "unbase64", "hex", "unhex"],
required=False,
),
user=dict(type="str", required=False),
),
supports_check_mode=True,
)

cmd = module.get_bin_path("systemd-creds", required=True)

name = module.params["name"]
newline = module.params["newline"]
secret = module.params["secret"]
timestamp = module.params["timestamp"]
transcode = module.params["transcode"]
user = module.params["user"]

stdin_secret = ["echo", "-n", secret]
decrypt_cmd = [cmd, "decrypt"]
if name:
decrypt_cmd.append("--name=" + name)
else:
decrypt_cmd.append("--name=")
if newline:
decrypt_cmd.append("--newline=" + newline)
else:
decrypt_cmd.append("--newline=auto")
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
if timestamp:
decrypt_cmd.append("--timestamp=" + timestamp)
if transcode:
decrypt_cmd.append("--transcode=" + transcode)
if user:
decrypt_cmd.append("--uid=" + user)
decrypt_cmd.extend(["-", "-"])

rc, stdout, stderr = module.run_command(stdin_secret)
rc, stdout, stderr = module.run_command(decrypt_cmd, data=stdout)
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved

module.exit_json(
changed=False,
msg=stdout,
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
rc=rc,
stderr=stderr,
)


if __name__ == "__main__":
main()
132 changes: 132 additions & 0 deletions plugins/modules/systemd_creds_encrypt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/python

# Copyright (c) 2024, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import absolute_import, division, print_function

__metaclass__ = type


DOCUMENTATION = """
module: systemd_creds_encrypt
short_description: Systemd's systemd-creds encrypt plugin
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
description:
- This module encrypts input using systemd's systemd-creds encrypt.
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
author:
- Thomas Sjögren (@konstruktoid)
version_added: '10.2.0'
options:
name:
description:
- The credential name to embed in the encrypted credential data.
type: str
required: false
not_after:
description:
- The time when the credential shall not be used anymore.
- Takes a timestamp specification in the format described in
V(systemd.time(7\\)).
type: str
required: false
pretty:
description:
- Pretty print the output so that it may be pasted directly into a
unit file.
type: bool
required: false
default: false
secret:
description:
- The secret to encrypt.
type: str
required: true
timestamp:
description:
- The timestamp to embed into the encrypted credential.
- Takes a timestamp specification in the format described in
V(systemd.time(7\\)).
felixfontein marked this conversation as resolved.
Show resolved Hide resolved
type: str
required: false
user:
description:
- A user name or numeric UID to encrypt the credential for.
- If set to the special string V(self) it sets the user to the user
of the calling process.
- Requires C(systemd) 256 or later.
type: str
required: false
notes:
- C(systemd-creds) requires C(systemd) 250 or later.
"""

EXAMPLES = """
- name: Encrypt secret
community.general.systemd_creds_encrypt:
name: db
not_after: +48hr
secret: access_token
register: encrypted_secret

- name: Print the encrypted secret
ansible.builtin.debug:
msg: "{{ encrypted_secret }}"
"""


from ansible.module_utils.basic import AnsibleModule


def main():
"""Encrypt secret using systemd-creds."""
module = AnsibleModule(
argument_spec=dict(
name=dict(type="str", required=False),
not_after=dict(type="str", required=False),
pretty=dict(type="bool", required=False, default=False),
secret=dict(type="str", required=True, no_log=True),
timestamp=dict(type="str", required=False),
user=dict(type="str", required=False),
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
),
supports_check_mode=True,
)

cmd = module.get_bin_path("systemd-creds", required=True)

name = module.params["name"]
not_after = module.params["not_after"]
pretty = module.params["pretty"]
secret = module.params["secret"]
timestamp = module.params["timestamp"]
user = module.params["user"]

stdin_secret = ["echo", "-n", secret]
encrypt_cmd = [cmd, "encrypt"]
if name:
encrypt_cmd.append("--name=" + name)
else:
encrypt_cmd.append("--name=")
if not_after:
encrypt_cmd.append("--not-after=" + not_after)
if pretty:
encrypt_cmd.append("--pretty")
if timestamp:
encrypt_cmd.append("--timestamp=" + timestamp)
if user:
encrypt_cmd.append("--uid=" + user)
encrypt_cmd.extend(["-", "-"])

rc, stdout, stderr = module.run_command(stdin_secret)
rc, stdout, stderr = module.run_command(encrypt_cmd, data=stdout)
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved

module.exit_json(
changed=False,
msg=stdout,
rc=rc,
stderr=stderr,
)


if __name__ == "__main__":
main()
12 changes: 12 additions & 0 deletions tests/integration/targets/systemd_creds_decrypt/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

needs/root

azp/posix/vm
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
skip/aix
skip/freebsd
skip/osx
skip/macos
skip/docker
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
57 changes: 57 additions & 0 deletions tests/integration/targets/systemd_creds_decrypt/main.yaml
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

- name: Test systemd_creds_decrypt
when:
- ansible_systemd.version is defined
- ansible_systemd.version | int >= 250
block:
- name: Encrypt secret
community.general.systemd_creds_encrypt:
name: api
not_after: +48hr
secret: access_token
register: encrypted_api_secret

- name: Print the encrypted secret
ansible.builtin.debug:
msg: "{{ encrypted_api_secret }}"

- name: Decrypt secret
community.general.systemd_creds_decrypt:
name: api
newline: false
secret: "{{ encrypted_api_secret.msg }}"
register: decrypted_secret

- name: Print the decrypted secret
ansible.builtin.debug:
msg: "{{ decrypted_secret }}"

- name: Assert that the decrypted secret is the same as the original secret
assert:
that:
- decrypted_secret.msg.strip() == 'access_token'
fail_msg: "Decrypted secret is not the same as the original secret"
success_msg: "Decrypted secret is the same as the original secret"

- name: Decrypt secret into hex
community.general.systemd_creds_decrypt:
name: api
newline: false
secret: "{{ encrypted_api_secret.msg }}"
transcode: hex
register: decrypted_secret_hex

- name: Print the trancoded decrypted secret
ansible.builtin.debug:
msg: "{{ decrypted_secret_hex }}"

- name: Assert that the decrypted secret is the same as the original secret
assert:
that:
- decrypted_secret_hex.msg == '6163636573735f746f6b656e0a'
fail_msg: "Decrypted secret is not the same as the original secret"
success_msg: "Decrypted secret is the same as the original secret"
12 changes: 12 additions & 0 deletions tests/integration/targets/systemd_creds_encrypt/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

needs/root

azp/posix/vm
konstruktoid marked this conversation as resolved.
Show resolved Hide resolved
skip/aix
skip/freebsd
skip/osx
skip/macos
skip/docker
Loading
Loading