Security Posture¶
Defence in depth¶
Layer 0 — Network¶
- UFW deny-by-default with 4 ports open: 22, 80, 443, 2222
- No public Docker socket exposure
- nginx is the only process bound to public 80/443
Layer 1 — Identity & secrets¶
- Vaultwarden at
vault.joaoluisbrazao.cloudis the authoritative password store - Signups are closed — new accounts require admin action
- No secret committed to git —
.envfiles are read at container start and never persisted to images - SSH key auth only — root password disabled
Layer 2 — Runtime monitoring¶
- Falco runs continuously, detecting anomalous syscall patterns at the kernel level
- ClamAV scans uploads and email attachments via the
clamavcontainer - Uptime Kuma with 36 monitors watches every public surface
- Alert pipe: Falco/Kuma → Telegram
@DownRabbitbot(CHAT_ID1827458013)
Layer 3 — Application¶
- All inputs validated at FastAPI level via Pydantic v2
- SQL queries always parameterised — never string-concatenated
- JWT tokens: 15 min access, 7 days refresh, httpOnly cookies
- CORS: explicit allowlist, never wildcard
- Security headers enforced by nginx: X-Frame-Options, X-Content-Type-Options, CSP
What we reject¶
- GitHub — sovereignty (see adr/0001-no-cloud-no-github.md)
- OpenClaw — RGPD Article 9 incompatibility after ClawHavoc (see adr/0003-no-openclaw.md)
- n8n for health data — workflow logs leak too easily
Data classification¶
| Class | Examples | Where it lives |
|---|---|---|
| Public | TCN site, gamine.org marketing | static files served by nginx |
| Internal | Vikunja tasks, brain knowledge | Postgres on VPS, behind auth |
| Sensitive | Vaultwarden vault, credentials | Vaultwarden only, never copied |
| RGPD Art. 9 (health) | MedVault (when deployed) | Local only — no external LLM, no external cloud |
SSL/TLS¶
- All public domains served via HTTPS only — HTTP redirects to HTTPS
- TLS 1.3 enforced; TLS 1.2 left enabled only for legacy compat where needed
- Certs renew automatically via
infra-certbot; expiry alerts via Telegram
Known gaps¶
| Gap | Tracking | Severity |
|---|---|---|
| No off-site backup copies | Vikunja #59 |
High |
| Volume backups empty for some volumes | Vikunja #59 |
High |
| Quarterly restore drill never performed | (this doc) | Medium |
| Backstage SSO for docs not yet implemented | (this doc) | Low |
| Hostinger-level firewall not configured | Acceptable (UFW handles it) | None |
| Malware scanner via hPanel not installed | Acceptable (ClamAV container handles it) | None |