Initial Traefik compose stack commit

This commit is contained in:
2026-07-16 20:02:07 -03:00
commit ee4e8804c5
8 changed files with 622 additions and 0 deletions
+217
View File
@@ -0,0 +1,217 @@
# Traefik reverse proxy stack
Docker Compose stack for [Traefik](https://traefik.io/) with Lets Encrypt (Cloudflare DNS challenge), a secured dashboard, and a reusable middleware library for security headers, CSP tiers, Authentik SSO, redirects, and IP allowlists.
## Prerequisites
- Docker Engine with Compose v2
- A Cloudflare account with API access for the domains you terminate here
- DNS for those domains pointed at the host that runs this stack
- A shared Docker network that your backend services also join (default: `docknet`)
## Quick start
```bash
git clone <this-repo> traefik && cd traefik
cp example.env .env
```
Edit `.env` (see [Environment variables](#environment-variables)), then:
```bash
docker compose --env-file .env up -d
```
Traefik will join (or create) the Docker network named by `NETWORK_NAME`, watch containers on that host for Traefik labels, and issue certificates via Cloudflare DNS. ACME data is stored in the Docker named volume `traefik-acme` (created automatically; no manual file setup).
## Layout
```
.
├── compose.yml # Traefik service, ports, dashboard labels, ACME volume
├── example.env # Env template (copy to .env)
└── traefik/
├── traefik.yaml # Static config: entrypoints, providers, TLS SANs
└── conf.d/
├── middlewares.yaml # Shared middleware library
├── routers.yaml # Optional file-based routers
└── services.yaml # Optional file-based services
```
Static config lives in `traefik/traefik.yaml`. Dynamic config (middlewares, optional routers/services) is loaded from `traefik/conf.d/` and hot-reloads when files change. Certificate state lives in the `traefik-acme` volume, not in the repo.
## Environment variables
Copy `example.env` to `.env` and fill in real values:
| Variable | Purpose |
| --- | --- |
| `CLOUDFLARE_EMAIL` | Cloudflare account email (ACME + API) |
| `CLOUDFLARE_API_KEY` | Cloudflare Global API Key (or scoped token with DNS edit) |
| `CONSOLE_HOST` | Hostname for the Traefik dashboard (e.g. `traefik.example.com`) |
| `NETWORK_NAME` | Docker network name shared with backends (default `docknet`) |
| `TRAEFIK_CONSOLE_AUTH` | Basic-auth users for the dashboard (`htpasswd` format) |
| `TRAEFIK_YAML` | Host path to static config (default `./traefik/traefik.yaml`) |
| `TRAEFIK_CONFD` | Host path to dynamic config dir (default `./traefik/conf.d`) |
Generate dashboard credentials:
```bash
# Install apache2-utils / httpd-tools if needed, then:
htpasswd -nbB admin 'your-strong-password'
```
Paste the output into `TRAEFIK_CONSOLE_AUTH`. If the value contains `$`, escape them for Compose (use `$$`) or quote carefully in `.env`.
## Ports
| Host | Container | Role |
| --- | --- | --- |
| `8342` | `80` | HTTP → redirects to HTTPS |
| `8767` | `443` | HTTPS |
| `8080` | `8080` | Traefik API / dashboard (also exposed via `CONSOLE_HOST` with TLS + basic auth) |
Point your public reverse path or firewall at `8767` (and `8342` if you want HTTP→HTTPS at the edge). Adjust the left-hand ports in `compose.yml` if you prefer `80`/`443` on the host.
## TLS and certificates
- Challenge: **DNS-01** via Cloudflare (`certificatesResolvers.letsencrypt`)
- Storage: Docker named volume `traefik-acme` mounted at `/letsencrypt` (Traefik creates `acme.json` on first run)
- Default certificate SANs for the TLS store are defined under `tls.stores.default` in `traefik/traefik.yaml` — edit that list for your domains
The volume survives `docker compose down` / `up`. Prefer `docker compose down` over `docker compose down -v` unless you intentionally want to wipe certs (which will re-hit Lets Encrypt rate limits).
Ensure Cloudflare credentials can create/delete `_acme-challenge` TXT records for every zone you serve.
## Dashboard
The dashboard is enabled and routed with Docker labels on the Traefik container:
- URL: `https://<CONSOLE_HOST>/`
- Entrypoint: `websecure`
- Auth: middleware `traefik-auth` (basic auth from `TRAEFIK_CONSOLE_AUTH`)
- Certificate: Lets Encrypt via `letsencrypt` resolver
## Using middlewares on your services
Connect apps to the same Docker network (`NETWORK_NAME`), enable Traefik, and attach middlewares by name with the `@file` provider:
```yaml
services:
myapp:
image: myapp:latest
networks:
- docknet
labels:
- traefik.enable=true
- traefik.http.routers.myapp.rule=Host(`app.example.com`)
- traefik.http.routers.myapp.entrypoints=websecure
- traefik.http.routers.myapp.tls.certresolver=letsencrypt
- traefik.http.routers.myapp.middlewares=security-supermax@file,gzip@file
- traefik.http.services.myapp.loadbalancer.server.port=8080
networks:
docknet:
external: true
name: docknet
```
Chain multiple middlewares with commas. File-provider names resolve as `<name>@file`.
## Middleware catalog
Defined in `traefik/conf.d/middlewares.yaml`.
### Security chains (pick one)
| Middleware | Intent |
| --- | --- |
| `default` | CSP strict + frame deny + gzip + HSTS + baseline security headers |
| `secure-all` | Permissive CSP + same-origin frames + HSTS + security headers |
| `security-mallcop` | Open referrer + open CSP + HSTS + security headers |
| `security-lax` | Lax referrer + lax CSP + HSTS + security headers |
| `security-medium` | Permissive referrer/CSP + same-origin frames + HSTS + security headers |
| `security-prison` | Medium referrer + balanced CSP + same-origin frames + HSTS + security headers |
| `security-supermax` | No-referrer + strict CSP + frame deny + HSTS + security headers |
| `security-supermax-with-analytics` | Supermax plus GA/Clarity/Umami-friendly CSP and COOP |
### CSP building blocks
| Middleware | Notes |
| --- | --- |
| `csp-open-header` | Very permissive (legacy / hard-to-lock apps) |
| `csp-lax-header` | Self + common inline/eval allowances |
| `csp-permissive-header` | Self-focused with inline/eval for scripts/styles |
| `csp-balanced-header` | Self + common CDNs (jsDelivr, cdnjs, unpkg, Google Fonts) |
| `csp-strict-header` | Self-only |
| `csp-strict-header-with-ga` | Strict plus analytics/Clarity/GTM endpoints |
### Other headers & utilities
| Middleware | Role |
| --- | --- |
| `security-headers` | `X-Content-Type-Options: nosniff`, disables legacy `X-XSS-Protection` |
| `hsts-header` | HSTS 2y, includeSubDomains, preload |
| `frame-deny-header` | `X-Frame-Options: DENY` |
| `frame-sameorigin-header` | `X-Frame-Options: SAMEORIGIN` |
| `cross-origin-opener-strict-policy` | `Cross-Origin-Opener-Policy: same-origin` |
| `gzip` | Response compression |
| `referrer-*-header` | Referrer-Policy tiers (`open``supermax`) |
| `strip-server-path` | Strips `/db`, `/server`, `/traefik` prefixes |
### Auth & access
| Middleware | Role |
| --- | --- |
| `authentik-outpost` | ForwardAuth to Authentik outpost (`outpost.mifi.holdings`) |
| `authentik-no-cache` | Disable caching on Authentik responses |
| `authentik-static-immutable` | Long-cache immutable static assets |
| `internal-ips` | IP allowlist (edit `sourceRange` for your networks) |
### Redirects
| Middleware | Role |
| --- | --- |
| `redirect-www-to-non-www` | `www.` → apex |
| `redirect-non-www-to-www` | apex → `www.` |
| `redirect-postfixadmin` | Mail settings path redirect (site-specific) |
Update Authentik outpost URL, IP allowlists, and domain-specific redirects in `middlewares.yaml` before deploying outside this environment.
## File-based routers and services
`traefik/conf.d/routers.yaml` and `services.yaml` are stubs for optional non-Docker backends. Uncomment or add entries when you need host/path rules that are not expressed as container labels.
## Operations
```bash
# Start / recreate after config or .env changes
docker compose --env-file .env up -d
# Follow logs
docker compose logs -f traefik
# Stop (keeps the ACME volume)
docker compose down
# Inspect ACME volume location (optional)
docker volume inspect traefik_traefik-acme
```
Dynamic files under `conf.d/` reload without a full restart. Changes to `traefik.yaml`, Compose labels, ports, or env vars generally need `docker compose up -d` (or a container recreate).
## Customization checklist
1. Set `.env` from `example.env` and generate `TRAEFIK_CONSOLE_AUTH`
2. Edit TLS SANs in `traefik/traefik.yaml` for your domains
3. Adjust `internal-ips` and Authentik URLs in `middlewares.yaml`
4. Ensure backends use `traefik.enable=true` and join `NETWORK_NAME`
5. Publish host ports (`8342`/`8767`) as needed for your network edge
## Security notes
- Do not commit `.env` or real API keys
- Prefer a scoped Cloudflare API token limited to DNS edit on required zones when possible
- Keep the dashboard behind strong basic auth (and consider further restricting with `internal-ips` or Authentik)
- Review CSP chains per app; start stricter and loosen only where breakage requires it
- Do not use `docker compose down -v` in production unless you mean to discard certificates