initial traefik layout
parent
57725520e4
commit
6e3b73f822
|
|
@ -0,0 +1,73 @@
|
||||||
|
# traefik — ОСК reverse proxy / SNI router
|
||||||
|
|
||||||
|
Replaces **nginx-proxy-manager** on serverweb. One `:443` entrypoint that does
|
||||||
|
**both**:
|
||||||
|
|
||||||
|
- **TLS pass-through (Layer 4)** for the Windows services that use Windows-Integrated
|
||||||
|
auth — TLS terminates at the backend, native Kerberos/NTLM/Negotiate is preserved
|
||||||
|
(NPM/openresty cannot do this):
|
||||||
|
- `workfolders.osk.team` → serverfile `192.168.0.11:443` (Work Folders)
|
||||||
|
- `mail.osk.team`, `autodiscover.osk.team` → servermail `192.168.0.6:443` (Exchange 2019)
|
||||||
|
- `kdcproxy.osk.team` → serverwsus `192.168.0.7:443` (KDC proxy)
|
||||||
|
- **TLS termination + Let's Encrypt** for the web dashboards:
|
||||||
|
- `start.osk.team` (flame), `portainer.osk.team`, `traefik.osk.team` (dashboard)
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `docker-compose.yaml` | Traefik v3 service, binds 80/443, mounts config + acme |
|
||||||
|
| `traefik.yml` | static config (entrypoints, providers, ACME) |
|
||||||
|
| `dynamic/passthrough.yml` | the TCP SNI-passthrough routers → Windows hosts |
|
||||||
|
| `dynamic/web.yml` | HTTP routers (dashboards) + dashboard basic-auth |
|
||||||
|
|
||||||
|
Routing config lives in git and redeploys on push (file provider, `watch: true`).
|
||||||
|
`acme.json` is **not** in git — it holds private keys and persists on the host.
|
||||||
|
|
||||||
|
## One-time host prep (serverweb)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /mnt/containers/traefik/container-data/acme
|
||||||
|
touch /mnt/containers/traefik/container-data/acme/acme.json
|
||||||
|
chmod 600 /mnt/containers/traefik/container-data/acme/acme.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Before deploying — edit
|
||||||
|
|
||||||
|
1. `traefik.yml`: set the real ACME `email`.
|
||||||
|
2. `dynamic/web.yml`: set a real `dash-auth` hash (`htpasswd -nbB admin 'pass'`),
|
||||||
|
and confirm the portainer backend (add portainer to `reverseproxy-nw`, or point
|
||||||
|
at `https://192.168.0.8:9443`).
|
||||||
|
|
||||||
|
## Deploy (Portainer GitOps)
|
||||||
|
|
||||||
|
- Decommission NPM first (it owns 80/443): stop/remove the `nginx-proxy-manager`
|
||||||
|
stack. **Do not** delete its data yet (rollback insurance).
|
||||||
|
- Add a new Portainer **Git** stack, compose path `traefik/docker-compose.yaml`,
|
||||||
|
same repo. Deploy.
|
||||||
|
- Test while the edge still points 443 → serverfile: from inside the LAN, hit each
|
||||||
|
SNI name against serverweb and confirm routing/cert.
|
||||||
|
- **Flip the edge** last:
|
||||||
|
- `TCP/80 → serverweb 192.168.0.8` (ACME http-01 + OWA http→https)
|
||||||
|
- `TCP/443 → serverweb 192.168.0.8` (was → serverfile)
|
||||||
|
- `UDP/443 → serverfile 192.168.0.11` (SMB over QUIC — unchanged, bypasses Traefik)
|
||||||
|
|
||||||
|
## Recommended consolidation
|
||||||
|
|
||||||
|
Once Traefik fronts 443, **retire the `kdcproxy` `:8443` hack**: route
|
||||||
|
`kdcproxy.osk.team` through Traefik on 443 (passthrough above) and revert the
|
||||||
|
client GPO `KdcProxy\ProxyServers\CORP.LOCAL` back to `<https kdcproxy.osk.team />`
|
||||||
|
(no port). Then the edge `TCP/8443` forward can be removed.
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
- **SNI required.** Passthrough routes only by SNI; any client not sending SNI
|
||||||
|
can't be routed (all modern Outlook/ActiveSync/Work Folders do send it).
|
||||||
|
- **Passthrough backends keep their own certs.** Traefik's LE only covers the
|
||||||
|
dashboards it terminates — **serverfile's Work Folders LE cert still renews on
|
||||||
|
serverfile** (the Sept-3 expiry is unchanged), and Exchange/KDC-proxy keep theirs.
|
||||||
|
- **Exchange cert** on servermail must cover `mail.osk.team` + `autodiscover.osk.team`
|
||||||
|
(and any other published names).
|
||||||
|
- **QUIC** stays a direct edge forward to serverfile; Traefik does not touch UDP/443.
|
||||||
|
- The dashboards' LE certs need public `:80` reachability for http-01. If they are
|
||||||
|
internal-only, use a DNS-01 challenge or a default cert instead.
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
services:
|
||||||
|
traefik:
|
||||||
|
image: traefik:v3
|
||||||
|
container_name: traefik
|
||||||
|
restart: always
|
||||||
|
# Traefik replaces nginx-proxy-manager on 80/443. Decommission NPM before
|
||||||
|
# deploying this (both can't bind 80/443). UDP/443 (SMB-over-QUIC) is NOT
|
||||||
|
# handled here — the router forwards UDP/443 straight to serverfile.
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
- ./traefik.yml:/etc/traefik/traefik.yml:ro,z
|
||||||
|
- ./dynamic:/etc/traefik/dynamic:ro,z
|
||||||
|
- /mnt/containers/traefik/container-data/acme:/acme:z
|
||||||
|
networks:
|
||||||
|
- reverseproxy-nw
|
||||||
|
|
||||||
|
networks:
|
||||||
|
reverseproxy-nw:
|
||||||
|
external: true
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
# SNI TLS pass-through (Layer 4) to the Windows backends.
|
||||||
|
#
|
||||||
|
# Traefik does NOT terminate TLS for these — it reads the SNI from the TLS
|
||||||
|
# ClientHello and forwards the raw connection to the backend, which terminates
|
||||||
|
# TLS itself and performs its own NATIVE auth (Kerberos / NTLM / Negotiate).
|
||||||
|
# This is the whole point: it preserves Windows-Integrated auth that an
|
||||||
|
# HTTP-terminating proxy (NPM/openresty) breaks.
|
||||||
|
#
|
||||||
|
# Requirements:
|
||||||
|
# - Clients MUST send SNI (all modern Outlook / ActiveSync / Work Folders do).
|
||||||
|
# - Each backend must present a cert valid for its own hostname(s):
|
||||||
|
# serverfile -> files.osk.team + workfolders.osk.team (LE, 9b279156…)
|
||||||
|
# servermail -> mail.osk.team + autodiscover.osk.team (Exchange cert)
|
||||||
|
# serverwsus -> kdcproxy.osk.team (self-signed, 02B9ADB3…)
|
||||||
|
|
||||||
|
tcp:
|
||||||
|
routers:
|
||||||
|
workfolders:
|
||||||
|
entryPoints: ["websecure"]
|
||||||
|
rule: "HostSNI(`workfolders.osk.team`)"
|
||||||
|
tls:
|
||||||
|
passthrough: true
|
||||||
|
service: serverfile-wf
|
||||||
|
|
||||||
|
exchange:
|
||||||
|
entryPoints: ["websecure"]
|
||||||
|
rule: "HostSNI(`mail.osk.team`, `autodiscover.osk.team`)"
|
||||||
|
tls:
|
||||||
|
passthrough: true
|
||||||
|
service: servermail-ex
|
||||||
|
|
||||||
|
kdcproxy:
|
||||||
|
entryPoints: ["websecure"]
|
||||||
|
rule: "HostSNI(`kdcproxy.osk.team`)"
|
||||||
|
tls:
|
||||||
|
passthrough: true
|
||||||
|
service: serverwsus-kdc
|
||||||
|
|
||||||
|
services:
|
||||||
|
serverfile-wf:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- address: "192.168.0.11:443" # serverfile — Work Folders
|
||||||
|
servermail-ex:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- address: "192.168.0.6:443" # servermail — Exchange 2019
|
||||||
|
serverwsus-kdc:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- address: "192.168.0.7:443" # serverwsus — KDC proxy
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
# HTTP routers for the web dashboards — Traefik TERMINATES TLS here and
|
||||||
|
# auto-issues Let's Encrypt certs (resolver "le"). These hosts have no
|
||||||
|
# Windows-Integrated auth, so termination is fine.
|
||||||
|
#
|
||||||
|
# ACME http-01 requires each Host below to be publicly resolvable and reachable
|
||||||
|
# on :80 through the edge. For internal-only dashboards, use a dnsChallenge or a
|
||||||
|
# default cert instead (see traefik.yml).
|
||||||
|
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
flame:
|
||||||
|
entryPoints: ["websecure"]
|
||||||
|
rule: "Host(`start.osk.team`)"
|
||||||
|
service: flame
|
||||||
|
tls:
|
||||||
|
certResolver: le
|
||||||
|
|
||||||
|
portainer:
|
||||||
|
entryPoints: ["websecure"]
|
||||||
|
rule: "Host(`portainer.osk.team`)"
|
||||||
|
service: portainer
|
||||||
|
tls:
|
||||||
|
certResolver: le
|
||||||
|
|
||||||
|
traefik-dashboard:
|
||||||
|
entryPoints: ["websecure"]
|
||||||
|
rule: "Host(`traefik.osk.team`)"
|
||||||
|
service: api@internal
|
||||||
|
middlewares: ["dash-auth"]
|
||||||
|
tls:
|
||||||
|
certResolver: le
|
||||||
|
|
||||||
|
services:
|
||||||
|
flame:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://flame:5005"
|
||||||
|
portainer:
|
||||||
|
# Portainer must share a network with Traefik. Either add the portainer
|
||||||
|
# container to reverseproxy-nw, or point this at the host IP instead:
|
||||||
|
# - url: "https://192.168.0.8:9443" (+ serversTransport insecureSkipVerify)
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "https://192.168.0.8:9443"
|
||||||
|
|
||||||
|
middlewares:
|
||||||
|
dash-auth:
|
||||||
|
basicAuth:
|
||||||
|
# Generate: htpasswd -nbB admin 'yourpassword' (escape $ as $$ only in
|
||||||
|
# docker-compose labels — in this YAML file use the raw single-$ hash).
|
||||||
|
users:
|
||||||
|
- "admin:$2y$05$HjhBPjFYOxYTWS37DScedenZRiRZ.qbxMsf10XQVujzCljE9VbQfG"
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Traefik v3 static configuration (ОСК reverse proxy / SNI router)
|
||||||
|
# Mounted read-only at /etc/traefik/traefik.yml
|
||||||
|
|
||||||
|
global:
|
||||||
|
checkNewVersion: false
|
||||||
|
sendAnonymousUsage: false
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: INFO
|
||||||
|
accessLog: {}
|
||||||
|
|
||||||
|
api:
|
||||||
|
dashboard: true # exposed via dynamic/web.yml router (traefik.osk.team) with basic-auth
|
||||||
|
|
||||||
|
entryPoints:
|
||||||
|
web:
|
||||||
|
address: ":80"
|
||||||
|
http:
|
||||||
|
redirections:
|
||||||
|
entryPoint:
|
||||||
|
to: websecure
|
||||||
|
scheme: https
|
||||||
|
websecure:
|
||||||
|
address: ":443"
|
||||||
|
# NOTE: TLS-passthrough TCP routers and TLS-terminating HTTP routers coexist
|
||||||
|
# on :443 — Traefik matches specific HostSNI(...) TCP routers first, and
|
||||||
|
# everything else falls through to the HTTP routers.
|
||||||
|
|
||||||
|
providers:
|
||||||
|
# Docker labels (for local containers that opt in with traefik.enable=true)
|
||||||
|
docker:
|
||||||
|
exposedByDefault: false
|
||||||
|
network: reverseproxy-nw
|
||||||
|
# File provider = all the static routing (SNI passthrough + dashboards)
|
||||||
|
file:
|
||||||
|
directory: /etc/traefik/dynamic
|
||||||
|
watch: true
|
||||||
|
|
||||||
|
# Let's Encrypt — only for hosts Traefik TERMINATES (dashboards).
|
||||||
|
# Passthrough hosts (workfolders/mail/kdcproxy) keep their own backend certs.
|
||||||
|
certificatesResolvers:
|
||||||
|
le:
|
||||||
|
acme:
|
||||||
|
email: gamroot@osk.team # <-- CHANGE to a real address
|
||||||
|
storage: /acme/acme.json
|
||||||
|
httpChallenge:
|
||||||
|
entryPoint: web
|
||||||
|
# For internal-only dashboards not reachable on :80 from the internet,
|
||||||
|
# switch to a dnsChallenge or a default self-signed cert instead.
|
||||||
Loading…
Reference in New Issue