From 7bcf273d5755b5d17a7f147ccb3542cc989ea172 Mon Sep 17 00:00:00 2001 From: Steve Brasier Date: Wed, 21 May 2025 16:04:59 +0000 Subject: [PATCH] wip: unify caas/normal secrets --- ansible/roles/passwords/README.md | 15 ++++ ansible/roles/passwords/defaults/main.yml | 15 +++- ansible/roles/passwords/tasks/main.yml | 78 ++++++++++++++++++- ansible/roles/passwords/tasks/validate.yml | 9 ++- .../roles/passwords/templates/passwords.yml | 2 +- ansible/site.yml | 9 +++ 6 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 ansible/roles/passwords/README.md diff --git a/ansible/roles/passwords/README.md b/ansible/roles/passwords/README.md new file mode 100644 index 000000000..fb1c84881 --- /dev/null +++ b/ansible/roles/passwords/README.md @@ -0,0 +1,15 @@ +This is pretty subtle. + +- secrets always need to be idempotent +- caas cannot run a pre-task to create files in inventory, so secrets + need to be loaded as part of site +- caas cannot write inventory, b/c that is in a container which is new each + run + + +TODO: +- remove the adhoc generate-passwords from non-caas +- remove and the other role/hook from caas and the secret overrides +- work out how we'd migrate secrets for caas???? +- test it properly +- doc this role properly diff --git a/ansible/roles/passwords/defaults/main.yml b/ansible/roles/passwords/defaults/main.yml index 95e3b6aca..a876e7da8 100644 --- a/ansible/roles/passwords/defaults/main.yml +++ b/ansible/roles/passwords/defaults/main.yml @@ -1,19 +1,26 @@ --- -slurm_appliance_secrets: +# TODO: should we make these "'{{}}'" to ensure templating is OK?? +passwords_defaults: vault_grafana_admin_password: "{{ secrets_openhpc_grafana_admin_password | default(vault_grafana_admin_password | default(lookup('password', '/dev/null'))) }}" vault_elasticsearch_admin_password: "{{ secrets_openhpc_elasticsearch_admin_password | default(vault_elasticsearch_admin_password | default(lookup('password', '/dev/null'))) }}" vault_mysql_root_password: "{{ secrets_openhpc_mysql_root_password | default(vault_mysql_root_password | default(lookup('password', '/dev/null'))) }}" vault_mysql_slurm_password: "{{ secrets_openhpc_mysql_slurm_password | default(vault_mysql_slurm_password | default(lookup('password', '/dev/null'))) }}" - vault_openhpc_mungekey: "{{ secrets_openhpc_mungekey | default(vault_openhpc_mungekey | default(secrets_openhpc_mungekey_default)) }}" + vault_openhpc_mungekey: "{{ secrets_openhpc_mungekey | default(vault_openhpc_mungekey | default(passwords_openhpc_mungekey_default)) }}" vault_freeipa_ds_password: "{{ vault_freeipa_ds_password | default(lookup('password', '/dev/null')) }}" vault_freeipa_admin_password: "{{ vault_freeipa_admin_password | default(lookup('password', '/dev/null')) }}" vault_k3s_node_password: "{{ vault_k3s_node_password | default(lookup('ansible.builtin.password', '/dev/null', length=64)) }}" vault_pulp_admin_password: "{{ vault_pulp_admin_password | default(lookup('password', '/dev/null', chars=['ascii_letters', 'digits'])) }}" vault_demo_user_password: "{{ vault_demo_user_password | default(lookup('password', '/dev/null')) }}" vault_alertmanager_admin_password: "{{ vault_alertmanager_admin_password | default(lookup('password', '/dev/null')) }}" + # vault_newthing: "{{ vault_newthing | default(lookup('password', '/dev/null')) }}" -secrets_openhpc_mungekey_default: +passwords_openhpc_mungekey_default: content: "{{ lookup('pipe', 'dd if=/dev/urandom bs=1 count=1024 2>/dev/null | base64') }}" -openhpc_passwords_output_path: "{{ lookup('env', 'APPLIANCES_ENVIRONMENT_ROOT') | default(undefined, true) | mandatory('You must define the APPLIANCES_ENVIRONMENT_ROOT environment variable') }}/inventory/group_vars/all/secrets.yml" +passwords_output_path: "{{ lookup('env', 'APPLIANCES_ENVIRONMENT_ROOT') | default(undefined, true) | mandatory('You must define the APPLIANCES_ENVIRONMENT_ROOT environment variable') }}/inventory/group_vars/all/secrets.yml" + +passwords_host: localhost +passwords_owner: "{{ ansible_user }}" +passwords_group: "{{ passwords_owner }}" + diff --git a/ansible/roles/passwords/tasks/main.yml b/ansible/roles/passwords/tasks/main.yml index 743a6cda8..a0a0cf2c8 100644 --- a/ansible/roles/passwords/tasks/main.yml +++ b/ansible/roles/passwords/tasks/main.yml @@ -1,8 +1,82 @@ --- +# STATUS: + +# looks like it works for caas, but not for normal now + +# can't use ansible.builtin.include_vars - that only looks on the deploy host +# (even with delegate) + +# For caas where passwords_output_path isn't actually in inventory we need +# to load them first, so that templating is idempotent + +- name: Slurp passwords if defined + ansible.builtin.slurp: + src: "{{ passwords_output_path }}" + delegate_to: "{{ passwords_host }}" + register: _passwords_slurp_first + failed_when: + - _passwords_slurp_first.failed + - "'file not found' not in _passwords_slurp_first.msg" + +- name: Set facts for passwords + set_fact: + "{{ item.key }}": "{{ item.value }}" + when: "'content' in _passwords_slurp_first" + loop: "{{ _passwords_slurp_first.content | b64decode | from_yaml | dict2items }}" + no_log: "{{ no_log | default(true) }}" + +# - name: Set facts for passwords +# set_fact: +# # nah can't template yaml keys so not sure we can do this! +# when: not _passwords_slurp.failed +# loop: "{{ _passwords_slurp.content | b64decode | from_yaml }}" + +- name: Ensure secrets directory exists + file: + path: "{{ passwords_output_path | dirname }}" + owner: "{{ passwords_owner }}" + group: "{{ passwords_group }}" + state: directory + #mode: ug=rwX,o=rX # non-caas for caas we want u=rwx,go= + delegate_to: "{{ passwords_host }}" + become: "{{ passwords_owner != ansible_user }}" # not sure about this in the general case but seems ok here + run_once: true + - name: Template passwords template: src: passwords.yml - dest: "{{ openhpc_passwords_output_path }}" - delegate_to: localhost + dest: "{{ passwords_output_path }}" + owner: "{{ passwords_owner }}" + group: "{{ passwords_group }}" + become: "{{ passwords_owner != ansible_user }}" + delegate_to: "{{ passwords_host }}" run_once: true + register: _passwords_template + +# even if the files are in inventory, even meta: inventory_reload doesn't +# get the new variables, so we need to set them as facts: +- name: Slurp passwords if changed + ansible.builtin.slurp: + src: "{{ passwords_output_path }}" + delegate_to: "{{ passwords_host }}" + register: _passwords_slurp_second + when: _passwords_template.changed + +- name: Set facts for passwords + set_fact: + "{{ item.key }}": "{{ item.value }}" + when: not _passwords_slurp_second.skipped | default(false) + loop: "{{ _passwords_slurp_second.content | b64decode | from_yaml | dict2items }}" + no_log: "{{ no_log | default(true) }}" + + + # we do see passwords end up in the templated config for slurm-controlled rebuild! + + + # oh man maybe this doesn't work b/c things are accessed through hostvars[*] ... + + # also; does this work for caas?? Because the vars won't be in inventory to + # start with, so then they won't exist, so they'll be re-templated despite + # the fact the file exists. Maybe we need to load them first, if the file + # exists?? \ No newline at end of file diff --git a/ansible/roles/passwords/tasks/validate.yml b/ansible/roles/passwords/tasks/validate.yml index b30b0696e..3b9976cc6 100644 --- a/ansible/roles/passwords/tasks/validate.yml +++ b/ansible/roles/passwords/tasks/validate.yml @@ -1,4 +1,5 @@ -- name: Assert secrets created - assert: - that: (hostvars[inventory_hostname].keys() | select('contains', 'vault_') | length) > 1 # 1 as may have vault_demo_user_password defined in dev - fail_msg: "No inventory variables 'vault_*' found: Has ansible/adhoc/generate-passwords.yml been run?" +# - name: Assert secrets created +# assert: +# that: (hostvars[inventory_hostname].keys() | select('contains', 'vault_') | length) > 1 # 1 as may have vault_demo_user_password defined in dev +# fail_msg: "No inventory variables 'vault_*' found: Has ansible/adhoc/generate-passwords.yml been run?" + # TODO: should maybe remove this, it isn't needed now?? \ No newline at end of file diff --git a/ansible/roles/passwords/templates/passwords.yml b/ansible/roles/passwords/templates/passwords.yml index 4e4429f06..cb5b527c3 100644 --- a/ansible/roles/passwords/templates/passwords.yml +++ b/ansible/roles/passwords/templates/passwords.yml @@ -1,3 +1,3 @@ --- # {{ ansible_managed }} -{{ slurm_appliance_secrets | to_nice_yaml }} \ No newline at end of file +{{ passwords_defaults | to_nice_yaml }} diff --git a/ansible/site.yml b/ansible/site.yml index d973d9cb3..73343f80a 100644 --- a/ansible/site.yml +++ b/ansible/site.yml @@ -1,5 +1,14 @@ --- +- name: Template passwords + hosts: cluster # TODO: maybe we need to run this + gather_facts: false + tasks: + - name: Include password generation role + include_role: + name: passwords + - meta: end_here + - name: Run pre.yml hook vars: # hostvars not available here, so have to recalculate environment root: