Files
petrolyst-infra/traefik/compose.yaml
T
Nasim SadatandClaude Opus 5.5 7d01f66f84
deploy / deploy (push) Canceled after 0s
traefik: apply secure-headers before sso on the dashboard
forwardAuth answers unauthenticated requests itself, so a middleware listed
after it never sees that redirect: the dashboard's 302 to Keycloak went out
without HSTS, nosniff and frame options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 00:43:43 +02:00

120 lines
4.4 KiB
YAML

# Edge proxy for the whole server. Deployed first by deploy/deploy.sh, which
# exports TRAEFIK_CONFIG_HASH from the release's ARTIFACTS file.
#
# Any stack joins by attaching a service to the external network "proxy" and
# setting traefik.* labels; apps without their own login can add the "sso"
# middleware (Keycloak client petrolyst, role checked by oauth2-proxy).
name: traefik
services:
socket-proxy:
image: tecnativa/docker-socket-proxy:v0.5.0
environment:
CONTAINERS: "1"
EVENTS: "1"
PING: "1"
VERSION: "1"
POST: "0"
LOG_LEVEL: warning
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- socket
restart: unless-stopped
mem_limit: 64m
traefik:
image: traefik:v3.7.16
command: ["--configFile=/etc/traefik/static/traefik.yml"]
ports:
- "80:80"
- "443:443"
# Folders, not single files: promotion replaces files by renaming, which a
# single-file bind mount would not follow.
volumes:
- ./static:/etc/traefik/static:ro
- ./dynamic:/etc/traefik/dynamic:ro
- letsencrypt:/letsencrypt
depends_on:
- socket-proxy
networks:
- proxy
- socket
restart: unless-stopped
mem_limit: 256m
healthcheck:
test: ["CMD", "traefik", "healthcheck", "--configFile=/etc/traefik/static/traefik.yml"]
interval: 10s
timeout: 5s
retries: 6
labels:
petrolyst.config-hash: ${TRAEFIK_CONFIG_HASH:?TRAEFIK_CONFIG_HASH unset - run through deploy/deploy.sh}
traefik.enable: "true"
traefik.http.routers.dashboard.rule: Host(`${TRAEFIK_HOST:-traefik.petrolyst.com}`)
traefik.http.routers.dashboard.entrypoints: websecure
traefik.http.routers.dashboard.tls.certresolver: le
traefik.http.routers.dashboard.service: api@internal
# secure-headers first: sso answers unauthenticated requests itself, and
# middlewares after it never see that response.
traefik.http.routers.dashboard.middlewares: secure-headers@file,sso
# Keycloak gate for the dashboard host only (cookie, return hosts and
# role are tied to it; README "Adding a project"): unauthenticated
# requests are sent to Keycloak, users without petrolyst:traefik-admin
# get 403.
traefik.http.middlewares.sso.forwardauth.address: http://oauth2-proxy:4180/
traefik.http.middlewares.sso.forwardauth.trustForwardHeader: "true"
traefik.http.middlewares.sso.forwardauth.authResponseHeaders: X-Auth-Request-User,X-Auth-Request-Email
oauth2-proxy:
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.5
command:
- --provider=keycloak-oidc
- --oidc-issuer-url=${KEYCLOAK_REALM_URL:-https://iam.thedigitalbridge.io/realms/thedigitalbridge}
- --client-id=petrolyst
- --client-secret-file=/run/secrets/keycloak_petrolyst_client_secret
- --cookie-secret-file=/run/secrets/oauth2_proxy_cookie_secret
- --code-challenge-method=S256
- --allowed-role=petrolyst:traefik-admin
# Required by oauth2-proxy at startup; access is decided by the role above.
- --email-domain=*
- --redirect-url=https://${TRAEFIK_HOST:-traefik.petrolyst.com}/oauth2/callback
- --whitelist-domain=${TRAEFIK_HOST:-traefik.petrolyst.com}
- --cookie-domain=${TRAEFIK_HOST:-traefik.petrolyst.com}
- --cookie-secure=true
- --cookie-samesite=lax
- --reverse-proxy=true
- --upstream=static://202
- --skip-provider-button=true
- --set-xauthrequest=true
- --http-address=0.0.0.0:4180
secrets:
- keycloak_petrolyst_client_secret
- oauth2_proxy_cookie_secret
networks:
- proxy
restart: unless-stopped
mem_limit: 128m
labels:
traefik.enable: "true"
traefik.docker.network: proxy
traefik.http.routers.oauth2.rule: Host(`${TRAEFIK_HOST:-traefik.petrolyst.com}`) && PathPrefix(`/oauth2/`)
traefik.http.routers.oauth2.entrypoints: websecure
traefik.http.routers.oauth2.tls.certresolver: le
traefik.http.routers.oauth2.middlewares: secure-headers@file
traefik.http.services.oauth2.loadbalancer.server.port: "4180"
networks:
proxy:
external: true
socket:
internal: true
volumes:
letsencrypt:
secrets:
keycloak_petrolyst_client_secret:
file: ${PETROLYST_ROOT:-/opt/petrolyst}/secrets/shared/keycloak_petrolyst_client_secret
oauth2_proxy_cookie_secret:
file: ${PETROLYST_ROOT:-/opt/petrolyst}/secrets/traefik/oauth2_proxy_cookie_secret