DMZ Tier¶
Architecture¶
The DMZ tier (CX22) is the only VM with public-facing ports (80/tcp, 443/tcp). It serves as the ingress layer for all web-accessible services.
flowchart LR
Internet((Internet)) -->|"443/tcp"| Caddy
subgraph DMZ["CX22 DMZ Node"]
Caddy["Caddy<br/>Reverse Proxy<br/>Auto-TLS"]
Auth["Authelia<br/>TOTP Auth"]
Ttyd["ttyd<br/>Web Terminal"]
Caddy -->|"forward_auth"| Auth
Caddy -->|"proxy"| Ttyd
end
Auth -.->|"authenticated"| Caddy
subgraph Hub["CX32 Hub (via private net)"]
Rancher["Rancher UI"]
Grafana["Grafana"]
end
Caddy -->|"10.0.1.1"| Rancher
Caddy -->|"10.0.1.1"| Grafana
style DMZ fill:#7b241c,stroke:#c0392b,color:#fff
style Hub fill:#1a5276,stroke:#2980b9,color:#fff
Caddy Reverse Proxy¶
Caddy handles all HTTPS ingress with automatic Let's Encrypt TLS certificates.
Caddyfile¶
{
email dev@vdhome.be
acme_dns hetzner {env.HETZNER_DNS_TOKEN}
}
ttyd.vdhome.be {
forward_auth authelia:9091 {
uri /api/verify?rd=https://auth.vdhome.be
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
}
reverse_proxy ttyd:7681
}
auth.vdhome.be {
reverse_proxy authelia:9091
}
rancher.vdhome.be {
forward_auth authelia:9091 {
uri /api/verify?rd=https://auth.vdhome.be
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
}
reverse_proxy 10.0.1.1:443 {
transport http {
tls_insecure_skip_verify
}
}
}
grafana.vdhome.be {
forward_auth authelia:9091 {
uri /api/verify?rd=https://auth.vdhome.be
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
}
reverse_proxy 10.0.1.1:3000
}
TLS Configuration¶
| Parameter | Value |
|---|---|
| CA | Let's Encrypt |
| Challenge type | DNS-01 (via Hetzner DNS API) |
| Auto-renewal | Yes (Caddy built-in) |
| Min TLS version | 1.2 |
| HSTS | Enabled (Caddy default) |
| OCSP stapling | Enabled (Caddy default) |
DNS-01 challenge
DNS-01 is used instead of HTTP-01 because it works for all subdomains without opening additional ports and allows wildcard certificates if needed.
Authelia TOTP Configuration¶
Authelia provides single-factor (TOTP) authentication for all web services.
| Parameter | Value |
|---|---|
| Auth method | TOTP (6-digit, 30s period) |
| Algorithm | SHA1 (standard TOTP) |
| User storage | Local YAML file (SOPS-encrypted in repo) |
| Session domain | .vdhome.be |
| Session lifetime | 8 hours |
| Inactivity timeout | 1 hour |
| Remember me | 30 days |
Protection Levels¶
| Domain | Policy |
|---|---|
auth.vdhome.be |
Bypass (Authelia portal itself) |
ttyd.vdhome.be |
one_factor (TOTP required) |
rancher.vdhome.be |
one_factor (TOTP required) |
grafana.vdhome.be |
one_factor (TOTP required) |
Why not two-factor?
Authelia supports two-factor (password + TOTP), but for a single-user lab, TOTP alone provides sufficient security. The TOTP seed is stored in an authenticator app and backed up in Bitwarden.
ttyd Web Terminal¶
ttyd provides a browser-based terminal, primarily for access from Defence workstations.
| Parameter | Value |
|---|---|
| Port | 7681 (internal) |
| Shell | /bin/bash |
| Forced command | tmux attach -t main \|\| tmux new -s main |
| Write mode | Enabled (authenticated users only) |
| Credential | Authelia handles auth (no ttyd-level auth) |
Forced Command¶
The ttyd session starts a tmux session, providing:
- Session persistence (browser disconnect does not kill the shell)
- Screen sharing (multiple browser tabs see the same session)
- Scrollback buffer
Security constraint
ttyd runs as the deploy user with sudo access. This is acceptable for a single-user lab but would require RBAC in a multi-user setup.
Web App Security Context¶
All web-facing pods in the ingress namespace run with restricted security contexts:
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE # Caddy only
DNS Records¶
| Record | Type | Target | Proxy |
|---|---|---|---|
ttyd.vdhome.be |
A | DMZ public IP | No |
auth.vdhome.be |
A | DMZ public IP | No |
rancher.vdhome.be |
A | DMZ public IP | No |
grafana.vdhome.be |
A | DMZ public IP | No |
vdhome.be |
A | DMZ public IP | No |
All DNS records are managed via OpenTofu using the Hetzner DNS provider.