--- id: task-011 title: 'Host-level Traefik hardening and cron setup' status: To Do assignee: [] created_date: '2026-03-15 08:00' labels: - dev-ops - enhancement dependencies: - task-010 priority: high --- ## Description Security hardening tasks that require host-level access on Netcup RS 8000. Cannot be done from the claude-dev container. Scripts are pre-built at `/opt/apps/dev-ops/`. ## Acceptance Criteria - [ ] #1 Run Traefik hardening script: `bash /opt/apps/dev-ops/traefik-hardening.sh` - [ ] #2 Edit Traefik docker-compose: add `cap_drop: [ALL]`, `cap_add: [NET_BIND_SERVICE]`, `security_opt: [no-new-privileges:true]`, `read_only: true` - [ ] #3 Restrict Traefik ports to localhost: `127.0.0.1:80:80` and `127.0.0.1:443:443` - [ ] #4 Add resource limits to Traefik: `memory: 512M`, `cpus: 2.0` - [ ] #5 Add CSP `frame-ancestors 'self'` to security-headers.yml - [ ] #6 Remove `insecureSkipVerify` from pentagi transport config - [ ] #7 Add weekly Docker prune cron: `23 4 * * 0 /opt/apps/dev-ops/docker-weekly-prune.sh >> /var/log/docker-prune.log 2>&1` - [ ] #8 Restart Traefik and verify: `cd /root/traefik && docker compose up -d` ## Notes ### Quick start ```bash # 1. Run the pre-built script (creates TLS + rate limit configs) bash /opt/apps/dev-ops/traefik-hardening.sh # 2. Edit Traefik compose (location: /root/traefik/docker-compose.yml) # Add to traefik service: # cap_drop: [ALL] # cap_add: [NET_BIND_SERVICE] # security_opt: [no-new-privileges:true] # read_only: true # tmpfs: [/tmp] # deploy: # resources: # limits: # memory: 512M # cpus: '2.0' # Change ports to: # - '127.0.0.1:80:80' # - '127.0.0.1:443:443' # 3. Add weekly prune cron crontab -e # Add: 23 4 * * 0 /opt/apps/dev-ops/docker-weekly-prune.sh >> /var/log/docker-prune.log 2>&1 # 4. Restart cd /root/traefik && docker compose up -d # 5. Verify curl -I https://jeffemmett.com ``` ### Risk - Restricting ports to 127.0.0.1 means ONLY Cloudflare tunnel traffic reaches Traefik. If cloudflared goes down, all external access is lost until it recovers. This is the intended behavior (no direct IP access). - `read_only` on Traefik may need a tmpfs for `/data` if ACME cert storage is inside the container.