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 16 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
159 changes: 159 additions & 0 deletions plugins/modules/systemd_creds_decrypt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#!/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
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
details:
- This action does not modify state.
diff_mode:
support: N/A
details:
- This action does not modify state.
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 }}"
"""

RETURN = r"""
value:
description:
- The decrypted secret.
- Note that Ansible only supports returning UTF-8 encoded strings.
If the decrypted secret is binary data, or a string encoded in another
way, use O(transcode=base64) or O(transcode=hex) to circument this
restriction. You then need to decode the data when using it, for
example using the P(ansible.builtin.b64decode#filter) filter.
type: str
returned: always
sample: "access_token"
"""


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


def main():
"""Decrypt 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"]

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(decrypt_cmd, data=secret, binary_data=True)

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


if __name__ == "__main__":
main()
149 changes: 149 additions & 0 deletions plugins/modules/systemd_creds_encrypt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/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'
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
details:
- This action does not modify state.
diff_mode:
support: N/A
details:
- This action does not modify state.
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
become: true
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 }}"
"""

RETURN = r"""
value:
description: The Base64 encoded encrypted secret.
type: str
returned: always
sample: "WhQZht+JQJax1aZemmGLxmAAAA..."
"""

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"]

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(encrypt_cmd, data=secret, binary_data=True)

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


if __name__ == "__main__":
main()
11 changes: 11 additions & 0 deletions tests/integration/targets/systemd_creds_decrypt/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 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/1
skip/aix
skip/freebsd
skip/osx
skip/macos
58 changes: 58 additions & 0 deletions tests/integration/targets/systemd_creds_decrypt/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
# 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
become: true
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.value }}"
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
ansible.builtin.assert:
that:
- decrypted_secret.value == '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.value }}"
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
ansible.builtin.assert:
that:
- decrypted_secret_hex.value == '6163636573735f746f6b656e'
fail_msg: "Decrypted secret is not the same as the original secret"
success_msg: "Decrypted secret is the same as the original secret"
Loading
Loading