03d2f6d57a
- scripts/run-bootstrap.sh: copies and runs the deploy user bootstrap on a fresh server, verifies SSH login, checks vault as a preflight step - scripts/bootstrap-deploy-user.sh: runs on the server as root, accepts the SSH public key as an argument (read from keys/ by run-bootstrap.sh) - scripts/check-vault.sh: decrypts vault.yml and verifies all 11 required secrets are present and non-empty - docs/runbook.md: restructured into correct order (collections → DNS → vault → bootstrap → playbook), added scripts reference table, moved first-run notes for Gitea/Keycloak/registry into their own section - README.md: added scripts/ to repo layout
198 lines
5.7 KiB
Markdown
198 lines
5.7 KiB
Markdown
# Provisioning Runbook
|
|
|
|
Follow these steps to provision the server from scratch — whether setting it up
|
|
for the first time or reinstalling after a wipe.
|
|
|
|
## Prerequisites
|
|
|
|
### Local tools
|
|
|
|
- Ansible installed (`pip install ansible`)
|
|
- `htpasswd` available (`apt install apache2-utils` or `brew install httpd`)
|
|
- `docker` available (for generating the registry htpasswd entry)
|
|
- `openssl` available (for generating secrets)
|
|
|
|
### SSH keys
|
|
|
|
Two SSH key pairs are required. Place them in the `keys/` directory (private keys
|
|
are gitignored; public keys are committed).
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `keys/root_cloud_ladkau_de` | Root access — used only during initial bootstrap |
|
|
| `keys/root_cloud_ladkau_de.pub` | Public counterpart |
|
|
| `keys/notroot_cloud_ladkau_de` | Deploy user — used by Ansible for all subsequent runs |
|
|
| `keys/notroot_cloud_ladkau_de.pub` | Public counterpart — installed on server by bootstrap |
|
|
|
|
Generate fresh key pairs if they do not already exist:
|
|
|
|
```bash
|
|
ssh-keygen -t ed25519 -f keys/root_cloud_ladkau_de -C "root@cloud.ladkau.de" -N ""
|
|
ssh-keygen -t ed25519 -f keys/notroot_cloud_ladkau_de -C "deploy@cloud.ladkau.de" -N ""
|
|
```
|
|
|
|
Upload the root public key to the server via the Strato control panel (or paste
|
|
it during the initial OS install) so that `root@217.154.207.148` is accessible
|
|
before running anything.
|
|
|
|
## Steps
|
|
|
|
### 1. Install Ansible collections
|
|
|
|
From the repo root:
|
|
|
|
```bash
|
|
ansible-galaxy collection install -r ansible/requirements.yml
|
|
```
|
|
|
|
Required collections:
|
|
- `community.general` — ufw, timezone modules
|
|
- `ansible.posix` — authorized_key module
|
|
- `community.docker` — docker_network, docker_compose_v2 modules
|
|
|
|
### 2. Configure DNS
|
|
|
|
Ensure the following DNS A records point to `217.154.207.148` before running
|
|
the playbook. Traefik requests Let's Encrypt certificates on first start and
|
|
DNS must resolve at that point.
|
|
|
|
| Domain | Record |
|
|
|---------------------|--------|
|
|
| cloud.ladkau.de | A → 217.154.207.148 |
|
|
| gitea.ladkau.de | A → 217.154.207.148 |
|
|
| nextcloud.ladkau.de | A → 217.154.207.148 |
|
|
| sso.ladkau.de | A → 217.154.207.148 |
|
|
| mail.ladkau.de | A → 217.154.207.148 |
|
|
| cr.ladkau.de | A → 217.154.207.148 |
|
|
| k8s.ladkau.de | A → 217.154.207.148 |
|
|
|
|
### 3. Create the vault and populate secrets
|
|
|
|
Create `ansible/group_vars/vault.yml` (gitignored) and encrypt it with Ansible Vault:
|
|
|
|
```bash
|
|
ansible-vault create ansible/group_vars/vault.yml
|
|
```
|
|
|
|
Populate all required secrets:
|
|
|
|
```yaml
|
|
# Traefik dashboard basic auth
|
|
# Generate: echo $(htpasswd -nB admin) | sed -e 's/\$/\$\$/g'
|
|
traefik_dashboard_users: "admin:$$2y$$05$$..."
|
|
|
|
# Gitea
|
|
# Generate secrets: openssl rand -hex 32
|
|
gitea_db_password: ""
|
|
gitea_secret_key: ""
|
|
gitea_internal_token: ""
|
|
|
|
# Keycloak
|
|
keycloak_db_password: ""
|
|
keycloak_admin_password: ""
|
|
|
|
# Nextcloud
|
|
nextcloud_db_password: ""
|
|
nextcloud_admin_password: ""
|
|
|
|
# Roundcube
|
|
# des_key must be exactly 24 characters: openssl rand -hex 12
|
|
roundcube_db_password: ""
|
|
roundcube_des_key: ""
|
|
|
|
# Container registry
|
|
# Generate: docker run --entrypoint htpasswd httpd:2 -Bbn <user> <password>
|
|
registry_htpasswd: "user:$2y$05$..."
|
|
```
|
|
|
|
Verify all secrets are present and non-empty:
|
|
|
|
```bash
|
|
bash scripts/check-vault.sh
|
|
```
|
|
|
|
To edit the vault later:
|
|
|
|
```bash
|
|
ansible-vault edit ansible/group_vars/vault.yml
|
|
```
|
|
|
|
### 4. Bootstrap the deploy user
|
|
|
|
Copies the bootstrap script to the server, runs it as root, and verifies the
|
|
deploy user can log in. Also runs `check-vault.sh` as a preflight check.
|
|
|
|
```bash
|
|
bash scripts/run-bootstrap.sh
|
|
```
|
|
|
|
### 5. Run the Ansible master playbook
|
|
|
|
```bash
|
|
ansible-playbook -i ansible/inventory.ini ansible/site.yml --ask-vault-pass
|
|
```
|
|
|
|
This runs all roles in order:
|
|
|
|
| # | Role | What it does |
|
|
|---|------------|--------------|
|
|
| 1 | `base` | OS hardening, deploy user, SSH config, ufw firewall, fail2ban |
|
|
| 2 | `docker` | Docker Engine + Compose plugin, shared Traefik network |
|
|
| 3 | `traefik` | Reverse proxy, automatic TLS via Let's Encrypt |
|
|
| 4 | `gitea` | Self-hosted Git with PostgreSQL |
|
|
| 5 | `nextcloud`| File storage with PostgreSQL, Redis, cron sidecar |
|
|
| 6 | `sso` | Keycloak single sign-on with PostgreSQL |
|
|
| 7 | `mail` | Roundcube webmail client with PostgreSQL |
|
|
| 8 | `registry` | Docker Registry v2 with htpasswd auth |
|
|
| 9 | `k8s` | Placeholder page at k8s.ladkau.de |
|
|
|
|
To apply a single role:
|
|
|
|
```bash
|
|
ansible-playbook -i ansible/inventory.ini ansible/site.yml --tags <role> --ask-vault-pass
|
|
```
|
|
|
|
## First-run notes
|
|
|
|
### Gitea
|
|
|
|
`gitea_disable_registration` defaults to `true`. For the first deploy, override
|
|
it to `false` in `vault.yml` so the setup wizard can create the admin account:
|
|
|
|
```bash
|
|
ansible-vault edit ansible/group_vars/vault.yml
|
|
# add: gitea_disable_registration: false
|
|
ansible-playbook -i ansible/inventory.ini ansible/site.yml --tags gitea --ask-vault-pass
|
|
```
|
|
|
|
After the admin account is created at `https://gitea.ladkau.de`, remove the
|
|
override and redeploy to close public registration.
|
|
|
|
### Keycloak
|
|
|
|
`KEYCLOAK_ADMIN` / `KEYCLOAK_ADMIN_PASSWORD` bootstrap the initial admin account
|
|
on first start only — Keycloak ignores them once the account exists. After
|
|
logging in at `https://sso.ladkau.de`, change the admin password via the UI.
|
|
|
|
### Container registry
|
|
|
|
```bash
|
|
# Login
|
|
docker login cr.ladkau.de
|
|
|
|
# Push
|
|
docker tag myimage:latest cr.ladkau.de/myimage:latest
|
|
docker push cr.ladkau.de/myimage:latest
|
|
|
|
# Pull
|
|
docker pull cr.ladkau.de/myimage:latest
|
|
```
|
|
|
|
## Scripts reference
|
|
|
|
| Script | Purpose |
|
|
|--------|---------|
|
|
| `scripts/run-bootstrap.sh` | Copy and run the deploy user bootstrap on a fresh server |
|
|
| `scripts/bootstrap-deploy-user.sh` | Runs on the server as root — creates the deploy user |
|
|
| `scripts/check-vault.sh` | Verify vault.yml exists and all required secrets are non-empty |
|