87 lines
4.4 KiB
Markdown
87 lines
4.4 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Purpose
|
|
|
|
Infrastructure-as-code for a Strato VPS at cloud.ladkau.de. Every config change must be committed — the git history is the change log. The goal is a fully reproducible reinstall from this repo alone.
|
|
|
|
## Key commands
|
|
|
|
```bash
|
|
# Install Ansible collections (one-time, after cloning)
|
|
ansible-galaxy collection install -r ansible/requirements.yml
|
|
|
|
# Run the full playbook (most common operation)
|
|
ansible-playbook -i ansible/inventory.ini ansible/site.yml --ask-vault-pass
|
|
|
|
# Run a single role
|
|
ansible-playbook -i ansible/inventory.ini ansible/site.yml --tags mail --ask-vault-pass
|
|
|
|
# Bootstrap a fresh server (before Ansible can run — creates the deploy user)
|
|
bash scripts/run-bootstrap.sh
|
|
|
|
# Verify all vault secrets are present and valid
|
|
bash scripts/check-vault.sh
|
|
|
|
# Verify all services are reachable
|
|
bash scripts/check-services.sh
|
|
|
|
# Edit encrypted secrets
|
|
ansible-vault edit ansible/group_vars/all/vault.yml
|
|
|
|
# Connect to server
|
|
ssh -i keys/notroot_cloud_ladkau_de deploy@217.154.207.148
|
|
```
|
|
|
|
Local dependencies: `pip install ansible passlib` and `openssl`.
|
|
|
|
## Architecture
|
|
|
|
### Provisioning flow
|
|
|
|
1. `scripts/run-bootstrap.sh` — copies and runs `bootstrap-deploy-user.sh` on the server as root; creates the `deploy` user with the SSH public key from `keys/notroot_cloud_ladkau_de.pub`
|
|
2. `ansible-playbook ansible/site.yml` — runs all roles in order against the `deploy` user (passwordless sudo)
|
|
|
|
### Role → service → data directory
|
|
|
|
Each role deploys one Docker Compose stack. The pattern is:
|
|
|
|
- **Template** lives in `roles/<name>/templates/docker-compose.yml.j2`
|
|
- Ansible renders it and writes it to `/opt/<name>/docker-compose.yml` on the server
|
|
- Data/config for each service lives under `/opt/<name>/` on the server
|
|
- The role also writes any config files (e.g. `dovecot.conf`, `passwd`, `fetchmailrc`) to `/opt/<name>/` before starting the stack
|
|
|
|
### Networking
|
|
|
|
Two Docker networks:
|
|
- `traefik_public` (external, created once by the `traefik` role) — all services that need HTTPS exposure join this network alongside Traefik
|
|
- Per-service internal networks (e.g. `mail_internal`) — isolate backend containers (databases, Dovecot) from the internet
|
|
|
|
Traefik routes HTTP/HTTPS/IMAPS via Docker labels on each service container. TLS certificates are issued via Let's Encrypt ACME. IMAPS (993) is handled as a TCP passthrough — Traefik terminates TLS and forwards plain IMAP to Dovecot port 143.
|
|
|
|
### Secrets
|
|
|
|
All secrets live in `ansible/group_vars/all/vault.yml` (Ansible Vault, AES-256). Plain variables are in `ansible/group_vars/all/vars.yml` — commented-out keys there show what each role expects from the vault. `scripts/check-vault.sh` validates all required keys are present.
|
|
|
|
Passwords that need bcrypt hashes (Traefik dashboard, container registry) are stored as plaintext in the vault and hashed in the Jinja2 templates at deploy time using `password_hash('bcrypt', ...)`. The `passlib` Python package is required locally for this.
|
|
|
|
### Mail stack specifics
|
|
|
|
The mail role deploys four containers on `mail_internal`: `mail-db` (PostgreSQL for Roundcube), `dovecot` (IMAP server), `roundcube` (webmail at mail.ladkau.de), and conditionally `fetchmail` (polls external POP3 accounts).
|
|
|
|
- Dovecot uses passwd-file auth: `roles/mail/templates/passwd.j2` generates `/opt/mail/passwd` with 8-field format (`user:hash::::::`) — the trailing `::::::` is required for userdb lookup
|
|
- Fetchmail delivers via LMTP to Dovecot port 24; the `auth_username_format = %Ln` in Dovecot's LMTP config strips the `@dovecot` domain that fetchmail appends
|
|
- Fetchmail 6.6.x rcfile syntax: `ssl` is a user-level option (inline after `password`), port is `smtphost dovecot/24` (slash-separated), `smtpport` keyword does not exist
|
|
- `mail.ladkau.de` is the Roundcube webmail client, not an MTA — no outgoing SMTP server is configured
|
|
|
|
### SSO
|
|
|
|
Keycloak at `sso.ladkau.de` is the identity provider. Gitea and Nextcloud are configured post-provisioning to use it (see `docs/runbook-configuration.md`).
|
|
|
|
## Runbooks
|
|
|
|
- `docs/runbook-provisioning.md` — step-by-step reinstall from scratch
|
|
- `docs/runbook-configuration.md` — first-run service configuration (Keycloak realm, Gitea admin, Nextcloud OIDC, etc.)
|
|
- `docs/runbook-mail-import.md` — migrating mbox-format mail from an old Dovecot server to Maildir++
|