diff --git a/infra/core/traefik/docker-compose.yml b/infra/core/traefik/docker-compose.yml new file mode 100644 index 0000000..1144898 --- /dev/null +++ b/infra/core/traefik/docker-compose.yml @@ -0,0 +1,47 @@ +services: + traefik: + image: traefik:v2.11 + container_name: ${INFRASTRUCTURE_LABEL:-stack}-traefik + restart: unless-stopped + command: + - --providers.file.directory=/etc/traefik/dynamic + - --providers.file.watch=true + - --providers.docker=true + - --providers.docker.exposedbydefault=false + - --providers.docker.network=${TRAEFIK_NETWORK:-proxy} + - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + - --certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL} + - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json + - --certificatesresolvers.letsencrypt.acme.httpchallenge=true + - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web + - --api.dashboard=true + - --log.level=${TRAEFIK_LOG_LEVEL:-INFO} + - --accesslog=true + ports: + - ${TRAEFIK_HTTP_PORT:-80}:80 + - ${TRAEFIK_HTTPS_PORT:-443}:443 + environment: + - TZ=${TZ:-UTC} + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./traefik.yml:/etc/traefik/traefik.yml:ro + - ./dynamic:/etc/traefik/dynamic:ro + - ./data:/letsencrypt + networks: + - proxy + labels: + - traefik.enable=true + - traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DASHBOARD_DOMAIN}`) + - traefik.http.routers.traefik.entrypoints=websecure + - traefik.http.routers.traefik.tls=true + - traefik.http.routers.traefik.tls.certresolver=letsencrypt + - traefik.http.routers.traefik.service=api@internal + - traefik.docker.network=${TRAEFIK_NETWORK:-proxy} + # Optional: protect dashboard with basic auth if TRAEFIK_BASIC_AUTH_USERS is set + - traefik.http.routers.traefik.middlewares=dashboard-basicauth@file + +networks: + proxy: + external: true + diff --git a/infra/core/traefik/dynamic/middlewares.yml b/infra/core/traefik/dynamic/middlewares.yml new file mode 100644 index 0000000..c850558 --- /dev/null +++ b/infra/core/traefik/dynamic/middlewares.yml @@ -0,0 +1,25 @@ +http: + middlewares: + redirect-to-https: + redirectScheme: + scheme: https + permanent: true + + security-headers: + headers: + frameDeny: true + contentTypeNosniff: true + browserXssFilter: true + referrerPolicy: no-referrer-when-downgrade + stsSeconds: 31536000 + stsIncludeSubdomains: true + stsPreload: true + + dashboard-basicauth: + basicAuth: + users: + # Provide users via env TRAEFIK_BASIC_AUTH_USERS, format: user:hashedpassword + # Example to generate: htpasswd -nbB admin 'yourpassword' + # If env is empty, you can comment this middleware out from labels + - ${TRAEFIK_BASIC_AUTH_USERS:-} + diff --git a/infra/core/traefik/dynamic/tls.yml b/infra/core/traefik/dynamic/tls.yml new file mode 100644 index 0000000..3808ea3 --- /dev/null +++ b/infra/core/traefik/dynamic/tls.yml @@ -0,0 +1,6 @@ +tls: + options: + default: + minVersion: VersionTLS12 + sniStrict: true + diff --git a/infra/core/traefik/traefik.yml b/infra/core/traefik/traefik.yml new file mode 100644 index 0000000..bd0e1e4 --- /dev/null +++ b/infra/core/traefik/traefik.yml @@ -0,0 +1,30 @@ +api: + dashboard: true + +providers: + docker: + exposedByDefault: false + network: ${TRAEFIK_NETWORK:-proxy} + file: + directory: /etc/traefik/dynamic + watch: true + +entryPoints: + web: + address: ":80" + websecure: + address: ":443" + +log: + level: ${TRAEFIK_LOG_LEVEL:-INFO} + +accessLog: {} + +certificatesResolvers: + letsencrypt: + acme: + email: ${ACME_EMAIL} + storage: /letsencrypt/acme.json + httpChallenge: + entryPoint: web +