{ 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 { sops.secrets = { "traefik.acmeEmail" = { owner = "traefik"; format = "dotenv"; }; "traefik.CLOUDFLARE_EMAIL" = { owner = "traefik"; format = "dotenv"; }; "traefik.CLOUDFLARE_DNS_API_TOKEN" = { owner = "traefik"; format = "dotenv"; }; }; services.traefik = { enable = true; environmentFiles = [ config.sops.secrets."traefik.acmeEmail".path config.sops.secrets."traefik.CLOUDFLARE_EMAIL".path config.sops.secrets."traefik.CLOUDFLARE_DNS_API_TOKEN".path ]; staticConfiguration = { entryPoints = { web = { address = ":80"; http = { redirections = { entryPoint = { to = "websecure"; scheme = "https"; }; }; }; }; websecure = { address = ":443"; tls = { certResolver = "letsencrypt"; domains = mkList ( map (domain: { main = domain; sans = [ "*.${domain}" ]; }) cfg.wildcardDomains ); }; }; }; certificatesResolvers = { letsencrypt = { acme = { email = "$acmeEmail"; storage = "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 ); }; }; }; }; }; }