platform/platform-infra/ansible/README.md
Bart Van Geyt d715244b76 Phase 1: Ansible host baseline for Ubuntu VM
Idempotent host configuration targeting Ubuntu 24.04 with ZFS on a
dedicated second disk. Role-based platform-infra/ansible:

- base: apt packages, timezone, unattended security upgrades.
- zfs: install ZFS, create pool on a dedicated disk (guarded against
  wiping a non-empty disk), create platform datasets + customers parent
  per docs/03; docker dataset mounted at /var/lib/docker.
- docker: Docker Engine + Compose plugin, daemon.json written before first
  start so the native zfs storage driver initializes on the ZFS data-root;
  per-site network address pool preconfigured.
- firewall: nftables inbound default-deny in a dedicated table that never
  flushes Docker's rules; container outbound SMTP blocked via a DOCKER-USER
  jump applied by a systemd oneshot.
- ssh_hardening: key-first SSH with an anti-lockout assertion, config
  validation gate, and the sftponly group for Phase 4 SFTP accounts.

Includes ansible.cfg, requirements.yml, inventory example, group_vars with
safety notes, and a run guide. Real inventory (hosts.yml) is git-ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 12:45:42 +02:00

3.3 KiB

Phase 1 — Host baseline (Ansible)

Idempotent host configuration for the heleos platform, targeting Ubuntu 24.04 LTS. Roles: base packages → ZFS pool/datasets → Docker (data-root on ZFS) → nftables firewall + container egress filter → SSH hardening.

Prerequisites

  1. An Ubuntu 24.04 VM you can SSH into as a sudo-capable user.
  2. A dedicated second virtual disk attached to the VM for the ZFS pool (e.g. /dev/sdb or /dev/vdb) — separate from the OS disk.
  3. Ansible installed on your workstation (pipx install ansible or apt).

Configure

cd platform-infra/ansible
ansible-galaxy collection install -r requirements.yml

cp inventory/hosts.yml.example inventory/hosts.yml   # edit host/user (git-ignored)
$EDITOR group_vars/all.yml                            # set zfs_pool_disk, keys, etc.

Key variables in group_vars/all.yml:

Variable Meaning
zfs_pool_disk The dedicated disk for the pool. Its contents will be destroyed.
zfs_pool_force Must be true to create a pool on a non-empty disk (safety gate).
admin_authorized_keys Public keys for the admin — required before disabling passwords.
ssh_disable_password_auth Leave false until key login is verified, then flip to true.
smtp_relay_host Optional; if set, containers may reach SMTP only on this host.

Run

ansible-playbook site.yml --check     # dry run (note: first run can't fully
                                      # check tasks that depend on ZFS/Docker
                                      # not yet present)
ansible-playbook site.yml             # apply

Run a single layer with tags: --tags zfs, --tags docker, --tags firewall, --tags ssh, --tags base.

Safety notes

  • ZFS is destructive: the play refuses to create a pool on a disk that already has a filesystem/partition unless zfs_pool_force: true. Double-check zfs_pool_disk points at the empty spare disk, not the OS disk. For production prefer a stable /dev/disk/by-id/... path over /dev/sdb.
  • SSH lock-out: the play asserts that admin_authorized_keys is non-empty before it will disable password authentication. Verify you can log in with your key before setting ssh_disable_password_auth: true.
  • Firewall coexistence: host inbound rules live in a dedicated inet heleos nftables table and never flush the global ruleset, so Docker's own iptables/nft rules are left intact. Container SMTP egress is blocked via a DOCKER-USER jump applied by the heleos-docker-egress service.

What this sets up

  • ZFS pool tank with platform datasets and the customers parent (see ../../docs/03-naming-conventions.md).
  • Docker Engine + Compose plugin, data-root on tank/platform/docker using the native zfs storage driver; per-site network address pool preconfigured.
  • nftables default-deny inbound (allow SSH/80/443 + established + loopback + ICMP); outbound SMTP blocked from containers.
  • Hardened SSH (key-first, root prohibit-password) and the sftponly group that per-customer SFTP accounts will join in Phase 4.

Verify after running

zpool status && zfs list
docker info | grep -E 'Storage Driver|Docker Root Dir'
sudo nft list table inet heleos
sudo iptables -S DOCKER-USER