{ config, lib, namespace, ... }: with lib; let cfg = config.${namespace}.services.traefik; # Process reverseProxies into service and router configurations reverseProxyServiceConfigs = let makeService = reverseProxy: nameValuePair reverseProxy.service.name reverseProxy.service.config; in listToAttrs (map makeService cfg.reverseProxies); reverseProxyRouterConfigs = let makeRouter = reverseProxy: nameValuePair reverseProxy.router.subdomain reverseProxy.router.config; in listToAttrs (map makeRouter cfg.reverseProxies); domain = "mjallen.dev"; # Forward services authUrl = "http://localhost:9000/outpost.goauthentik.io"; cacheUrl = "http://localhost:9012"; hassUrl = "http://nuc-nixos.local:8123"; # Plugins traefikPlugins = { bouncer = { moduleName = "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"; version = "v1.4.5"; }; geoblock = { moduleName = "github.com/PascalMinder/geoblock"; version = "v0.2.5"; }; }; # Ports httpPort = 80; httpsPort = 443; traefikPort = 8080; metricsPort = 8082; forwardPorts = [ httpPort httpsPort traefikPort metricsPort ]; # misc letsEncryptEmail = "jalle008@proton.me"; configDir = "/media/nas/main/appdata"; in { imports = [ ./options.nix ]; config = mkIf cfg.enable { sops = { secrets = { "jallen-nas/traefik/crowdsec/lapi-key" = { sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); owner = config.users.users.traefik.name; group = config.users.users.traefik.group; restartUnits = [ "traefik.service" ]; }; "jallen-nas/traefik/crowdsec/capi-machine-id" = { sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); owner = config.users.users.traefik.name; group = config.users.users.traefik.group; restartUnits = [ "traefik.service" ]; }; "jallen-nas/traefik/crowdsec/capi-password" = { sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); owner = config.users.users.traefik.name; group = config.users.users.traefik.group; restartUnits = [ "traefik.service" ]; }; "jallen-nas/traefik/cloudflare-dns-api-token" = { sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); owner = config.users.users.traefik.name; group = config.users.users.traefik.group; restartUnits = [ "traefik.service" ]; }; "jallen-nas/traefik/cloudflare-zone-api-token" = { sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); owner = config.users.users.traefik.name; group = config.users.users.traefik.group; restartUnits = [ "traefik.service" ]; }; "jallen-nas/traefik/cloudflare-api-key" = { sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); owner = config.users.users.traefik.name; group = config.users.users.traefik.group; restartUnits = [ "traefik.service" ]; }; "jallen-nas/traefik/cloudflare-email" = { sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); owner = config.users.users.traefik.name; group = config.users.users.traefik.group; restartUnits = [ "traefik.service" ]; }; }; templates = { "traefik.env" = { content = '' CLOUDFLARE_DNS_API_TOKEN=${config.sops.placeholder."jallen-nas/traefik/cloudflare-dns-api-token"} CLOUDFLARE_ZONE_API_TOKEN=${config.sops.placeholder."jallen-nas/traefik/cloudflare-zone-api-token"} CLOUDFLARE_API_KEY=${config.sops.placeholder."jallen-nas/traefik/cloudflare-api-key"} CLOUDFLARE_EMAIL=${config.sops.placeholder."jallen-nas/traefik/cloudflare-email"} ''; owner = config.users.users.traefik.name; group = config.users.users.traefik.group; restartUnits = [ "traefik.service" ]; }; }; }; networking.firewall = { allowedTCPPorts = forwardPorts; allowedUDPPorts = forwardPorts; }; services.traefik = { enable = true; dataDir = "${configDir}/traefik"; dynamic.dir = "${configDir}/traefik"; group = "jallen-nas"; # group; environmentFiles = [ config.sops.templates."traefik.env".path ]; staticConfigOptions = { entryPoints = { web = { address = ":${toString httpPort}"; asDefault = true; http.redirections.entrypoint = { to = "websecure"; scheme = "https"; }; }; websecure = { address = ":${toString httpsPort}"; asDefault = true; http.tls.certResolver = "letsencrypt"; }; metrics = { address = ":${toString metricsPort}"; # Port for metrics }; }; log = { level = "INFO"; }; metrics = { prometheus = { entryPoint = "metrics"; addEntryPointsLabels = true; addServicesLabels = true; buckets = [ 0.1 0.3 1.2 5.0 ]; # Response time buckets }; }; certificatesResolvers.letsencrypt.acme = { email = letsEncryptEmail; storage = "${config.services.traefik.dataDir}/acme.json"; dnsChallenge = { provider = "cloudflare"; resolvers = [ "1.1.1.1:53" "8.8.8.8:53" ]; }; }; # Access the Traefik dashboard on :8080 api = { dashboard = true; insecure = true; }; experimental = { plugins = traefikPlugins; }; }; dynamicConfigOptions = { http = { serversTransports = { internal-https = { insecureSkipVerify = true; }; http1 = { serverName = "localhost"; disableHTTP2 = true; }; }; middlewares = { authentik = { forwardAuth = { tls.insecureSkipVerify = true; address = "${authUrl}/auth/traefik"; trustForwardHeader = true; authResponseHeaders = [ "X-authentik-username" "X-authentik-groups" "X-authentik-email" "X-authentik-name" "X-authentik-uid" "X-authentik-jwt" "X-authentik-meta-jwks" "X-authentik-meta-outpost" "X-authentik-meta-provider" "X-authentik-meta-app" "X-authentik-meta-version" ]; }; }; crowdsec = { plugin = { bouncer = { enabled = true; crowdsecLapiKeyFile = config.sops.secrets."jallen-nas/traefik/crowdsec/lapi-key".path; crowdsecLapiScheme = "http"; crowdsecLapiHost = "localhost:8181"; crowdsecLapiPath = "/"; crowdsecLapiTLSInsecureVerify = false; crowdsecCapiMachineIdFile = config.sops.secrets."jallen-nas/traefik/crowdsec/capi-machine-id".path; crowdsecCapiPasswordFile = config.sops.secrets."jallen-nas/traefik/crowdsec/capi-password".path; crowdsecCapiScenarios = [ ]; }; }; }; whitelist-geoblock = { plugin = { geoblock = { silentStartUp = false; allowLocalRequests = true; logLocalRequests = false; logAllowedRequests = false; logApiRequests = false; api = "https://get.geojs.io/v1/ip/country/{ip}"; apiTimeoutMs = 500; cacheSize = 25; forceMonthlyUpdate = true; allowUnknownCountries = false; unknownCountryApiResponse = "nil"; blackListMode = false; countries = [ "CA" "US" ]; }; }; }; internal-ipallowlist = { ipAllowList = { sourceRange = [ "127.0.0.1/32" "10.0.1.0/24" ]; }; }; }; services = { auth.loadBalancer.servers = [ { url = authUrl; } ]; cache.loadBalancer = { servers = [ { url = cacheUrl; } ]; serversTransport = "http1"; }; hass.loadBalancer.servers = [ { url = hassUrl; } ]; nginx.loadBalancer.servers = [ { url = "http://localhost:8188"; } ]; } // reverseProxyServiceConfigs; routers = { auth = { entryPoints = [ "websecure" ]; rule = "HostRegexp(`{subdomain:[a-z]+}.mjallen.dev`) && PathPrefix(`/outpost.goauthentik.io/`)"; service = "auth"; middlewares = [ "crowdsec" "whitelist-geoblock" ]; priority = 15; tls.certResolver = "letsencrypt"; }; matrix2 = { entryPoints = [ "websecure" ]; rule = "Host(`matrix.mjallen.dev`) && PathPrefix(`/.well-known/matrix/`)"; service = "nginx"; middlewares = [ "crowdsec" "whitelist-geoblock" ]; priority = 1; tls.certResolver = "letsencrypt"; }; matrix3 = { entryPoints = [ "websecure" ]; rule = "Host(`mjallen.dev`) && PathPrefix(`/.well-known/matrix/`)"; service = "nginx"; middlewares = [ "crowdsec" "whitelist-geoblock" ]; priority = 1; tls.certResolver = "letsencrypt"; }; cache = { entryPoints = [ "websecure" ]; rule = "Host(`cache.${domain}`)"; service = "cache"; middlewares = [ ]; priority = 10; tls.certResolver = "letsencrypt"; }; hass = { entryPoints = [ "websecure" ]; rule = "Host(`hass.${domain}`)"; service = "hass"; middlewares = [ "crowdsec" "whitelist-geoblock" # "authentik" ]; priority = 10; tls.certResolver = "letsencrypt"; }; } // reverseProxyRouterConfigs; }; }; }; }; }