82 lines
3.9 KiB
YAML
82 lines
3.9 KiB
YAML
---
|
|
# Materializes the SSH key the k3s plays authenticate with, from Vault
|
|
# (homelab/ci/ssh-k3s) onto the controller. Imported as the first play of both
|
|
# playbooks/k3s.yml and playbooks/k3s_maintenance.yml — a separate file rather
|
|
# than copied into each, since both target k3s_cluster and both would
|
|
# otherwise depend on a file only the other one creates.
|
|
#
|
|
# This is the same shape CI already uses for the Unraid key:
|
|
# .forgejo/workflows/deploy.yml fetches homelab/ci/ssh, writes it to disk and
|
|
# points ANSIBLE_PRIVATE_KEY_FILE at it. Vault is the one place secrets live,
|
|
# so a manual k3s run shouldn't need the key hand-loaded into ssh-agent first
|
|
# — which was the only way it worked before.
|
|
#
|
|
# hosts: k3s_cluster, not localhost, and deliberately so: the implicit
|
|
# localhost is not a member of `all`, so it doesn't inherit group_vars/all.yml
|
|
# — where vault_addr, vault_kv_mount and vault_auth_method live. Targeting the
|
|
# group picks those up along with k3s_ssh_key_vault_path/_local_path from
|
|
# group_vars/k3s_cluster.yml. Nothing here connects to a Pi (gather_facts is
|
|
# off and every task is delegated or connectionless), which is the whole
|
|
# point: at this stage the key isn't on disk yet.
|
|
#
|
|
# become: false on the delegated tasks because the k3s_cluster group sets
|
|
# ansible_become: true for the Pis (inventory/hosts.yml) — without it these
|
|
# would try to sudo on the controller.
|
|
|
|
- name: Fetch the k3s SSH deploy key from Vault
|
|
hosts: k3s_cluster
|
|
gather_facts: false
|
|
|
|
tasks:
|
|
# The escape hatch, and the reason it's a flag rather than just pointing
|
|
# ansible_ssh_private_key_file somewhere else: if Vault is unreachable,
|
|
# overriding the key path alone doesn't help — this play would still fail
|
|
# before the first real one runs. Skipping it is the only thing that lets
|
|
# a run proceed on a local key. Both overrides together:
|
|
#
|
|
# ansible-playbook playbooks/k3s.yml \
|
|
# -e k3s_ssh_key_fetch=false \
|
|
# -e ansible_ssh_private_key_file=~/.ssh/k3s_ansible
|
|
- name: Fetch and write the key
|
|
when: k3s_ssh_key_fetch | default(true) | bool
|
|
block:
|
|
# run_once because the key is per-cluster, not per-host: one Vault
|
|
# read for the whole group. Facts set by a run_once task apply to
|
|
# every host in the play, but nothing outside this play needs it.
|
|
- name: Look up the k3s SSH deploy key from Vault
|
|
ansible.builtin.set_fact:
|
|
k3s_ssh_key_secret: >-
|
|
{{ lookup('community.hashi_vault.vault_kv2_get',
|
|
k3s_ssh_key_vault_path,
|
|
engine_mount_point=vault_kv_mount,
|
|
url=vault_addr,
|
|
auth_method=vault_auth_method,
|
|
role_id=vault_role_id | default(omit),
|
|
secret_id=vault_secret_id | default(omit)).secret }}
|
|
run_once: true
|
|
no_log: true
|
|
|
|
- name: Ensure the local key directory exists
|
|
ansible.builtin.file:
|
|
path: "{{ k3s_ssh_key_local_path | dirname }}"
|
|
state: directory
|
|
mode: "0700"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
|
|
# trim + an explicit trailing newline: OpenSSH rejects a key file
|
|
# whose final line isn't terminated, and `vault kv put
|
|
# PRIVATE_KEY=@file` is as likely to have stored one with trailing
|
|
# whitespace as not. 0600 for the same reason — ssh refuses a key file
|
|
# that's group- or world-readable. Both fail at connection time rather
|
|
# than here, which is a much less obvious error to read.
|
|
- name: Write the private key to the controller
|
|
ansible.builtin.copy:
|
|
dest: "{{ k3s_ssh_key_local_path }}"
|
|
content: "{{ k3s_ssh_key_secret.PRIVATE_KEY | trim }}\n"
|
|
mode: "0600"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
no_log: true
|