fix #684: correctly handle multiple mixed key sources separated by newlines

This commit is contained in:
jkhall81 2025-12-26 11:20:42 -07:00
parent 5f44339fa5
commit c8a5dc49af
3 changed files with 74 additions and 24 deletions

View file

@ -0,0 +1,3 @@
---
bugfixes:
- authorized_key - fix a bug where providing multiple key sources (URLs or file paths) separated by newlines would fail or only process the first source. The module now correctly resolves each source in a newline-separated list before processing the keys (https://github.com/ansible-collections/ansible.posix/issues/684).

View file

@ -568,32 +568,47 @@ def enforce_state(module, params):
follow = params.get('follow', False) follow = params.get('follow', False)
error_msg = "Error getting key from: %s" error_msg = "Error getting key from: %s"
# if the key is a url or file, request it and use it as key source # Split the raw input into individual lines first
if key.startswith("http"): # This is the key to supporting mixed URLs and raw keys
try: raw_sources = [s.strip() for s in key.splitlines() if s.strip()]
resp, info = fetch_url(module, key) final_keys = []
if info['status'] != 200:
module.fail_json(msg=error_msg % key)
else:
key = resp.read()
except Exception:
module.fail_json(msg=error_msg % key)
# resp.read gives bytes on python3, convert to native string type for source in raw_sources:
key = to_native(key, errors='surrogate_or_strict') if source.startswith('#'):
continue
if key.startswith("file"): validate_certs = params.get('validate_certs', True)
# if the key is an absolute path, check for existense and use it as a key source # Identify if this specific line is a URL
key_path = urlparse(key).path if source.startswith(('http://', 'https://')):
if not os.path.exists(key_path): try:
module.fail_json(msg="Path to a key file not found: %s" % key_path) resp, info = fetch_url(module, source, validate_certs=validate_certs)
if not os.path.isfile(key_path): if info['status'] != 200:
module.fail_json(msg="Path to a key is a directory and must be a file: %s" % key_path) module.fail_json(msg=error_msg % source)
try:
with open(key_path, 'r') as source_fh: # Fetch the keys from the URL and convert to native string
key = source_fh.read() url_content = to_native(resp.read(), errors='surrogate_or_strict')
except OSError as e: if url_content:
module.fail_json(msg="Failed to read key file %s : %s" % (key_path, to_native(e))) final_keys.append(url_content)
except Exception:
module.fail_json(msg=error_msg % source)
# Identify if this specific line is a local file path
elif source.startswith("file://"):
key_path = urlparse(source).path
if not os.path.exists(key_path):
module.fail_json(msg="Path to a key file not found: %s" % key_path)
try:
with open(key_path, 'r') as source_fh:
final_keys.append(source_fh.read())
except OSError as e:
module.fail_json(msg="Failed to read key file %s : %s" % (key_path, to_native(e)))
# If it's not a URL or a File, it's a raw SSH key
else:
final_keys.append(source)
# Join everything back together for the rest of the module's existing logic
key = "\n".join(final_keys)
# extract individual keys into an array, skipping blank lines and comments # extract individual keys into an array, skipping blank lines and comments
new_keys = [s for s in key.splitlines() if s and not s.startswith('#')] new_keys = [s for s in key.splitlines() if s and not s.startswith('#')]

View file

@ -95,3 +95,35 @@
- result.changed == False - result.changed == False
- multiple_keys_comments.stdout == multiple_key_exclusive.strip() - multiple_keys_comments.stdout == multiple_key_exclusive.strip()
- result.key_options == None - result.key_options == None
- name: Create local key source simulating a URL response
ansible.builtin.copy:
dest: "{{ output_dir | expanduser }}/simulated_url.txt"
content: |
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDXJ1HrhQEXOexamplekey1fromurl test1@example.com
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGexamplekey2fromurl test2@example.com
mode: "0644"
- name: Add mixed keys (file source + raw key)
ansible.posix.authorized_key:
user: root
key: |
file://{{ output_dir | expanduser }}/simulated_url.txt
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqmqp9pP9pP9pP9pP9pP9pP9pP9pP9pP9pP9pP test@example.com
state: present
path: "{{ output_dir | expanduser }}/authorized_keys"
exclusive: true
register: mixed_source_result
- name: Get the file content for mixed source verification
ansible.builtin.command: /bin/cat "{{ output_dir | expanduser }}/authorized_keys"
changed_when: false
register: mixed_file_content
- name: Assert mixed sources were merged and processed correctly
ansible.builtin.assert:
that:
- mixed_source_result.changed == True
- mixed_file_content.stdout_lines | length == 3
- "'test1@example.com' in mixed_file_content.stdout"
- "'test@example.com' in mixed_file_content.stdout"