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

@ -22,7 +22,7 @@ set `enable_<service>: false` in `config.yml` to disable any service — DNS rec
| image update alerts | `enable_diun` | on | [diun](https://github.com/crazy-max/diun) |
| intrusion prevention | `enable_fail2ban` | on | [fail2ban](https://github.com/fail2ban/fail2ban) |
> **restic** is off by default — it requires a [Hetzner Storage Box](https://www.hetzner.com/storage/storage-box/) for its backup target. enable it and configure `restic_repository` in `config.yml` once you have one.
> **restic** is off by default — it requires a [Hetzner Storage Box](https://www.hetzner.com/storage/storage-box/). run `ansible-playbook playbooks/storage_box.yml` once to create the box, generate an SSH key pair, and install it — then set `enable_restic: true` and re-run `site.yml`.
## what you need
@ -33,6 +33,7 @@ set `enable_<service>: false` in `config.yml` to disable any service — DNS rec
- `ansible` and `ansible-galaxy`
- `direnv` (optional but recommended — loads `.envrc` automatically)
- `ssh-keygen`, `openssl`, `envsubst` (standard on most systems)
- `sshpass` (only needed for `storage_box.yml`)
if you already have a server with SSH access and passwordless sudo, you can skip provisioning and jump straight to [deploy](#deploy).
@ -45,7 +46,7 @@ run the interactive setup wizard:
./setup.sh
```
it walks you through: stack name, SSH key, admin username, server hostname, domain, Hetzner API token, and generates all secrets. config is written to `$XDG_CONFIG_HOME/linderhof/<stack>/` and won't overwrite existing files.
it walks you through: stack name, SSH key, admin username, server hostname, domain, Hetzner API token, storage box name and password, and generates all secrets. config is written to `$XDG_CONFIG_HOME/linderhof/<stack>/` and won't overwrite existing files.
activate the stack and review the generated config:
@ -67,9 +68,17 @@ ansible-galaxy collection install -r requirements.yml
full deployment order for a fresh server:
```bash
ansible-playbook playbooks/deploy.yml # provision → dns → storage_box → bootstrap → site (all-in-one)
ansible-playbook playbooks/dkim_sync.yml # generate DKIM keys and publish to DNS (run once after mail is up)
```
or step by step:
```bash
ansible-playbook playbooks/provision.yml # create server, writes IP to stack config
ansible-playbook playbooks/dns.yml # create DNS zones and records
ansible-playbook playbooks/storage_box.yml # create storage box and install SSH key (if using restic)
ansible-playbook playbooks/site.yml --tags bootstrap # users, SSH hardening, packages, Docker
ansible-playbook playbooks/site.yml # deploy all services
ansible-playbook playbooks/dkim_sync.yml # generate DKIM keys and publish to DNS