# ───────────────────────────────────────────────────────────────────────────── # Traefik DYNAMIC configuration (file provider) — 43-meditsina # Host path: /mnt/containers/traefik/container-data/dynamic/services.yml # Hot-reloaded on save (watch: true) — no container restart needed for edits here. # # Replaces 13 nginx-proxy-manager proxy hosts. Two are deliberately NOT migrated: # nginx.shcnw.ru — NPM's own admin UI; NPM is gone # nginxlogs.shcnw.ru — goaccess; it parses NPM's logs, and Exchange (99% of traffic) # moves to L4 where there is no HTTP access log. Retire it. # ───────────────────────────────────────────────────────────────────────────── tcp: # =========================================================================== # Exchange — SNI PASS-THROUGH (Layer 4). # TLS terminates ON Exchange (192.168.0.6), so connection-bound Windows auth # (NTLM / Negotiate) survives end-to-end → desktop Outlook can be configured # from outside. Also removes the ActiveSync `Cmd=Ping` 504s: IIS owns the # long-poll, so nginx's 60s proxy_read_timeout no longer truncates it. # # Exchange presents its own 24-SAN public LE cert (C178CFB8…, exp 2026-11-24); # Traefik must NOT terminate or manage a cert for these names. # # The 24 SNIs below are exactly that certificate's SAN list. Three of them # (mail/autodiscover.alisailina.com, autodiscover.neviol.ru) have no NPM vhost # today and currently fail the TLS handshake outright — including them here # fixes them at no cost. See the migration plan §5. # =========================================================================== routers: exchange: entryPoints: [websecure] # v3 REJECTS HostSNI(`a`,`b`) — they must be OR-ed. rule: >- HostSNI(`mail.shcnw.ru`) || HostSNI(`autodiscover.shcnw.ru`) || HostSNI(`mail.exprinta.ru`) || HostSNI(`autodiscover.exprinta.ru`) || HostSNI(`mail.inkam.navy`) || HostSNI(`autodiscover.inkam.navy`) || HostSNI(`mail.inkam.parts`) || HostSNI(`autodiscover.inkam.parts`) || HostSNI(`mail.nt.parts`) || HostSNI(`autodiscover.nt.parts`) || HostSNI(`mail.ankil.ltd`) || HostSNI(`autodiscover.ankil.ltd`) || HostSNI(`mail.pda.ae`) || HostSNI(`autodiscover.pda.ae`) || HostSNI(`mail.nordtextile.ru`) || HostSNI(`autodiscover.nordtextile.ru`) || HostSNI(`mail.setto.ru`) || HostSNI(`autodiscover.setto.ru`) || HostSNI(`mail.smartdtl.ru`) || HostSNI(`autodiscover.smartdtl.ru`) || HostSNI(`mail.neviol.ru`) || HostSNI(`autodiscover.neviol.ru`) || HostSNI(`mail.alisailina.com`) || HostSNI(`autodiscover.alisailina.com`) tls: passthrough: true service: exchange services: exchange: loadBalancer: servers: - address: "192.168.0.6:443" http: # =========================================================================== # Middlewares # =========================================================================== middlewares: lan-only: # = NPM "Access List: allow 192.168.0.0/22; deny all" ipAllowList: sourceRange: - "192.168.0.0/22" dash-auth: basicAuth: usersFile: /etc/traefik/users/dashboard.htpasswd redirect-https: redirectScheme: scheme: https permanent: true # Backends presenting self-signed certs (NPM forwarded with "verify SSL" off). serversTransports: insecure: insecureSkipVerify: true # =========================================================================== # Routers on :80 (web) # =========================================================================== routers: # --- Forward the ACME challenge for the Exchange names to Exchange itself. --- # This is what makes win-acme's HTTP-01 SelfHosting validation on SERVERMAIL work # WITHOUT the manual port-80 NAT repoint that every past renewal has needed. # REQUIRES the edge NAT for :80 to point at this docker host (192.168.0.9). # Priority must beat the catch-all redirect below. exchange-acme: entryPoints: [web] priority: 1000 rule: >- PathPrefix(`/.well-known/acme-challenge/`) && ( Host(`mail.shcnw.ru`) || Host(`autodiscover.shcnw.ru`) || Host(`mail.exprinta.ru`) || Host(`autodiscover.exprinta.ru`) || Host(`mail.inkam.navy`) || Host(`autodiscover.inkam.navy`) || Host(`mail.inkam.parts`) || Host(`autodiscover.inkam.parts`) || Host(`mail.nt.parts`) || Host(`autodiscover.nt.parts`) || Host(`mail.ankil.ltd`) || Host(`autodiscover.ankil.ltd`) || Host(`mail.pda.ae`) || Host(`autodiscover.pda.ae`) || Host(`mail.nordtextile.ru`) || Host(`autodiscover.nordtextile.ru`) || Host(`mail.setto.ru`) || Host(`autodiscover.setto.ru`) || Host(`mail.smartdtl.ru`) || Host(`autodiscover.smartdtl.ru`) || Host(`mail.neviol.ru`) || Host(`autodiscover.neviol.ru`) || Host(`mail.alisailina.com`) || Host(`autodiscover.alisailina.com`) ) service: exchange-http # --- Everything else on :80 redirects to https (replaces the entrypoint-level # redirect, which would outrank exchange-acme). Lowest priority. --- http-catchall: entryPoints: [web] priority: 1 rule: "PathPrefix(`/`)" middlewares: [redirect-https] service: noop # ========================================================================= # Routers on :443 (websecure) — L7, Traefik terminates and manages the cert # ========================================================================= crm: entryPoints: [websecure] rule: "Host(`crm.shcnw.ru`) || Host(`crm.inkam.navy`)" service: crm tls: { certResolver: le } onec: entryPoints: [websecure] rule: "Host(`1c.exprinta.ru`)" service: onec tls: { certResolver: le } # ---- LAN-only (internal admin) ---- start: entryPoints: [websecure] rule: "Host(`start.shcnw.ru`)" service: flame middlewares: [lan-only] tls: { certResolver: le } portainer: entryPoints: [websecure] rule: "Host(`portainer.shcnw.ru`)" service: portainer middlewares: [lan-only] tls: { certResolver: le } cockpit: entryPoints: [websecure] rule: "Host(`cockpit.shcnw.ru`)" service: cockpit middlewares: [lan-only] tls: { certResolver: le } # ---- Traefik dashboard (LAN-only + basic-auth) ---- # Needs a PUBLIC A-record even though access is LAN-only, because ACME validates # from the internet. traefik.shcnw.ru -> 217.15.22.194 created 2026-08-26. dashboard: entryPoints: [websecure] rule: "Host(`traefik.shcnw.ru`)" service: api@internal middlewares: [lan-only, dash-auth] tls: { certResolver: le } # =========================================================================== # Services (backends) — mirror the NPM proxy-host forward targets # =========================================================================== services: exchange-http: # :80 ACME challenge forward only (see exchange-acme) loadBalancer: servers: [{ url: "http://192.168.0.6:80" }] crm: # Bitrix — self-signed cert (CN=Bitrix, 2023→2033) loadBalancer: serversTransport: insecure servers: [{ url: "https://192.168.0.10:443" }] onec: # Apache/2.4.23 (Win64), plain HTTP loadBalancer: servers: [{ url: "http://192.168.0.4:80" }] flame: # sibling container on reverseproxy-nw loadBalancer: servers: [{ url: "http://flame:5005" }] portainer: loadBalancer: serversTransport: insecure servers: [{ url: "https://192.168.0.9:9443" }] cockpit: loadBalancer: serversTransport: insecure servers: [{ url: "https://192.168.0.9:9090" }] noop: # never reached — http-catchall always redirects first loadBalancer: servers: [{ url: "http://127.0.0.1:1" }]