---
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

<!-- SECTION:DESCRIPTION:BEGIN -->
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/`.
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #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`
<!-- AC:END -->

## 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.
