From 67c320fcc273ce2b84a3888e32eb9e679c997439 Mon Sep 17 00:00:00 2001 From: Sandwich Date: Thu, 12 Feb 2026 21:50:55 +0100 Subject: [PATCH] fix(vars): enforce strict list-only DNS and user.key format for IaC compatibility --- main.yml | 14 ++++--- roles/configuration/tasks/users.yml | 5 ++- roles/global_defaults/defaults/main.yml | 2 +- roles/global_defaults/tasks/system.yml | 53 ++++++++++++------------- 4 files changed, 38 insertions(+), 36 deletions(-) diff --git a/main.yml b/main.yml index aed4adb..d5f1e4c 100644 --- a/main.yml +++ b/main.yml @@ -31,7 +31,7 @@ system_user_input: "{{ (system_input.user | default({})) if (system_input.user is mapping) else {} }}" system_root_input: "{{ (system_input.root | default({})) if (system_input.root is mapping) else {} }}" prompt_user_name: "{{ user_name | default(system_user_name | default(''), true) | string }}" - prompt_user_key: "{{ user_public_key | default(user_key | default(system_user_key | default(''), true), true) | string }}" + prompt_user_key: "{{ user_public_key | default(user_key | default(system_user_key | default(''), true), true) | string | trim }}" prompt_user_password: "{{ user_password | default(system_user_password | default(''), true) | string }}" prompt_root_password: "{{ root_password | default(system_root_password | default(''), true) | string }}" ansible.builtin.set_fact: @@ -45,8 +45,14 @@ (system_user_input.name | default('') | string | length) > 0 ) | ternary(system_user_input.name | string, prompt_user_name), 'key': ( - (system_user_input.key | default('') | string | length) > 0 - ) | ternary(system_user_input.key | string, prompt_user_key), + system_user_input.key + if (system_user_input.key is iterable and system_user_input.key is not string and system_user_input.key | length > 0) + else ( + [prompt_user_key] + if (prompt_user_key | length > 0) + else [] + ) + ), 'password': ( (system_user_input.password | default('') | string | length) > 0 ) | ternary(system_user_input.password | string, prompt_user_password) @@ -60,7 +66,6 @@ recursive=True ) }} - changed_when: false - name: Load global defaults ansible.builtin.import_role: @@ -109,7 +114,6 @@ and (ansible_host | default('') | string | length) > 0 ) }} - changed_when: false - name: Reset SSH connection before post-reboot tasks when: diff --git a/roles/configuration/tasks/users.yml b/roles/configuration/tasks/users.yml index b2faef7..0c0a47a 100644 --- a/roles/configuration/tasks/users.yml +++ b/roles/configuration/tasks/users.yml @@ -26,12 +26,13 @@ group: 1000 mode: "0700" -- name: Add SSH public key to authorized_keys +- name: Add SSH public keys to authorized_keys when: system_cfg.user.key | length > 0 ansible.builtin.lineinfile: path: /mnt/home/{{ system_cfg.user.name }}/.ssh/authorized_keys - line: "{{ system_cfg.user.key }}" + line: "{{ item }}" owner: 1000 group: 1000 mode: "0600" create: true + loop: "{{ system_cfg.user.key }}" diff --git a/roles/global_defaults/defaults/main.yml b/roles/global_defaults/defaults/main.yml index a646525..e9a962b 100644 --- a/roles/global_defaults/defaults/main.yml +++ b/roles/global_defaults/defaults/main.yml @@ -42,7 +42,7 @@ system_defaults: user: name: "" password: "" - key: "" + key: [] root: password: "" luks: diff --git a/roles/global_defaults/tasks/system.yml b/roles/global_defaults/tasks/system.yml index 434f0d5..8ca21a5 100644 --- a/roles/global_defaults/tasks/system.yml +++ b/roles/global_defaults/tasks/system.yml @@ -2,7 +2,7 @@ - name: Ensure system input is a dictionary ansible.builtin.set_fact: system: "{{ system | default({}) }}" - changed_when: false + - name: Validate system input types ansible.builtin.assert: @@ -16,6 +16,23 @@ fail_msg: "system and its nested keys (network, user, root, luks, features) must be dictionaries." quiet: true +- name: Validate DNS and user.key are lists (not strings) + when: system.network is defined and system.network.dns is defined + ansible.builtin.assert: + that: + - system.network.dns.servers is not defined or (system.network.dns.servers is iterable and system.network.dns.servers is not string) + - system.network.dns.search is not defined or (system.network.dns.search is iterable and system.network.dns.search is not string) + fail_msg: "system.network.dns.servers and system.network.dns.search must be lists, not strings." + quiet: true + +- name: Validate user.key is a list + when: system.user is defined and system.user.key is defined + ansible.builtin.assert: + that: + - system.user.key is iterable and system.user.key is not string + fail_msg: "system.user.key must be a list of SSH public key strings." + quiet: true + - name: Validate system features input types when: system.features is defined loop: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}" @@ -69,28 +86,8 @@ }} gateway: "{{ system_raw.network.gateway | default('') | string }}" dns: - servers: >- - {{ - ( - system_raw.network.dns.servers - if system_raw.network.dns.servers is iterable and system_raw.network.dns.servers is not string - else (system_raw.network.dns.servers | string).split(',') - ) - | map('trim') - | reject('equalto', '') - | list - }} - search: >- - {{ - ( - system_raw.network.dns.search - if system_raw.network.dns.search is iterable and system_raw.network.dns.search is not string - else (system_raw.network.dns.search | string).split(',') - ) - | map('trim') - | reject('equalto', '') - | list - }} + servers: "{{ system_raw.network.dns.servers | default([]) }}" + search: "{{ system_raw.network.dns.search | default([]) }}" path: "{{ system_raw.path | default('') | string }}" packages: >- {{ @@ -107,7 +104,7 @@ user: name: "{{ system_raw.user.name | string }}" password: "{{ system_raw.user.password | string }}" - key: "{{ system_raw.user.key | string }}" + key: "{{ system_raw.user.key | default([]) }}" root: password: "{{ system_raw.root.password | string }}" luks: @@ -152,7 +149,7 @@ hostname: "{{ system_name }}" os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}" os_version: "{{ system_raw.version | default('') | string }}" - changed_when: false + - name: Normalize system disks input vars: @@ -187,7 +184,7 @@ - name: Initialize normalized disk list ansible.builtin.set_fact: system_disks_cfg: [] - changed_when: false + - name: Build normalized system disk configuration vars: @@ -247,7 +244,7 @@ - name: Update system configuration with normalized disks ansible.builtin.set_fact: system_cfg: "{{ system_cfg | combine({'disks': system_disks_cfg}, recursive=True) }}" - changed_when: false + - name: Set install_drive from primary disk when: @@ -255,4 +252,4 @@ - system_disks_cfg[0].device | string | length > 0 ansible.builtin.set_fact: install_drive: "{{ system_disks_cfg[0].device }}" - changed_when: false +