Metainformationen zur Seite
  •  

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
server:traefik [2025/09/12 10:30] walbaumserver:traefik [2025/09/25 15:05] (aktuell) walbaum
Zeile 1: Zeile 1:
 ====== Traefik ====== ====== Traefik ======
  
-;; URL :: [[https://traefik.smns-bw.org/|https://traefik.smns-bw.org/]] +  ; URL : [[https://traefik.smns-bw.org/|https://traefik.smns-bw.org/]] 
- ;; User :: smns-tr  +  ; User : smns-tr  
-;; Passwort :: <decrypt>U2FsdGVkX18v7/dGh8b4MGuIQ8RPzXqjcWSlZ/AaEFgCQeNQcE1AYQxEWGLdiM9Y</decrypt> +  ; Passwort : <decrypt>U2FsdGVkX18v7/dGh8b4MGuIQ8RPzXqjcWSlZ/AaEFgCQeNQcE1AYQxEWGLdiM9Y</decrypt> 
- ;; Production :: hetzner:/opt/traefik/+  ; Production : hetzner:/opt/traefik/
  
-├── traefik\\ +<code> 
-│ ├── configfiles\\ +├── traefik 
-│ │ ├── {{:server:config.yml|config.yml}} \\ +│ ├── configfiles 
-│ │ ├── {{:server:middleware-chains.yml|middleware-chains.yml}} \\ +│ │ ├── config.yml 
-│ │ ├── {{:server:middlewares.yml|middlewares.yml}} \\ +│ │ ├── middleware-chains.yml 
-│ │ ├── {{:server:tls-opts.yml|tls-opts.yml}} \\ +│ │ ├── middlewares.yml 
-│ ├── {{:server:docker-compose.yml|docker-compose.yml}} \\ +│ │ ├── tls-opts.yml 
-│ ├── .env\\ +│ ├── docker-compose.yml 
-│ ├── traefik.log\\ +│ ├── .env 
-│ ├── {{:server:traefik.yml|traefik.yml}} \\+│ ├── traefik.log 
 +│ ├── traefik.yml
 │ ├── access.log │ ├── access.log
 +</code>
  
 In .env steht die URL traefik.smns-bw.org sowie die Zugangsdaten für diese Seite für docker-compose.yml In .env steht die URL traefik.smns-bw.org sowie die Zugangsdaten für diese Seite für docker-compose.yml
Zeile 40: Zeile 42:
 Damit der Docker.sock nicht nach außen exposed ist, ist zusammen mit Traefik ein docker-socket aufgesetzt, der von hier stammt: [[https://github.com/wollomatic/socket-proxy|https://github.com/wollomatic/socket-proxy]] Damit der Docker.sock nicht nach außen exposed ist, ist zusammen mit Traefik ein docker-socket aufgesetzt, der von hier stammt: [[https://github.com/wollomatic/socket-proxy|https://github.com/wollomatic/socket-proxy]]
  
-docker-compose.yml+<codedoc toggle docker-compose.yml>
-<code>+
 services: services:
   traefik:   traefik:
Zeile 149: Zeile 150:
     driver: bridge     driver: bridge
     internal: true     internal: true
-</code>+</codedoc> 
 +\\ 
 +<codedoc toggle traefik.yml> 
 +entryPoints: 
 +  http: 
 +    address: ":80" 
 +    http: 
 +      redirections: 
 +        entryPoint: 
 +          to: ':443' 
 +          scheme: https 
 +  https: 
 +    address: ":443" 
 +#    http3: 
 +#      advertisedPort: 443 
 +  postgres: 
 +    address: ":5434" 
 +  ssh: 
 +    address: ":666" 
 +  healthcheck: 
 +    address: ":8082" 
 +log: 
 +  level: INFO 
 +  filePath: "/logs/traefik.log" 
 +  format: json 
 + 
 +accessLog: 
 +  filePath: "/logs/access.log" 
 +  bufferingSize: 100 
 + 
 +api: {} 
 + 
 +ping: 
 +  entryPoint: healthcheck 
 + 
 +providers: 
 +  docker: 
 +#    endpoint: "tcp://socket-proxy:2375" 
 +    endpoint: "tcp://dockerproxy:2375" 
 +#    endpoint: "unix:///var/run/docker.sock" 
 +    watch: true 
 +    network: proxy 
 +#    exposedByDefault: false 
 +  file: 
 +    directory: "/configfiles" 
 +    watch: true 
 + 
 +certificatesResolvers: 
 +  leresolver: 
 +    acme: 
 +      email: "it@smns-bw.org" 
 +      storage: "/acme.json" 
 +      caServer: "https://acme-v02.api.letsencrypt.org/directory" 
 +      tlsChallenge: {} 
 + 
 +experimental: 
 +  plugins: 
 +    traefik-plugin-cookie-path-prefix: 
 +      moduleName: "github.com/SchmitzDan/traefik-plugin-cookie-path-prefix" 
 +      version: "v0.0.3" 
 +</codedoc> 
 +\\ 
 +<codedoc toggle config.yml> 
 +http: 
 +  routers: 
 +    webmin: 
 +      entryPoints: 
 +        - "https" 
 +      rule: Host(`webmin.smns-bw.org`) 
 +      service: webmin 
 +      middlewares: 
 +        - "secure-webmin" 
 +      tls: 
 +        certResolver: leresolver 
 +        options: tls-opts 
 + 
 +    traefik: 
 +      rule: Host(`traefik.smns-bw.org`) 
 +      service: cookies 
 + 
 +    smns_stats: 
 +      rule: Host(`statistics.smns-bw.org`) 
 +#      rule: "PathPrefix(`/statistics`)" 
 +      service: smns_stats 
 +      entryPoints: 
 +        - "https" 
 +      middlewares: 
 +        - "secure-collections" 
 +      tls: 
 +        certResolver: leresolver 
 +        options: tls-opts 
 + 
 +  services: 
 +    webmin: 
 +      loadBalancer: 
 +        servers: 
 +          - url: "http://172.18.0.1:10000/" 
 +        passHostHeader: true 
 +        sticky: 
 +          cookie: 
 +            secure: true 
 +            httpOnly: true 
 +            sameSite: lax 
 + 
 +    smns_stats: 
 +      loadBalancer: 
 +        servers: 
 +          - url: "http://172.18.0.1:8050" 
 +        passHostHeader: true 
 + 
 +    cookies: 
 +      loadBalancer: 
 +        sticky: 
 +          cookie: 
 +            secure: true 
 +            httpOnly: true 
 +            sameSite: lax 
 +</codedoc> 
 +\\ 
 +<codedoc toggle middleware-chains.yml> 
 +http: 
 +  middlewares: 
 +    secure-global: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "cors-default" 
 +          - "CSP-base" 
 + 
 +    secure-traefik: 
 +      chain: 
 +        middlewares: 
 +          - "traefik-auth"               # auth first, so errors still get headers/CSP 
 +          - "security-headers-base" 
 +          - "cors-default" 
 +          - "CSP-base" 
 + 
 +    secure-idservice: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "cors-default" 
 +          - "CSP-idservice" 
 + 
 +    secure-biocase: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "cors-default" 
 +          - "CSP-biocase" 
 + 
 +    secure-webmin: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "cors-default" 
 +          - "CSP-webmin" 
 + 
 +    secure-prestashop: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "cors-default" 
 +          - "CSP-prestashop" 
 + 
 +    secure-ent: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "cors-default" 
 +          - "CSP-ent" 
 + 
 +    secure-collections: 
 +      chain: 
 +        middlewares: 
 +          - "webportal-errors"            # moved before headers so error pages get hardened 
 +          - "security-headers-hsts-subdomains" 
 +          - "security-headers-allowframes" 
 +          - "cors-collections" 
 +          - "CSP-collections" 
 + 
 +    secure-digiphyll: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "security-headers-allowframes" 
 +          - "cors-default" 
 +          - "CSP-digiphyll" 
 + 
 +    secure-dokuwiki: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-hsts-subdomains" 
 +          - "security-headers-allowframes" 
 +          - "cors-default" 
 +          - "CSP-dokuwiki" 
 + 
 +    secure-geometroidea: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "cors-default" 
 +          - "CSP-geometroidea" 
 + 
 +    secure-h5p: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "security-headers-allowframes" 
 +          - "cors-default" 
 +          - "CSP-h5p" 
 + 
 +    secure-awstats: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "cors-default" 
 +          - "CSP-awstats" 
 + 
 +    secure-librechat: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-noindex" 
 +          - "cors-default" 
 +          - "CSP-librechat" 
 + 
 +    secure-pictures: 
 +      chain: 
 +        middlewares: 
 +          - "security-headers-base" 
 +          - "security-headers-allowframes" 
 +          - "cors-default" 
 +          - "CSP-base" 
 +</codedoc> 
 +\\ 
 +<codedoc toggle middlewares.yml> 
 +http: 
 +  middlewares: 
 +    default-whitelist: 
 +      ipWhiteList: 
 +        sourceRange: 
 +          - "127.0.0.1/32" 
 +          - "172.19.0.0/24" 
 + 
 +    traefik-auth: 
 +      basicAuth: 
 +        users: 
 +          - "smns-tr:$2y$10$mUF04Rf7852wnP5xGxewte1hW4X/LcEN2c7of5xPOEyzbAXGVrEAi" 
 + 
 +    # Core security baseline used by most services 
 +    security-headers-base: 
 +      headers: &SEC_BASE 
 +      addVaryHeader: true 
 + 
 +      # Response header hardening 
 +      contentTypeNosniff: true 
 +      browserXssFilter: true 
 +      referrerPolicy: "same-origin" 
 +      permissionsPolicy: fullscreen=(self "https://smns-bw.org"), geolocation=*, midi=(), camera=(), usb=(), magnetometer=(), accelerometer=(), vr=(), speaker=(), ambient-light-sensor=(), gyroscope=(), microphone=(), payment=() 
 + 
 +      # HSTS 
 +      forceSTSHeader: true 
 +      stsSeconds: 31536000 
 +      stsPreload: false 
 +      # Keep includeSubdomains off in base to avoid accidental lock-in; use variant below when desired. 
 +      # stsIncludeSubdomains: false 
 + 
 +      # X-Frame-Options — deny by default (use CSP frame-ancestors for granular control) 
 +      frameDeny: true 
 +      # customFrameOptionsValue: "SAMEORIGIN"  # optional alternative if you want XFO too 
 + 
 +      # TLS/Proxy awareness 
 +      sslRedirect: true                # FIX: belongs at headers root (not inside sslProxyHeaders) 
 +      sslProxyHeaders: 
 +        X-Forwarded-Proto: "https" 
 +      hostsProxyHeaders: 
 +        - "X-Forwarded-Host" 
 + 
 +      # Standard response scrubbing 
 +      customResponseHeaders: &RESP_BASE 
 +        Server: ""                     # FIX: unify case 
 +        X-Powered-By: ""               # FIX: unify case 
 +        X-Robots-Tag: "index, follow"  # override via -noindex variant 
 + 
 +      # Upstream request adjustments (to your apps) 
 +      customRequestHeaders: &REQ_BASE 
 +        X-Forwarded-Proto: "https"     # OK to keep here 
 + 
 +    # Variant: noindex 
 +    security-headers-noindex: 
 +      headers: 
 +      <<: *SEC_BASE 
 +      customResponseHeaders: 
 +        <<: *RESP_BASE 
 +        X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex" 
 + 
 +    # Variant: allow framing (prefer CSP frame-ancestors to control who can frame you) 
 +    security-headers-allowframes: 
 +      headers: 
 +      <<: *SEC_BASE 
 +      frameDeny: false 
 +      # customFrameOptionsValue is mostly ignored by modern browsers except SAMEORIGIN/DENY. 
 +      # "ALLOW-FROM" is deprecated; rely on CSP if you need granular control. 
 +      customFrameOptionsValue: "SAMEORIGIN" 
 + 
 +    # Variant: HSTS include subdomains, longer max-age 
 +    security-headers-hsts-subdomains: 
 +      headers: 
 +      <<: *SEC_BASE 
 +      stsIncludeSubdomains: true 
 +      stsSeconds: 63072000 
 + 
 +    # Optional: shared Cache-Control for static-ish services 
 +    cache-public: 
 +      headers: 
 +      customResponseHeaders: 
 +        Cache-Control: "public"         # FIX: was (incorrectly) in customRequestHeaders 
 + 
 +    # CORS: compose these with the security headers as needed 
 +    cors-default: 
 +      headers: &CORS_DEFAULT 
 +      accessControlAllowMethods: 
 +        - GET 
 +        - OPTIONS 
 +        - PUT 
 +      accessControlAllowHeaders: "*" 
 +      accessControlMaxAge: 100 
 +      addVaryHeader: true 
 + 
 +    cors-collections: 
 +      headers: 
 +      <<: *CORS_DEFAULT 
 +      accessControlAllowOriginList: 
 +        - https://pydeepzoom.smns-bw.org 
 +        - https://pictures.smns-bw.org 
 + 
 +    # Base CSP used by services that only need same-origin + *.smns-bw.org 
 +    CSP-base: 
 +      headers: 
 +      contentSecurityPolicy: &CSP_BASE > 
 +        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        frame-ancestors 'self' *.smns-bw.org; 
 +        frame-src *.smns-bw.org; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        img-src 'self' data:; 
 +        connect-src 'self'; 
 +        font-src 'self'; 
 +        style-src 'self' 'unsafe-inline'; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 + 
 +    # If you still reference CSP-global anywhere, just point it to the base: 
 +    CSP-global: 
 +      headers: 
 +      contentSecurityPolicy: *CSP_BASE 
 + 
 +    # Deltas below (only where different from base) 
 + 
 +    CSP-dokuwiki: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        frame-ancestors 'self' *.smns-bw.org; 
 +        frame-src *.smns-bw.org *.google.com; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        img-src 'self' https://www.dokuwiki.org https://www.gravatar.com data:; 
 +        connect-src 'self'; 
 +        font-src 'self'; 
 +        style-src 'self' 'unsafe-inline'; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 + 
 +    CSP-idservice: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        frame-ancestors 'self' *.smns-bw.org; 
 +        frame-src *.smns-bw.org; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        img-src 'self' https://physalia.evolution.uni-bonn.de data:; 
 +        connect-src 'self'; 
 +        font-src 'self'; 
 +        style-src 'self' 'unsafe-inline'; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org https://physalia.evolution.uni-bonn.de; 
 + 
 +    CSP-librechat: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        frame-ancestors 'self' *.smns-bw.org; 
 +        frame-src *.smns-bw.org; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        img-src 'self' blob: data:; 
 +        connect-src 'self'; 
 +        font-src 'self'; 
 +        style-src 'self' 'unsafe-inline'; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 + 
 +    # Fixed: removed invalid 'unsafe-inline' and 'unsafe-eval' from -elem directives 
 +    CSP-awstats: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        frame-ancestors 'self' *.smns-bw.org; 
 +        frame-src *.smns-bw.org; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        img-src 'self' https://chart.googleapis.com; 
 +        connect-src 'self' https://www.gstatic.com/charts/; 
 +        font-src 'self'; 
 +        style-src 'self' 'unsafe-inline'; 
 +        style-src-elem https://www.gstatic.com/charts/; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        script-src-elem https://www.google.com/jsapi https://www.gstatic.com/charts/; 
 + 
 +    CSP-h5p: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        frame-ancestors https://www.naturkundemuseum-bw.de; 
 +        frame-src *.smns-bw.org; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        img-src 'self' data:; 
 +        connect-src 'self'; 
 +        font-src 'self'; 
 +        style-src 'self' 'unsafe-inline'; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 + 
 +    CSP-biocase: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        frame-ancestors 'self' *.smns-bw.org; 
 +        frame-src *.smns-bw.org; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        img-src 'self' https://unpkg.com/ https://*.tile.osm.org data:; 
 +        connect-src 'self'; 
 +        font-src 'self'; 
 +        style-src 'self' 'unsafe-inline' https://unpkg.com/leaflet@1.3.3/dist/leaflet.css; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org https://unpkg.com/leaflet@1.3.3/dist/leaflet.js https://code.jquery.com/jquery-1.7.min.js; 
 + 
 +    CSP-prestashop: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        frame-ancestors 'self' *.smns-bw.org; 
 +        frame-src *.smns-bw.org; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        img-src 'self' data:; 
 +        connect-src 'self'; 
 +        font-src 'self'; 
 +        style-src 'self' 'unsafe-inline'; 
 +        style-src-elem https://market.smns-bw.org; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' https://market.smns-bw.org; 
 + 
 +    # Base + broader font-src 
 +    CSP-webmin: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        base-uri 'self'; 
 +        img-src 'self' data:; 
 +        font-src 'self' *.smns-bw.org data:; 
 +        style-src 'self' 'unsafe-inline'; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 + 
 +    # Fixed: stray "data" token removed, keep single "data:" at end 
 +    CSP-collections: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'none'; 
 +        frame-ancestors 'self' *.smns-bw.org pictures.smns-bw.org; 
 +        frame-src *.smns-bw.org; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        style-src 'self' 'unsafe-inline' *.smns-bw.org https://cloud.ccm19.de; 
 +        connect-src 'self' https://cloud.ccm19.de https://matomo.naturkundemuseum-bw.de/; 
 +        font-src 'self' *.smns-bw.org; 
 +        img-src 'self' *.smns-bw.org https://a.tile.openstreetmap.org https://b.tile.openstreetmap.org https://c.tile.openstreetmap.org https://cloud.ccm19.de data:; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' https://pydeepzoom.smns-bw.org https://matomo.naturkundemuseum-bw.de https://cloud.ccm19.de; 
 + 
 +    # Fixed: removed invalid 'unsafe-inline' from connect-src and from script/style -elem 
 +    CSP-ent: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'none'; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        style-src-elem https://ent.smns-bw.org/static/css/; 
 +        connect-src 'self' https://matomo.naturkundemuseum-bw.de; 
 +        script-src-elem https://matomo.naturkundemuseum-bw.de https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js https://openseadragon.github.io/openseadragon/openseadragon.js; 
 +        img-src 'self' https://openseadragon.github.io/openseadragon/images/; 
 + 
 +    CSP-digiphyll: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; 
 +        frame-ancestors 'self' *.smns-bw.org; 
 +        frame-src *.smns-bw.org https://144.41.33.40/; 
 +        base-uri 'self'; 
 +        form-action 'self'; 
 +        img-src 'self' https://a.tile.openstreetmap.org https://b.tile.openstreetmap.org https://c.tile.openstreetmap.org https://unpkg.com/ data:; 
 +        connect-src 'self'; 
 +        font-src 'self' data:; 
 +        style-src 'self' 'unsafe-inline' data:; 
 +        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org https://mathjax.rstudio.com/latest/ http://144.41.33.40/ data:; 
 + 
 +    # Fixed: removed invalid 'unsafe-eval' from style-src 
 +    CSP-geometroidea: 
 +      headers: 
 +      contentSecurityPolicy:
 +        default-src 'self'; 
 +        style-src 'self' 'unsafe-inline' *.smns-bw.org https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css https://fonts.googleapis.com/css2; 
 +        font-src 'self' https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/webfonts/ https://fonts.gstatic.com/s/nunito/v25/; 
 +        img-src 'self' data:; 
 + 
 +    my-traefik-plugin-cookie-path-prefix: 
 +      plugin: 
 +        traefik-plugin-cookie-path-prefix: 
 +          prefix: smns 
 + 
 +    webportal-errors: 
 +      errors: 
 +        status: 
 +          service: error-pages-webportal@docker 
 +            - "404-503" 
 +          query: "/{status}.html" 
 + 
 +#    another-service-errors: 
 +#      errors: 
 +#        status: 
 +#          - "404-503" 
 +#        service: error-pages-another-service 
 +#        query: "/error-pages-another-service/{status}.html" 
 +</codedoc> 
 +\\ 
 +<codedoc toggle tls-opts.yml> 
 +tls: 
 +  options: 
 +    tls-opts: 
 +      minVersion: VersionTLS12 
 +      cipherSuites: 
 +        - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" 
 +        - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" 
 +        - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" 
 +        - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" 
 +        - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305" 
 +        - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305" 
 +      curvePreferences: 
 +        - CurveP521 
 +        - CurveP384 
 +#      sniStrict: true 
 +</codedoc> 
 + 
 +===== Traefik security headers and CSP (DRY config) ===== 
 + 
 +Last updated: 25.09.2025 
 + 
 +==== High‑level design ==== 
 + 
 +  * One baseline for security headers: security-headers-base 
 +  * Small header variants you can compose: -noindex, -allowframes, -hsts-subdomains, cache-public 
 +  * Separate CORS middlewares: cors-default, cors-collections 
 +  * One base CSP: CSP-base (+ per‑service CSP middlewares that only add what’s different) 
 +  * Chains compose the above into per‑service “one-liners” you reference from docker-compose labels. 
 + 
 +==== Security headers ==== 
 + 
 +=== Baseline (security-headers-base) === 
 + 
 +Sets the shared hardening and sane defaults. Highlights: 
 + 
 +  * addVaryHeader: true 
 +  * contentTypeNosniff: true 
 +  * browserXssFilter: true 
 +  * referrerPolicy: same-origin 
 +  * permissionsPolicy: fullscreen=(self "https://smns-bw.org"), geolocation=*, midi=(), camera=(), usb=(), magnetometer=(), accelerometer=(), vr=(), speaker=(), ambient-light-sensor=(), gyroscope=(), microphone=(), payment=() 
 +  * HSTS: forceSTSHeader: true, stsSeconds: 31536000, stsPreload: false 
 +  * X-Frame-Options: frameDeny: true by default (prefer CSP frame-ancestors for allowlists) 
 +  * TLS/proxy awareness: sslRedirect: true; sslProxyHeaders.X-Forwarded-Proto: https 
 +  * Scrub response headers (customResponseHeaders): Server: "", X-Powered-By: "", X-Robots-Tag: "index, follow" 
 +  * Upstream request header: customRequestHeaders.X-Forwarded-Proto: https 
 +Rationale: 
 + 
 +  * Default deny framing (clickjacking defense); enable per service via -allowframes and control who via CSP. 
 +  * Keep includeSubdomains off by default to avoid accidental HSTS lock-in; use the variant when needed. 
 + 
 +==== Variants you can compose ==== 
 + 
 +  * security-headers-noindex 
 +    * Same as base, but X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex" 
 +  * security-headers-allowframes 
 +    * Same as base, but frameDeny: false (use CSP frame-ancestors to specify who can frame) 
 +  * security-headers-hsts-subdomains 
 +    * Same as base, but stsIncludeSubdomains: true, stsSeconds: 63072000 
 +  * cache-public 
 +    * Adds Cache-Control: public to customResponseHeaders 
 + 
 +==== CORS middlewares ==== 
 + 
 +  * cors-default 
 +    * accessControlAllowMethods: GET, OPTIONS, PUT 
 +    * accessControlAllowHeaders: "*" 
 +    * accessControlMaxAge: 100 
 +    * addVaryHeader: true 
 +  * cors-collections 
 +    * Inherits cors-default + accessControlAllowOriginList: 
 +      * https://pydeepzoom.smns-bw.org 
 +      * https://pictures.smns-bw.org 
 + 
 +==== Content Security Policy (CSP) ==== 
 + 
 +=== Base CSP (CSP-base) === 
 + 
 +Applies to most services unchanged. 
 + 
 +<code yaml> CSP-base: headers: contentSecurityPolicy: &CSP_BASE > default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; frame-ancestors 'self' *.smns-bw.org; frame-src *.smns-bw.org; base-uri 'self'; form-action 'self'; img-src 'self' data:; connect-src 'self'; font-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org; </code> 
 + 
 +Notes: 
 + 
 +  * Only one CSP header is honored. Don’t stack multiple CSP middlewares in a single chain; “last header wins.” 
 +  * Use Report-Only during testing by duplicating to contentSecurityPolicyReportOnly. 
 + 
 +==== How to add a new service ==== 
 + 
 +  * If defaults are enough: 
 +    * Use secure-global chain. 
 +  * If it needs framing: 
 +    * Use security-headers-allowframes in the chain and set CSP frame-ancestors to the exact allowlist. 
 +  * If it needs special CORS: 
 +    * Add or create a cors-<service> middleware and include it in the chain. 
 +  * If it needs special CSP: 
 +    * Copy CSP-base to CSP-<service> and append only the minimal deltas (new hosts/tokens). 
 +  * Wire it in docker-compose: 
 +    * traefik.http.routers.<name>.middlewares=secure-<name>@file 
 +==== Verification (quick commands) ==== 
 + 
 +  * Check headers: 
 +    * curl -sI https://<host> | egrep -i "strict-transport-security|x-frame-options|x-content-type-options|referrer-policy|permissions-policy|x-robots-tag" 
 +    * Check CSP is present (and only once): 
 +    * curl -sI https://<host> | grep -i "content-security-policy" 
 +  * Check CORS (preflight example): 
 +    * curl -sI -X OPTIONS https://<host>/ -H "Origin: https://example.com" -H "Access-Control-Request-Method: GET" 
 +  * Check robots indexing: 
 +    * curl -sI https://<host>/ | grep -i x-robots-tag 
 + 
 +==== Operational notes ==== 
 + 
 +  * Prefer CSP frame-ancestors over X-Frame-Options for precise embedding control. 
 +  * Consider a Report-Only CSP during rollouts (duplicate middleware with contentSecurityPolicyReportOnly). 
 +  * HSTS includeSubdomains is opt-in via variant to avoid unintentional hard lock-in. 
 +  * If a page doesn’t render: check the browser console for CSP violations first; add only the specific host/type needed. 
 +==== Appendix: Anchor usage ==== 
 +We used YAML anchors to stay DRY: 
 + 
 +  * &SEC_BASE / *SEC_BASE for baseline headers. 
 +  * &CSP_BASE / *CSP_BASE for base CSP string. 
 +This keeps the source config compact while allowing targeted overrides.
  
 ===== Traefik v3 Healthcheck (Docker) ===== ===== Traefik v3 Healthcheck (Docker) =====
Zeile 257: Zeile 920:
   * [[https://docs.docker.com/reference/compose-file/services/#healthcheck|Docker Compose healthcheck docs]]   * [[https://docs.docker.com/reference/compose-file/services/#healthcheck|Docker Compose healthcheck docs]]
  
-Authored for SMNS IT by Chattie and AI Programmer — [[:server:date?media=server:date|]]+Authored for SMNS IT by Chattie and AI Programmer