Claude Code 0f76abd1fa Harden geoblock, remove secrets from config, support dev host
Refs #1

## Security

* Remove the CrowdSec LAPI key from config.yml. Traefik does not interpolate
  env vars in its YAML config, so the key had to be literal there - which is
  why the file kept drifting out of version control. It is now defined as a
  docker-compose label, where compose substitutes ${CROWDSEC_LAPI_KEY} from
  .env. config.yml is therefore secret-free and stays in git.

  NOTE: this renames the middleware crowdsec-bouncer@file -> @docker.
  Site stacks referencing @file must be updated.

  The old key remains in this repo's history and must still be rotated and
  scrubbed - removing it here does not un-expose it.

* Move the dashboard basic-auth hash out of docker-compose.yml into .env.
  In .env every '$' must be doubled to '$$', or compose silently collapses
  the hash to an empty string and the dashboard rejects every password.

* Stop publishing port 8080. The API is not insecure-mode, so nothing served
  there; publishing it only widened exposure.

## Geoblock (the availability fix)

* allowUnknownCountries: false -> true. Previously a geojs.io outage made
  every lookup "unknown" and therefore blocked, taking sites down for US
  visitors too - a third-party service in the critical path with no degraded
  mode. Now an outage degrades to "geoblocking ineffective". CrowdSec still
  blocks actual attackers.

* cacheSize: 25 -> 5000. At 25, nearly every visitor triggered a fresh API
  call, adding up to 750ms and risking rate limits.

## Dev host support

* Add traefik.dev.yml, selected by TRAEFIK_STATIC_CONFIG in .env (defaults to
  traefik.yml, so prod behaviour is unchanged). It differs from traefik.yml
  ONLY in the ACME challenge type: public port 80 forwards to prod for its
  renewals, so an HTTP-01 challenge for a dev hostname hits prod's Traefik,
  matches no router, and 404s. Dev uses DNS-01 via Route 53 instead, which
  needs no inbound connectivity.

* Add .env.example documenting every variable.

## Captured from prod's working tree

Prod had uncommitted drift; these were running but never committed:
plugin bumps (bouncer v1.3.3->v1.3.5, geoblock v0.2.7->v0.2.8), the
entryPoints HTTP->HTTPS redirect with priority 1, and httpClientTimeout: 0.

The priority: 1 on that redirect is load-bearing - it keeps the redirect
below normal routers so basil's Host(`localhost`) HTTP route still serves.
Do not raise it.

## Verified on dev (prkl10), Traefik 3.7.10

7 routers / 8 middlewares, 0 warnings, 0 errors. Certificates issued via
DNS-01. pihole and technitium routers preserved. Sites serving 200.
2026-08-05 02:09:45 -06:00

Traefik Reverse Proxy Configuration

This directory contains the Traefik reverse proxy configuration for managing SSL certificates and routing traffic to services.

Services Managed

  • Mealie (recipes.pkartchner.com) - Recipe manager
  • Gogs (git.pkartchner.com) - Git repository server
  • Traefik Dashboard (traefik.pkartchner.com) - Traefik management UI

Features

  • Automatic HTTPS with Let's Encrypt SSL certificates
  • Automatic HTTP to HTTPS redirect
  • Docker service discovery
  • Security headers middleware
  • Traefik dashboard with basic auth

Files

  • docker-compose.yml - Traefik container configuration
  • traefik.yml - Main Traefik configuration
  • config.yml - Dynamic configuration for external services
  • acme.json - Let's Encrypt certificate storage (auto-generated)

Setup

1. DNS Configuration

Ensure these DNS records point to your server's public IP:

A    recipes.pkartchner.com    →  YOUR_PUBLIC_IP
A    git.pkartchner.com        →  YOUR_PUBLIC_IP
A    traefik.pkartchner.com    →  YOUR_PUBLIC_IP

2. Start Traefik

cd /srv/docker-compose/traefik
docker compose up -d

3. Check Logs

docker logs traefik -f

Dashboard Access

Access the Traefik dashboard at: https://traefik.pkartchner.com

Default credentials:

  • Username: admin
  • Password: change-this-password

Change the password:

# Generate new password hash
echo $(htpasswd -nb admin yournewpassword) | sed -e s/\\$/\\$\\$/g

# Update the label in docker-compose.yml:
# traefik.http.middlewares.traefik-auth.basicauth.users=admin:$HASH

SSL Certificates

Traefik automatically obtains and renews SSL certificates from Let's Encrypt.

  • Certificates are stored in acme.json
  • Auto-renewal happens 30 days before expiration
  • Email notifications sent to: pkartch@gmail.com

Staging vs Production

The configuration uses Let's Encrypt production by default.

To use staging (for testing, to avoid rate limits): Uncomment this line in traefik.yml:

caServer: https://acme-staging-v02.api.letsencrypt.org/directory

Port Configuration

  • 80 - HTTP (redirects to HTTPS)
  • 443 - HTTPS (main entry point)
  • 8080 - Traefik dashboard

Adding New Services

Docker Services

Add labels to your service's docker-compose.yml:

services:
  myservice:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.myservice.rule=Host(`myservice.pkartchner.com`)"
      - "traefik.http.routers.myservice.entrypoints=https"
      - "traefik.http.routers.myservice.tls.certresolver=letsencrypt"
      - "traefik.http.services.myservice.loadbalancer.server.port=PORT"
    networks:
      - traefik

External Services

Add to config.yml:

http:
  routers:
    myservice:
      rule: "Host(`myservice.pkartchner.com`)"
      entryPoints:
        - https
      service: myservice
      tls:
        certResolver: letsencrypt

  services:
    myservice:
      loadBalancer:
        servers:
          - url: "http://INTERNAL_IP:PORT"

Troubleshooting

Check Traefik logs

docker logs traefik --tail 100

Verify network

docker network ls | grep traefik

Test certificate

openssl s_client -connect recipes.pkartchner.com:443 -servername recipes.pkartchner.com

Reload configuration

docker compose restart traefik

Security Notes

  • Change the default dashboard password immediately
  • Keep acme.json permissions at 600
  • Regularly update Traefik image
  • Monitor access logs
  • Consider disabling the dashboard in production

Maintenance

Update Traefik

docker compose pull
docker compose up -d

Backup certificates

cp acme.json acme.json.backup

View certificate info

Check the Traefik dashboard under "HTTP" → "Routers"

Description
Deploy of Traefik for docker server
Readme 55 KiB
Languages
Markdown 100%