boma/docs/decisions/002-security.md

74 lines
2.8 KiB
Markdown
Raw Normal View History

# ADR-002 — Security baseline
## Context
Every managed host must reach a defined security baseline before any services
are deployed. This baseline is applied by the `base` role and is non-negotiable —
it runs first, on every host, every time.
The goal is a principled, maintainable baseline appropriate for a homelab with
some public-facing services — not a compliance exercise.
## Baseline components
### Access & authentication
- SSH key authentication only — password auth disabled
- Root login disabled — `PermitRootLogin no`
- Dedicated `ansible` user with locked-down sudo (NOPASSWD for automation)
- No shared user accounts — per-person SSH keys in `group_vars/all/vars.yml`
### Firewall
- `nftables` (native on Debian 13, replaces iptables)
- Default policy: deny inbound, allow established/related, allow loopback
- Rules managed entirely by Ansible — never edited manually on hosts
- Port definitions live in `group_vars/` so rules stay in sync with deployed services
- Docker's own iptables rules are disabled — nftables manages all filtering
> **Note on Docker + nftables**: Docker historically bypassed iptables-based firewalls.
> This is addressed by setting `"iptables": false` in Docker daemon config and managing
> all rules via nftables explicitly. See `docs/decisions/004-docker-model.md`.
### Intrusion deterrence
- `fail2ban` monitoring SSH (and optionally reverse proxy logs)
- Configured to ban after 5 failed attempts, 1-hour ban
### Updates
- `unattended-upgrades` enabled for **security patches only**
- Full system upgrades triggered deliberately via Ansible (`make deploy PLAYBOOK=upgrade`)
- No automatic reboots — reboots are a conscious operational decision
### Minimal attack surface
- No unnecessary packages installed
- Docker daemon TCP socket disabled — Unix socket only
- No open ports beyond those explicitly defined in firewall rules
### Audit trail
- `auditd` installed and running with a baseline ruleset
- Logs shipped to a central location if a log aggregation service is available
## Secrets management
- Ansible Vault for all secrets (API keys, passwords, certificates)
- Vault password stored outside the repo (`.vault_pass` gitignored)
- New collaborators receive vault password via a separate secure channel
- See `docs/runbooks/rotate-secrets.md` for rotation procedure
## What this baseline does not include
- Full CIS benchmark hardening — adds complexity for marginal gain at this scale
- SELinux / AppArmor — not applied by default, revisit if threat model changes
- Intrusion detection (IDS) — out of scope for now
## Decision
This baseline was chosen to be:
- **Effective** against the realistic threat model (exposed services, shared repo)
- **Maintainable** by a small team without security expertise overhead
- **Automated** — no manual steps should be needed to reach baseline state