Decompose architecture into 23 atomic tasks across 7 parallel generations

Task graph covers all Phase 1 concerns: config system, TLS termination,
proxy handler, operations (rate limiting, logging, health check, admin
socket, signals, shutdown, body size limit), deployment artifacts, and
two review checkpoints.

No circular dependencies. Critical path length of 7. Risk distribution:
3 high-risk (ACME, TLS listener setup, startup orchestration), 7 medium,
11 low, 2 trivial.
This commit is contained in:
2026-06-11 11:21:10 +00:00
parent ceb59ad9b9
commit 309878c561
23 changed files with 1676 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
---
id: deploy/systemd-and-container
name: Create systemd unit file, Dockerfile, and docker-compose.yml for production deployment
status: pending
depends_on: [ops/signals-and-shutdown]
scope: moderate
risk: low
impact: component
level: implementation
---
## Description
Create the deployment artifacts: systemd unit file, Dockerfile, and docker-compose.yml template.
### Systemd Unit File
```ini
[Unit]
Description=Reverse Proxy
After=network.target
Wants=network-online.target
[Service]
Type=notify
NotifyAccess=all
ExecStart=/usr/local/bin/reverse-proxy --config /etc/reverse-proxy/config.toml
Restart=on-failure
RestartSec=5
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/reverse-proxy /var/log/reverse-proxy
# ACME challenge cache directory
StateDirectory=reverse-proxy
[Install]
WantedBy=multi-user.target
```
The proxy signals readiness to systemd via `sd_notify("READY=1")` after binding listeners and completing initial configuration load.
### Dockerfile
Multi-stage build:
- Build stage: `rust:alpine` with `x86_64-unknown-linux-musl` target for static linking
- Run stage: `alpine` (or `scratch` for absolute minimum)
- The `aws_lc_rs` crypto provider is statically linked — no OpenSSL dependency
- Binary is self-contained, no runtime dependencies beyond libc for DNS resolution
### Docker Compose Template
Example template showing:
- Reverse proxy with volume mounts for config, ACME cache, logs, and admin socket
- `allow_wildcard_bind = true` for container deployments
- Health check using `wget` against local health endpoint
- Network configuration for upstream services
### Fail2ban Configuration
- Filter definition matching the `RATE_LIMIT` log prefix
- Jail configuration for rate-limiting offenders
## Acceptance Criteria
- [ ] Systemd unit file at `deploy/reverse-proxy.service`
- [ ] `Type=notify` with `sd_notify("READY=1")` integration in binary
- [ ] Security hardening directives in unit file
- [ ] `ReadWritePaths` for ACME cache and log directory
- [ ] Dockerfile with multi-stage build (`rust:alpine``alpine`/`scratch`)
- [ ] Static linking with `x86_64-unknown-linux-musl` target
- [ ] Docker Compose template at `deploy/docker-compose.yml`
- [ ] Volume mounts for config (ro), ACME cache (rw), logs (rw), admin socket (rw)
- [ ] Health check in Docker Compose using `wget` against `http://127.0.0.1:9900/health`
- [ ] Fail2ban filter definition at `deploy/fail2ban/filter.d/reverse-proxy.conf`
- [ ] Fail2ban jail configuration at `deploy/fail2ban/jail.d/reverse-proxy.conf`
- [ ] `docker build` succeeds
- [ ] Container starts and responds to health check
## References
- docs/architecture/operations.md — systemd, container deployment, fail2ban, health check
- docs/architecture/decisions/020-container-deployment.md — container model rationale
## Notes
> The Dockerfile should use `musl` for static linking. The `aws_lc_rs` crypto provider is statically linked. The resulting binary has no runtime dependencies beyond libc for DNS resolution (which `musl` provides).
## Summary
> To be filled on completion