# Traefik for the test swarm (M1). Runs as a swarm service on the `proxy` overlay so it can # reach recipe service VIPs (a host process couldn't). TLS terminates here using the operator's # pre-issued wildcard cert via the file provider — NO ACME for commoninternet.net (§4.0). # Recipe routers only need `traefik.enable=true` + a Host(...) rule + tls=true; the default # certificate (the wildcard) is served for every *.ci.commoninternet.net host. { pkgs, ... }: let # Static config. Docker *Swarm* provider (v3) + file provider for the cert. staticCfg = pkgs.writeText "traefik.yml" '' entryPoints: web: address: ":80" websecure: address: ":443" providers: swarm: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false network: proxy file: directory: /etc/traefik/dynamic watch: true log: level: INFO accessLog: {} api: dashboard: false ping: {} ''; # Dynamic config: serve the pre-issued wildcard as the DEFAULT certificate, so any # *.ci.commoninternet.net router with tls=true is covered without a cert resolver. certsCfg = pkgs.writeText "certs.yml" '' tls: stores: default: defaultCertificate: certFile: /var/lib/ci-certs/live/fullchain.pem keyFile: /var/lib/ci-certs/live/privkey.pem certificates: - certFile: /var/lib/ci-certs/live/fullchain.pem keyFile: /var/lib/ci-certs/live/privkey.pem ''; stack = pkgs.writeText "traefik-stack.yml" '' version: "3.8" services: traefik: image: traefik:v3.3 ports: - target: 80 published: 80 mode: host - target: 443 published: 443 mode: host volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - /var/lib/ci-certs/live:/var/lib/ci-certs/live:ro - ${staticCfg}:/etc/traefik/traefik.yml:ro - ${certsCfg}:/etc/traefik/dynamic/certs.yml:ro networks: - proxy deploy: mode: replicated replicas: 1 placement: constraints: - node.role == manager restart_policy: condition: any networks: proxy: external: true ''; in { # Gateway forwards 80/443 to cc-ci over the public interface (enp5s0), so open them. networking.firewall.allowedTCPPorts = [ 80 443 ]; systemd.services.traefik-deploy = { description = "Deploy the Traefik swarm stack"; after = [ "swarm-init.service" ]; requires = [ "swarm-init.service" ]; wantedBy = [ "multi-user.target" ]; path = [ pkgs.docker ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' set -eu docker stack deploy --detach=true -c ${stack} traefik ''; }; }