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> |
||
|---|---|---|
| .. | ||
| group_vars | ||
| inventory | ||
| roles | ||
| ansible.cfg | ||
| README.md | ||
| requirements.yml | ||
| site.yml | ||
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
- An Ubuntu 24.04 VM you can SSH into as a sudo-capable user.
- A dedicated second virtual disk attached to the VM for the ZFS pool
(e.g.
/dev/sdbor/dev/vdb) — separate from the OS disk. - Ansible installed on your workstation (
pipx install ansibleor 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-checkzfs_pool_diskpoints 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_keysis non-empty before it will disable password authentication. Verify you can log in with your key before settingssh_disable_password_auth: true. - Firewall coexistence: host inbound rules live in a dedicated
inet heleosnftables table and never flush the global ruleset, so Docker's own iptables/nft rules are left intact. Container SMTP egress is blocked via aDOCKER-USERjump applied by theheleos-docker-egressservice.
What this sets up
- ZFS pool
tankwith platform datasets and thecustomersparent (see ../../docs/03-naming-conventions.md). - Docker Engine + Compose plugin, data-root on
tank/platform/dockerusing the nativezfsstorage 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
sftponlygroup 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