NixOS
There is a greate overview with guides and additional ressources to harden the base OS in the Wiki.
Basic Hardening
- Non-Root User: eg.
user = “1000:100”
- Advanced:
user = "nobody:nogroup"
- drop capabilities, restart, read the error if it crashes, add back the minimum
- run
podman inspect (check capabilities: NET_RAW, SYS_CHROOT, MKNOD, ...)
- add
capabilities = {ALL = false} to everything
- Caddy, Vaultwarden, (maybe more) need
NET_BIND_SERVICE for port 80/443
- PostgreSQL: needs to chown data directories (
CHOWN, SETUID, SETGID, ...)
--security-opt=no-new-privileges
- read-only filesystem:
--read-only
- make sure these are
false:
Podman Networks
- don't put everything on the same network: Don't use the default bridge network
- Use Reverse Proxy for HTTPS
- expose only containers which are made for being exposed
- databases have own networks:
internal: true (zero internet access, only specific app)
- Reverse proxy gets its own network
- Inter-service communication goes through a separate network, eg. mail
# after: database isolated, no internet
networks:
default:
name: myapp_db
internal: true
web_ingress:
external: true
Related: use per-service database container. I already do this though. Just in case, two services should share a database: Dedicated database + role per service, connection limits per role, and then revoked CONNECT from PUBLIC on every database.
Resource limits
- disable swap per container (memswap_limit = mem_limit): only container gets killed, not whole box
- mem_limit: educated guess from stats or do real profiling?
- add PID limits: prevent fork bomb
- tiered CPU with cpu_shares:
- Reverse proxy and databases get highest priority
- App services get medium
- Background workers get lowest
Health checks
- replace "is the process alive" with real HTTP check
- each runtime needs its own approach:
- Node: no curl, use Node's http module inline
- Python slim: no curl, use urllib
- Postgres: pg_isready just works
Pinned versions & Auto Updates
This adds little to no security because version tags are still mutable. However it would make it safer/easier to use podman auto-update.
When using the podman home-manager module, I can activate auto-updating on a weekly basis like this:
per container: services.podman.containers.<name>.autoUpdate
services.podman.autoUpdate = {
enable = true;
onCalendar = "Sun *-*-* 00:00";
};
For maximum security I'd have to pin every image to SHA256 digests, Tradeoff: manual updates
HTTP Headers
e.g. HSTS, CSP, ...
Check Security for each service: Mozilla Observatory
Fail2Ban
Enable Logs for services to be used in fail2ban jails. Look for service-specific guides.
Secrets
- I already use sops for secrets
- when possible no plain-text env vars: prefer Files / Podman Secrets
- API Keys: Use scoped tokens where possible
Fix once
Reverse Proxy
Use TLS 1.2 minimum, Restricted ciphers (already default in caddy).
Maybe switch to nginx with lego or certbot for HTTPS certs?
Sources
NixOS
There is a greate overview with guides and additional ressources to harden the base OS in the Wiki.
Basic Hardening
user = “1000:100”user = "nobody:nogroup"podman inspect(check capabilities: NET_RAW, SYS_CHROOT, MKNOD, ...)capabilities = {ALL = false}to everythingNET_BIND_SERVICEfor port 80/443CHOWN,SETUID,SETGID, ...)--security-opt=no-new-privileges--read-only--read-only-tmpfsfalse:--privileged--ttyPodman Networks
internal: true(zero internet access, only specific app)Related: use per-service database container. I already do this though. Just in case, two services should share a database: Dedicated database + role per service, connection limits per role, and then revoked CONNECT from PUBLIC on every database.
Resource limits
Health checks
Pinned versions & Auto Updates
This adds little to no security because version tags are still mutable. However it would make it safer/easier to use
podman auto-update.When using the podman home-manager module, I can activate auto-updating on a weekly basis like this:
per container:
services.podman.containers.<name>.autoUpdateFor maximum security I'd have to pin every image to SHA256 digests, Tradeoff: manual updates
HTTP Headers
e.g. HSTS, CSP, ...
Check Security for each service: Mozilla Observatory
Fail2Ban
Enable Logs for services to be used in fail2ban jails. Look for service-specific guides.
Secrets
Fix once
caddy-etc,stalwart-etc, ...Reverse Proxy
Use TLS 1.2 minimum, Restricted ciphers (already default in caddy).
Maybe switch to nginx with lego or certbot for HTTPS certs?
Sources