diff --git a/Makefile b/Makefile index be91d5a..15ca2fb 100644 --- a/Makefile +++ b/Makefile @@ -130,9 +130,23 @@ backup: ## Backup S1 OS config to /opt/backup/railiance/infra/ (age-encrypted, r sudo tools/cmd/railiance-backup-s1 # ---- Ansible ---- -ansible-bootstrap: ## Run base bootstrap play (users, ssh, ufw, sops-agent) +ansible-bootstrap: ## Run base bootstrap play (users, ssh, ufw, sops-agent, custodian-agent) cd ansible && ansible-playbook playbooks/bootstrap.yaml -u admin +provision-custodian-agent: ## Deploy custodian agent SSH key to all managed hosts + @python3 -c "import yaml; d=yaml.safe_load(open('ansible/inventory/group_vars/all.yaml')); k=d.get('custodian_agent_pubkey',''); exit(0 if k else 1)" \ + || (echo "ERROR: custodian_agent_pubkey is empty. Run: cd ~/the-custodian && make custodian-keygen"; exit 1) + cd ansible && ansible-playbook playbooks/bootstrap.yaml -u $(SSH_USER) \ + --tags custodian_agent \ + --extra-vars "@inventory/group_vars/all.yaml" + +provision-custodian-agent-host: ## Deploy custodian agent key to one host: make provision-custodian-agent-host HOST=railiance01 + @test -n "$(HOST)" || (echo "Usage: make provision-custodian-agent-host HOST="; exit 1) + cd ansible && ansible-playbook playbooks/bootstrap.yaml -u $(SSH_USER) \ + --limit "$(HOST)" \ + --tags custodian_agent \ + --extra-vars "@inventory/group_vars/all.yaml" + # ---- Orchestration ---- apply: tf-fmt tf-apply ansible-bootstrap ## Provision via Terraform then converge via Ansible diff --git a/ansible/inventory/group_vars/all.yaml b/ansible/inventory/group_vars/all.yaml new file mode 100644 index 0000000..a161dd4 --- /dev/null +++ b/ansible/inventory/group_vars/all.yaml @@ -0,0 +1,20 @@ +# Ansible group vars — applied to all managed hosts. +# +# custodian_agent_pubkey: the SSH public key for the Custodian automation identity. +# +# HOW TO SET THIS: +# 1. Generate the keypair on the workstation (one-time): +# cd ~/the-custodian && make custodian-keygen +# This creates ~/.ssh/id_custodian_agent (private, never committed) +# and writes the public key to: +# ~/railiance-infra/ansible/inventory/group_vars/all.yaml ← this file +# +# 2. Commit the updated all.yaml (public key only — safe to commit). +# +# 3. Deploy to all managed hosts: +# cd ~/railiance-infra && make provision-custodian-agent +# +# The key below is a placeholder — replace by running `make custodian-keygen`. + +custodian_agent_user: tegwick +custodian_agent_pubkey: "" diff --git a/ansible/playbooks/bootstrap.yaml b/ansible/playbooks/bootstrap.yaml index 983a024..ed2c57e 100644 --- a/ansible/playbooks/bootstrap.yaml +++ b/ansible/playbooks/bootstrap.yaml @@ -6,4 +6,5 @@ roles: - role: base - role: sops_agent + - role: custodian_agent # injects ~/.ssh/id_custodian_agent.pub into authorized_keys # - role: wireguard # enable if you configure WireGuard variables diff --git a/ansible/roles/custodian_agent/tasks/main.yml b/ansible/roles/custodian_agent/tasks/main.yml new file mode 100644 index 0000000..ed84f8a --- /dev/null +++ b/ansible/roles/custodian_agent/tasks/main.yml @@ -0,0 +1,47 @@ +--- +# custodian_agent role — injects the Custodian automation SSH identity +# +# What it does: +# - Ensures the target user (custodian_agent_user) exists +# - Adds custodian_agent_pubkey to that user's authorized_keys +# - Restricts the key: no X11, no port-forward, no pty for non-interactive use +# +# Variables (set in group_vars/all.yaml or pass via --extra-vars): +# custodian_agent_user: user account the key is added to (default: tegwick) +# custodian_agent_pubkey: the public key string (required, set in all.yaml) +# +# The private key lives on the workstation at ~/.ssh/id_custodian_agent. +# It is NEVER committed to any repository. + +- name: Ensure target user exists + tags: [custodian_agent] + ansible.builtin.user: + name: "{{ custodian_agent_user | default('tegwick') }}" + state: present + shell: /bin/bash + create_home: true + +- name: Ensure .ssh directory exists for target user + tags: [custodian_agent] + ansible.builtin.file: + path: "/home/{{ custodian_agent_user | default('tegwick') }}/.ssh" + state: directory + owner: "{{ custodian_agent_user | default('tegwick') }}" + group: "{{ custodian_agent_user | default('tegwick') }}" + mode: '0700' + +- name: Add custodian agent public key to authorized_keys + tags: [custodian_agent] + ansible.posix.authorized_key: + user: "{{ custodian_agent_user | default('tegwick') }}" + key: "{{ custodian_agent_pubkey }}" + key_options: "no-X11-forwarding,no-agent-forwarding" + comment: "custodian-agent@{{ inventory_hostname }}" + state: present + when: custodian_agent_pubkey is defined and custodian_agent_pubkey | length > 0 + +- name: Warn if custodian_agent_pubkey is not set + tags: [custodian_agent] + ansible.builtin.debug: + msg: "WARNING: custodian_agent_pubkey is not set — skipping authorized_keys injection" + when: custodian_agent_pubkey is not defined or custodian_agent_pubkey | length == 0