add support for systemd creds encrypt/decrypt (#9383)
* add support for systemd creds encrypt/decrypt Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * add __metaclass__ Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * Python 2.7 issues Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * update version_added and ci test aliases Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * switch to container Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * run tests in docker as well Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * move tasks into tasks/ Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * no need to call echo Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * lint and add become: Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * dont append a newline Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * don't clean newlines Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * only use module name Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * clean Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * change msg to value Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * add return values Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * update attributes and description Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * Update plugins/modules/systemd_creds_decrypt.py Co-authored-by: Felix Fontein <felix@fontein.de> * set newline default Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * Update plugins/modules/systemd_creds_encrypt.py Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Update plugins/modules/systemd_creds_encrypt.py Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Update plugins/modules/systemd_creds_encrypt.py Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * update required and spelling Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> * use single backslash Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> --------- Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com> Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>pull/9431/head
parent
d887930e49
commit
482a90e8b4
|
@ -1341,6 +1341,10 @@ files:
|
||||||
maintainers: precurse
|
maintainers: precurse
|
||||||
$modules/sysrc.py:
|
$modules/sysrc.py:
|
||||||
maintainers: dlundgren
|
maintainers: dlundgren
|
||||||
|
$modules/systemd_creds_decrypt.py:
|
||||||
|
maintainers: konstruktoid
|
||||||
|
$modules/systemd_creds_encrypt.py:
|
||||||
|
maintainers: konstruktoid
|
||||||
$modules/sysupgrade.py:
|
$modules/sysupgrade.py:
|
||||||
maintainers: precurse
|
maintainers: precurse
|
||||||
$modules/taiga_issue.py:
|
$modules/taiga_issue.py:
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
#!/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 = r"""
|
||||||
|
module: systemd_creds_decrypt
|
||||||
|
short_description: C(systemd)'s C(systemd-creds decrypt) plugin
|
||||||
|
description:
|
||||||
|
- This module decrypts input using C(systemd)'s C(systemd-creds decrypt).
|
||||||
|
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 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
|
||||||
|
default: false
|
||||||
|
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 returning it.
|
||||||
|
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"
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
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, default=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=")
|
||||||
|
decrypt_cmd.append("--newline=" + ("yes" if newline else "no"))
|
||||||
|
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()
|
|
@ -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 = r"""
|
||||||
|
module: systemd_creds_encrypt
|
||||||
|
short_description: C(systemd)'s C(systemd-creds encrypt) plugin
|
||||||
|
description:
|
||||||
|
- This module encrypts input using C(systemd)'s C(systemd-creds encrypt).
|
||||||
|
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\)).
|
||||||
|
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", default=False),
|
||||||
|
secret=dict(type="str", required=True, no_log=True),
|
||||||
|
timestamp=dict(type="str", 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"]
|
||||||
|
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()
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -0,0 +1,55 @@
|
||||||
|
---
|
||||||
|
# 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_encrypt
|
||||||
|
when:
|
||||||
|
- ansible_systemd.version is defined
|
||||||
|
- ansible_systemd.version | int >= 250
|
||||||
|
block:
|
||||||
|
- name: Encrypt secret
|
||||||
|
become: true
|
||||||
|
systemd_creds_encrypt:
|
||||||
|
name: db
|
||||||
|
not_after: +48hr
|
||||||
|
secret: access_token
|
||||||
|
register: encrypted_secret
|
||||||
|
|
||||||
|
- name: Assert encrypted secret output is base64 encoded
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- encrypted_secret.value | b64decode
|
||||||
|
fail_msg: "Encrypted secret is not base64 encoded"
|
||||||
|
success_msg: "Encrypted secret is base64 encoded"
|
||||||
|
|
||||||
|
- name: Print the encrypted secret
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ encrypted_secret }}"
|
||||||
|
|
||||||
|
- name: Assert that SetCredentialEncrypted message is not in the output
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- '"SetCredentialEncrypted" not in encrypted_secret.value'
|
||||||
|
fail_msg: "SetCredentialEncrypted is in the output"
|
||||||
|
success_msg: "SetCredentialEncrypted is not in the output"
|
||||||
|
|
||||||
|
- name: Encrypt secret
|
||||||
|
become: true
|
||||||
|
community.general.systemd_creds_encrypt:
|
||||||
|
name: web
|
||||||
|
not_after: +5y
|
||||||
|
pretty: true
|
||||||
|
secret: token
|
||||||
|
register: pretty_encrypted_secret
|
||||||
|
|
||||||
|
- name: Pretty print the encrypted secret
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ pretty_encrypted_secret }}"
|
||||||
|
|
||||||
|
- name: Assert that SetCredentialEncrypted message is in the output
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- '"SetCredentialEncrypted=web: " in pretty_encrypted_secret.value'
|
||||||
|
fail_msg: "SetCredentialEncrypted is not in the output"
|
||||||
|
success_msg: "SetCredentialEncrypted is in the output"
|
Loading…
Reference in New Issue