diff --git a/Makefile b/Makefile index 60a7c98..deb9acd 100644 --- a/Makefile +++ b/Makefile @@ -143,6 +143,17 @@ provision-custodian-agent-host: ## Deploy custodian agent key to one host: make cd ansible && ansible-playbook playbooks/custodian-agent.yaml -u $(SSH_USER) \ --limit "$(HOST)" +bootstrap-ssh-ca: ## Deploy OpenBao SSH CA trust + auth_principals: make bootstrap-ssh-ca SSH_CA_PUBKEY=/path/to/ca_user.pub + @test -n "$(SSH_CA_PUBKEY)" || (echo "Usage: make bootstrap-ssh-ca SSH_CA_PUBKEY=/path/to/ca_user.pub [HOST=Railiance01]"; exit 1) + cd ansible && ansible-playbook playbooks/bootstrap-ssh-ca.yaml -u $(SSH_USER) \ + -e ssh_ca_pubkey_path="$(SSH_CA_PUBKEY)" \ + $(if $(HOST),--limit "$(HOST)",) + +bootstrap-ssh-ca-host: ## Deploy SSH CA trust to one host: make bootstrap-ssh-ca-host HOST=Railiance01 SSH_CA_PUBKEY=... + @test -n "$(HOST)" && test -n "$(SSH_CA_PUBKEY)" || \ + (echo "Usage: make bootstrap-ssh-ca-host HOST=Railiance01 SSH_CA_PUBKEY=/path/to/ca_user.pub"; exit 1) + $(MAKE) bootstrap-ssh-ca SSH_CA_PUBKEY="$(SSH_CA_PUBKEY)" HOST="$(HOST)" + # ---- Orchestration ---- apply: tf-fmt tf-apply ansible-bootstrap ## Provision via Terraform then converge via Ansible diff --git a/ansible/inventory/ssh_principals.yaml b/ansible/inventory/ssh_principals.yaml new file mode 100644 index 0000000..8b6c087 --- /dev/null +++ b/ansible/inventory/ssh_principals.yaml @@ -0,0 +1,19 @@ +# Central SSH certificate principals inventory — synced with ops-warden actor +# principals (wiki/ActorInventoryPatterns.md). Public data only. + +ssh_principals: + CoulombCore: + users: + tegwick: + - agt-task-bridge + - agt-interhub-bootstrap + - adm-full + - atm-backup-daily + + Railiance01: + users: + tegwick: + - agt-task-bridge + - agt-interhub-bootstrap + - adm-full + - atm-backup-daily diff --git a/ansible/playbooks/bootstrap-ssh-ca.yaml b/ansible/playbooks/bootstrap-ssh-ca.yaml new file mode 100644 index 0000000..efccfb9 --- /dev/null +++ b/ansible/playbooks/bootstrap-ssh-ca.yaml @@ -0,0 +1,19 @@ +--- +# Deploy OpenBao SSH user CA trust and per-user auth_principals. +# +# Prerequisite: railiance-platform openbao-configure-ssh (exports CA pubkey). +# +# cd ~/railiance-platform +# OPENBAO_TOKEN_FILE=~/.local/openbao/platform-admin.token \ +# OPENBAO_SSH_CA_PUBKEY_OUT=/tmp/openbao-ssh-ca.pub \ +# make openbao-configure-ssh +# +# cd ~/railiance-infra +# make bootstrap-ssh-ca SSH_CA_PUBKEY=/tmp/openbao-ssh-ca.pub + +- hosts: all + become: true + vars_files: + - ../inventory/ssh_principals.yaml + roles: + - role: ssh_ca_host diff --git a/ansible/roles/ssh_ca_host/handlers/main.yml b/ansible/roles/ssh_ca_host/handlers/main.yml new file mode 100644 index 0000000..cad74d8 --- /dev/null +++ b/ansible/roles/ssh_ca_host/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Restart sshd + ansible.builtin.service: + name: ssh + state: restarted diff --git a/ansible/roles/ssh_ca_host/tasks/main.yml b/ansible/roles/ssh_ca_host/tasks/main.yml new file mode 100644 index 0000000..daae6ef --- /dev/null +++ b/ansible/roles/ssh_ca_host/tasks/main.yml @@ -0,0 +1,85 @@ +--- +- name: Require SSH CA public key path + ansible.builtin.assert: + that: + - ssh_ca_pubkey_path is defined + - ssh_ca_pubkey_path | length > 0 + fail_msg: >- + Set ssh_ca_pubkey_path to the OpenBao SSH CA public key file + (from railiance-platform openbao-configure-ssh). + +- name: Stat SSH CA public key source + ansible.builtin.stat: + path: "{{ ssh_ca_pubkey_path }}" + delegate_to: localhost + become: false + register: ssh_ca_pubkey_stat + +- name: Fail when SSH CA public key is missing + ansible.builtin.fail: + msg: "SSH CA public key not found on controller: {{ ssh_ca_pubkey_path }}" + when: not ssh_ca_pubkey_stat.stat.exists + +- name: Ensure SSH CA directory exists + ansible.builtin.file: + path: /etc/ssh/ca + state: directory + owner: root + group: root + mode: "0755" + +- name: Install SSH user CA public key + ansible.builtin.copy: + src: "{{ ssh_ca_pubkey_path }}" + dest: /etc/ssh/ca/ca_user.pub + owner: root + group: root + mode: "0644" + notify: Restart sshd + +- name: Configure SSH certificate trust + ansible.builtin.copy: + dest: /etc/ssh/sshd_config.d/20-ssh-ca.conf + owner: root + group: root + mode: "0644" + content: | + TrustedUserCAKeys /etc/ssh/ca/ca_user.pub + AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u + notify: Restart sshd + +- name: Ensure auth_principals directory exists + ansible.builtin.file: + path: /etc/ssh/auth_principals + state: directory + owner: root + group: root + mode: "0755" + +- name: Resolve principals for this host + ansible.builtin.set_fact: + ssh_ca_host_principals: >- + {{ + (ssh_principals[inventory_hostname].users + if ssh_principals is defined + and inventory_hostname in ssh_principals + else {}) + }} + +- name: Deploy auth_principals files per user + ansible.builtin.copy: + dest: "/etc/ssh/auth_principals/{{ item.key }}" + owner: root + group: root + mode: "0644" + content: "{{ item.value | join('\n') }}\n" + loop: "{{ ssh_ca_host_principals | dict2items }}" + when: ssh_ca_host_principals | length > 0 + notify: Restart sshd + +- name: Warn when no principals configured for host + ansible.builtin.debug: + msg: >- + No principals in ssh_principals.yaml for {{ inventory_hostname }} — + CA trust installed; add users under hosts..users to enable cert login. + when: ssh_ca_host_principals | length == 0