Add storage_box playbook and fix HCLOUD_TOKEN extraction

- Add storage_box role: generates SSH key pair, creates Hetzner Storage
  Box with known password, installs public key via install-ssh-key,
  writes storagebox.yml to stack config. Idempotent: skips key install
  if SSH key auth already works.
- Add deploy.yml: one-shot playbook chaining provision → dns →
  storage_box → bootstrap → site for fresh deployments
- Fix .envrc HCLOUD_TOKEN extraction stripping surrounding quotes from
  vault YAML values
- Add restic_storagebox_password to vault template and setup.sh prompt
- Add sshpass to README prerequisites

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthias Johnson 2026-03-01 17:43:14 -07:00
parent 203bd5bf6e
commit db70b4ba06
13 changed files with 218 additions and 18 deletions

View file

@ -0,0 +1,20 @@
---
# Storage box name used to identify (or create) the box
restic_storagebox_name: "{{ server_name }}-backup"
# Set these only when creating a new storage box from scratch.
# Leave unset if the box already exists (identified by restic_storagebox_name above).
# restic_storagebox_type: bx11
# restic_storagebox_location: fsn1
# SSH port for Hetzner Storage Boxes
restic_ssh_port: 23
# Path where the private key is stored on the Ansible controller (per-stack)
restic_local_key_path: "{{ lookup('env', 'LINDERHOF_DIR') }}/restic_backup"
# Path on the target server where the private key will be deployed
restic_ssh_key: /root/.ssh/restic_backup
# Remote path on the storage box for this server's backups
restic_remote_path: "backups/{{ server_name }}"

View file

@ -0,0 +1,86 @@
---
- name: Generate restic SSH key pair
ansible.builtin.command:
cmd: >-
ssh-keygen -t ed25519
-f {{ restic_local_key_path }}
-N ""
-C "restic-{{ server_name }}"
creates: "{{ restic_local_key_path }}"
check_mode: false
- name: Check if SSH public key exists
ansible.builtin.stat:
path: "{{ restic_local_key_path }}.pub"
register: ssh_pub_key_stat
- name: Read SSH public key
ansible.builtin.slurp:
src: "{{ restic_local_key_path }}.pub"
register: ssh_pub_key_raw
when: ssh_pub_key_stat.stat.exists
- name: Set public key fact
ansible.builtin.set_fact:
restic_ssh_pub_key: "{{ ssh_pub_key_raw.content | b64decode | trim }}"
when: ssh_pub_key_stat.stat.exists
- name: Configure Hetzner Storage Box
hetzner.hcloud.storage_box:
name: "{{ restic_storagebox_name }}"
storage_box_type: "{{ restic_storagebox_type | default(omit) }}"
location: "{{ restic_storagebox_location | default(omit) }}"
password: "{{ restic_storagebox_password }}"
api_token: "{{ hcloud_token }}"
access_settings:
ssh_enabled: true
state: present
register: storagebox_result
when: ssh_pub_key_stat.stat.exists
- name: Check SSH key auth on Storage Box
ansible.builtin.shell: |
echo "bye" | sftp -i {{ restic_local_key_path }} \
-o BatchMode=yes -o StrictHostKeyChecking=no \
-P 23 \
{{ storagebox_result.hcloud_storage_box.username }}@{{ storagebox_result.hcloud_storage_box.server }}
register: ssh_key_check
failed_when: false
changed_when: false
when: ssh_pub_key_stat.stat.exists
- name: Install SSH public key on Storage Box
ansible.builtin.shell: |
cat {{ restic_local_key_path }}.pub | \
sshpass -p "{{ restic_storagebox_password }}" \
ssh -o StrictHostKeyChecking=no -p 23 \
{{ storagebox_result.hcloud_storage_box.username }}@{{ storagebox_result.hcloud_storage_box.server }} \
install-ssh-key
no_log: true
when: ssh_pub_key_stat.stat.exists and ssh_key_check.rc != 0
- name: Write storagebox.yml to stack config directory
ansible.builtin.copy:
content: |
---
# Storage box config — written automatically by storage_box.yml, do not edit manually
restic_user: {{ storagebox_result.hcloud_storage_box.username }}
restic_host: {{ storagebox_result.hcloud_storage_box.server }}
restic_ssh_port: {{ restic_ssh_port }}
restic_remote_path: {{ restic_remote_path }}
restic_ssh_key: {{ restic_ssh_key }}
restic_local_key_path: {{ restic_local_key_path }}
dest: "{{ lookup('env', 'ANSIBLE_INVENTORY') | dirname }}/group_vars/all/storagebox.yml"
mode: "0600"
when: ssh_pub_key_stat.stat.exists
- name: Print connection info
ansible.builtin.debug:
msg:
- "Storage box configured successfully"
- "User: {{ storagebox_result.hcloud_storage_box.username }}"
- "Host: {{ storagebox_result.hcloud_storage_box.server }}"
- "Remote path: {{ restic_remote_path }}"
- "Local key: {{ restic_local_key_path }}"
- "Next: set enable_restic: true and run site.yml or restic.yml"
when: ssh_pub_key_stat.stat.exists