From 84780db7b31b4898df387ee3b11dee28e9a043ce Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sun, 31 Aug 2025 08:07:36 +0300 Subject: [PATCH 1/8] Remove redundant required attributes in systemd_creds_encrypt --- plugins/modules/systemd_creds_encrypt.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/modules/systemd_creds_encrypt.py b/plugins/modules/systemd_creds_encrypt.py index 2c4912427ef..1d1f60dcb46 100644 --- a/plugins/modules/systemd_creds_encrypt.py +++ b/plugins/modules/systemd_creds_encrypt.py @@ -33,18 +33,15 @@ 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: @@ -56,14 +53,12 @@ - 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. """ From c6193e024a373d6f00d4f2309f38ffb5234f9be7 Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sat, 2 Aug 2025 16:19:00 +0300 Subject: [PATCH 2/8] Introduce the dest option for systemd_creds_encrypt --- plugins/modules/systemd_creds_encrypt.py | 27 +++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/plugins/modules/systemd_creds_encrypt.py b/plugins/modules/systemd_creds_encrypt.py index 1d1f60dcb46..df479a8fa11 100644 --- a/plugins/modules/systemd_creds_encrypt.py +++ b/plugins/modules/systemd_creds_encrypt.py @@ -41,6 +41,7 @@ pretty: description: - Pretty print the output so that it may be pasted directly into a unit file. + - Does not affect anything when the O(dest) option is used. type: bool default: false secret: @@ -48,6 +49,11 @@ - The secret to encrypt. type: str required: true + dest: + description: + - The destination for the credential file. + type: path + version_added: 11.3.0 timestamp: description: - The timestamp to embed into the encrypted credential. @@ -75,6 +81,13 @@ - name: Print the encrypted secret ansible.builtin.debug: msg: "{{ encrypted_secret }}" + +- name: Create a credential file + become: true + community.general.systemd_creds_encrypt: + name: db + secret: access_token + dest: /etc/credstore.encrypted/db.cred """ RETURN = r""" @@ -98,6 +111,7 @@ def main(): secret=dict(type="str", required=True, no_log=True), timestamp=dict(type="str"), user=dict(type="str"), + dest=dict(type="path"), ), supports_check_mode=True, ) @@ -110,6 +124,7 @@ def main(): secret = module.params["secret"] timestamp = module.params["timestamp"] user = module.params["user"] + dest = module.params["dest"] encrypt_cmd = [cmd, "encrypt"] if name: @@ -118,19 +133,25 @@ def main(): encrypt_cmd.append("--name=") if not_after: encrypt_cmd.append("--not-after=" + not_after) - if pretty: + if pretty and not dest: encrypt_cmd.append("--pretty") if timestamp: encrypt_cmd.append("--timestamp=" + timestamp) if user: encrypt_cmd.append("--uid=" + user) - encrypt_cmd.extend(["-", "-"]) + + encrypt_cmd.append("-") + + if dest: + encrypt_cmd.append(dest) + else: + encrypt_cmd.append("-") rc, stdout, stderr = module.run_command(encrypt_cmd, data=secret, binary_data=True) module.exit_json( changed=False, - value=stdout, + value=stdout if not dest else None, rc=rc, stderr=stderr, ) From 8aa1262acaf08ad0c4d92e8da4fa181afb89d5c3 Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sat, 2 Aug 2025 16:39:34 +0300 Subject: [PATCH 3/8] Test dest option in systemd_creds_encrypt --- .../systemd_creds_encrypt/meta/main.yml | 7 ++++++ .../systemd_creds_encrypt/tasks/main.yaml | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/integration/targets/systemd_creds_encrypt/meta/main.yml diff --git a/tests/integration/targets/systemd_creds_encrypt/meta/main.yml b/tests/integration/targets/systemd_creds_encrypt/meta/main.yml new file mode 100644 index 00000000000..982de6eb035 --- /dev/null +++ b/tests/integration/targets/systemd_creds_encrypt/meta/main.yml @@ -0,0 +1,7 @@ +--- +# 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 + +dependencies: + - setup_remote_tmp_dir diff --git a/tests/integration/targets/systemd_creds_encrypt/tasks/main.yaml b/tests/integration/targets/systemd_creds_encrypt/tasks/main.yaml index 8ed45b2dc3c..7f2ef8ba17b 100644 --- a/tests/integration/targets/systemd_creds_encrypt/tasks/main.yaml +++ b/tests/integration/targets/systemd_creds_encrypt/tasks/main.yaml @@ -53,3 +53,25 @@ - '"SetCredentialEncrypted=web: " in pretty_encrypted_secret.value' fail_msg: "SetCredentialEncrypted is not in the output" success_msg: "SetCredentialEncrypted is in the output" + + - name: Ensure the dest file doesn't already exist + ansible.builtin.file: + path: "{{ remote_tmp_dir }}/systemd_cred_encrypt_test_secret.cred" + state: absent + + - name: Encrypt secret into the dest file + community.general.systemd_creds_encrypt: + secret: token + dest: "{{ remote_tmp_dir }}/systemd_cred_encrypt_test_secret.cred" + + - name: Check if the dest file exists + stat: + path: "{{ remote_tmp_dir }}/systemd_cred_encrypt_test_secret.cred" + register: dest_file + + - name: Assert that the dest file exists + ansible.builtin.assert: + that: + - "dest_file.stat.exists" + fail_msg: "The dest file doesn't exist" + success_msg: "The dest file does exist" From f6e67fe3634541d631dccda1ae2e7e6cec5f8513 Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sat, 2 Aug 2025 17:11:00 +0300 Subject: [PATCH 4/8] Add changelog fragment for dest option in systemd-creds-encrypt --- .../fragments/10549-systemd-creds-encrypt-dest-option-added.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelogs/fragments/10549-systemd-creds-encrypt-dest-option-added.yml diff --git a/changelogs/fragments/10549-systemd-creds-encrypt-dest-option-added.yml b/changelogs/fragments/10549-systemd-creds-encrypt-dest-option-added.yml new file mode 100644 index 00000000000..5981b4e93f1 --- /dev/null +++ b/changelogs/fragments/10549-systemd-creds-encrypt-dest-option-added.yml @@ -0,0 +1,2 @@ +minor_changes: + - systemd_creds_encrypt - add ``dest`` option to control path to the credential file to be created (https://github.com/ansible-collections/community.general/pull/10549). From b34d0fde94683e24586f59ae110cb29a30d21db5 Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sun, 3 Aug 2025 09:31:52 +0300 Subject: [PATCH 5/8] Make dest and pretty options mutually exclusive in systemd_creds_encrypt --- plugins/modules/systemd_creds_encrypt.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/modules/systemd_creds_encrypt.py b/plugins/modules/systemd_creds_encrypt.py index df479a8fa11..6cc2c5c4088 100644 --- a/plugins/modules/systemd_creds_encrypt.py +++ b/plugins/modules/systemd_creds_encrypt.py @@ -41,7 +41,6 @@ pretty: description: - Pretty print the output so that it may be pasted directly into a unit file. - - Does not affect anything when the O(dest) option is used. type: bool default: false secret: @@ -114,6 +113,9 @@ def main(): dest=dict(type="path"), ), supports_check_mode=True, + mutually_exclusive=[ + ['pretty', 'dest'] + ] ) cmd = module.get_bin_path("systemd-creds", required=True) @@ -133,7 +135,7 @@ def main(): encrypt_cmd.append("--name=") if not_after: encrypt_cmd.append("--not-after=" + not_after) - if pretty and not dest: + if pretty: encrypt_cmd.append("--pretty") if timestamp: encrypt_cmd.append("--timestamp=" + timestamp) From ce4116bd673770c5df30b4b857b823a4ab11d061 Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sun, 3 Aug 2025 09:32:48 +0300 Subject: [PATCH 6/8] Support check mode in systemd_creds_encrypt --- plugins/modules/systemd_creds_encrypt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/systemd_creds_encrypt.py b/plugins/modules/systemd_creds_encrypt.py index 6cc2c5c4088..fce3e00b380 100644 --- a/plugins/modules/systemd_creds_encrypt.py +++ b/plugins/modules/systemd_creds_encrypt.py @@ -144,7 +144,7 @@ def main(): encrypt_cmd.append("-") - if dest: + if dest and not module.check_mode: encrypt_cmd.append(dest) else: encrypt_cmd.append("-") From 451c2e3c15c16f798237699693bec69284de767e Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sun, 3 Aug 2025 14:40:34 +0300 Subject: [PATCH 7/8] Don't disable using dest filename as credential name in systemd_creds_encrypt --- plugins/modules/systemd_creds_encrypt.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/modules/systemd_creds_encrypt.py b/plugins/modules/systemd_creds_encrypt.py index fce3e00b380..5cc6dabfb63 100644 --- a/plugins/modules/systemd_creds_encrypt.py +++ b/plugins/modules/systemd_creds_encrypt.py @@ -32,6 +32,11 @@ name: description: - The credential name to embed in the encrypted credential data. + When using the O(dest) option, you can set this to an empty + string ("") to prevent C(systemd-creds encrypt) from using + filename from O(dest) as the credential name. Conversely, + leave out the O(name) option to let C(systemd-creds encrypt) + use the filename from O(dest) as the credential name. type: str not_after: description: @@ -129,9 +134,9 @@ def main(): dest = module.params["dest"] encrypt_cmd = [cmd, "encrypt"] - if name: + if name is not None: encrypt_cmd.append("--name=" + name) - else: + elif not dest: encrypt_cmd.append("--name=") if not_after: encrypt_cmd.append("--not-after=" + not_after) From 74e2aa180a46e530d340d15a29ee202dd0c35b7e Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sun, 3 Aug 2025 15:46:24 +0300 Subject: [PATCH 8/8] Make systemd_creds_encrypt report changes --- plugins/modules/systemd_creds_encrypt.py | 28 ++++++++++++++- .../systemd_creds_encrypt/tasks/main.yaml | 34 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/plugins/modules/systemd_creds_encrypt.py b/plugins/modules/systemd_creds_encrypt.py index 5cc6dabfb63..c432e4d816c 100644 --- a/plugins/modules/systemd_creds_encrypt.py +++ b/plugins/modules/systemd_creds_encrypt.py @@ -103,6 +103,7 @@ """ from ansible.module_utils.basic import AnsibleModule +import os def main(): @@ -133,6 +134,31 @@ def main(): user = module.params["user"] dest = module.params["dest"] + if dest and os.path.isfile(dest): + decrypt_cmd = [cmd, "decrypt"] + if name: + decrypt_cmd.append("--name=" + name) + + decrypt_cmd.append(dest) + decrypt_cmd.append("-") + + rc, stdout, stderr = module.run_command(decrypt_cmd) + if rc != 0: + return module.fail_json( + "The credential file already exists; Couldn't decrypt to verify if it contains the same secret", + changed=False, + value=None, + rc=rc, + stderr=stderr, + ) + elif stdout == secret: + return module.exit_json( + changed=False, + value=None, + rc=rc, + stderr=stderr, + ) + encrypt_cmd = [cmd, "encrypt"] if name is not None: encrypt_cmd.append("--name=" + name) @@ -157,7 +183,7 @@ def main(): rc, stdout, stderr = module.run_command(encrypt_cmd, data=secret, binary_data=True) module.exit_json( - changed=False, + changed=bool(dest), value=stdout if not dest else None, rc=rc, stderr=stderr, diff --git a/tests/integration/targets/systemd_creds_encrypt/tasks/main.yaml b/tests/integration/targets/systemd_creds_encrypt/tasks/main.yaml index 7f2ef8ba17b..bdf48d842e6 100644 --- a/tests/integration/targets/systemd_creds_encrypt/tasks/main.yaml +++ b/tests/integration/targets/systemd_creds_encrypt/tasks/main.yaml @@ -63,6 +63,14 @@ community.general.systemd_creds_encrypt: secret: token dest: "{{ remote_tmp_dir }}/systemd_cred_encrypt_test_secret.cred" + register: encrypted_dest + + - name: Assert that the task changes when no secret was present + ansible.builtin.assert: + that: + - "encrypted_dest.changed" + fail_msg: "The task didn't change, but should have" + success_msg: "The task changed, as it should have" - name: Check if the dest file exists stat: @@ -75,3 +83,29 @@ - "dest_file.stat.exists" fail_msg: "The dest file doesn't exist" success_msg: "The dest file does exist" + + - name: Encrypt the same secret into the same dest file as before + community.general.systemd_creds_encrypt: + secret: token + dest: "{{ remote_tmp_dir }}/systemd_cred_encrypt_test_secret.cred" + register: encrypted_dest_again + + - name: Assert that the task doesn't change with the same secret + ansible.builtin.assert: + that: + - "not encrypted_dest_again.changed" + fail_msg: "The task changed, but shouldn't have" + success_msg: "The task didn't change, as it should have" + + - name: Encrypt a different secret into the same dest file as before + community.general.systemd_creds_encrypt: + secret: another token + dest: "{{ remote_tmp_dir }}/systemd_cred_encrypt_test_secret.cred" + register: encrypted_dest_again + + - name: Assert that the task changes with a different secret + ansible.builtin.assert: + that: + - "encrypted_dest_again.changed" + fail_msg: "The task didn't change, but should have" + success_msg: "The task changed, as it should have"