{ name = "Traefik"; description = '' Runs the Traefik reverse proxy. ''; nixosModule = { lib, pkgs, config, hosts, ... }: with lib; { options.traefik = { wildcardDomains = mkOption { type = types.listOf types.str; default = [ ]; }; }; config = let cfg = config.traefik; routes = concatMap ( hostname: concatMap ( role: role.traefikRoutes { inherit hostname; config = hosts.${hostname}.config; } ) hosts.${hostname}.roles ) (builtins.attrNames hosts); in { networking.firewall.allowedTCPPorts = [ 80 443 ]; sops.secrets = { "traefik/acmeEmail" = { owner = "traefik"; }; "traefik/CLOUDFLARE_EMAIL" = { owner = "traefik"; }; "traefik/CLOUDFLARE_DNS_API_TOKEN" = { owner = "traefik"; }; }; sops.templates."traefik.env" = { owner = "traefik"; content = '' acmeEmail="${config.sops.placeholder."traefik/acmeEmail"}" CLOUDFLARE_EMAIL="${config.sops.placeholder."traefik/CLOUDFLARE_EMAIL"}" CLOUDFLARE_DNS_API_TOKEN="${config.sops.placeholder."traefik/CLOUDFLARE_DNS_API_TOKEN"}" ''; }; services.traefik = { enable = true; environmentFiles = [ config.sops.templates."traefik.env".path ]; staticConfigOptions = { entryPoints = { web = { address = ":80"; http = { redirections = { entryPoint = { to = "websecure"; scheme = "https"; }; }; }; }; websecure = { address = ":443"; http.tls = { certResolver = "letsencrypt"; domains = map (domain: { main = domain; sans = [ "*.${domain}" ]; }) cfg.wildcardDomains; }; }; }; certificatesResolvers = { letsencrypt = { acme = { email = "$acmeEmail"; storage = "/run/traefik/acme.json"; dnsChallenge = { provider = "cloudflare"; }; }; }; }; http = { routers = listToAttrs ( map (route: { name = route.name; value = { entrypoints = [ "websecure" ]; service = route.name; rule = route.rule; }; }) routes ); services = listToAttrs ( map (route: { name = route.name; value.loadBalancer.servers.url = [ route.target ]; }) routes ); }; }; }; }; }; }