{ pkgs, lib, config, ... }: let cfg = config.xyno.services.traefik; simpleProxyOpts = lib.mapAttrsToList ( n: v: let router = "simpleproxy-${n}-router"; service = "simpleproxy-${n}-service"; spl = lib.splitString "." v.host; certDomain = if (builtins.length spl) > 2 then lib.concatStringsSep "." (builtins.tail spl) else spl; in { routers."${router}-robotstxt" = { service = "robotstxt"; rule = "Host(`${v.host}`) && Path(`/robots.txt`)"; tls.certResolver = "letsencrypt"; tls.domains = [ { main = certDomain; sans = [ "*.${certDomain}" ]; } ]; }; routers.${router} = { inherit service; rule = "Host(`${v.host}`)"; tls.certResolver = "letsencrypt"; tls.domains = [ { main = certDomain; sans = [ "*.${certDomain}" ]; } ]; }; services.${service} = { loadBalancer.servers = [ { url = v.internal; } ]; loadBalancer.serverTransport = lib.mkIf (v.transport != null) v.transport; }; services.robotstxt = { loadBalancer.servers = [ { url = "http://127.0.0.2:8080"; } ]; }; } ) cfg.simpleProxy; in { options.xyno.services.traefik.enable = lib.mkEnableOption "enables traefik"; options.xyno.services.traefik.noBots = lib.mkOption { type = lib.types.bool; default = true; }; options.xyno.services.traefik.simpleProxy = lib.mkOption { example = { "example" = { host = "example.org"; middlewares = [ "meow" ]; internal = "http://127.0.0.1:8080"; }; }; default = { }; type = lib.types.attrsOf ( lib.types.submodule { options = { middlewares = lib.mkOption { type = lib.types.nullOr (lib.types.listOf lib.types.str); }; internal = lib.mkOption { type = lib.types.str; }; host = lib.mkOption { type = lib.types.str; }; transport = lib.mkOption { type = lib.types.nullOr lib.types.anything; default = null; }; }; } ); }; config = lib.mkIf cfg.enable { services.nginx = { enable = lib.mkIf cfg.noBots true; defaultListen = lib.mkIf cfg.noBots [ { addr = "127.0.0.2"; port = 8080; } ]; virtualHosts._.default = true; virtualHosts._.locations."/".root = pkgs.writeTextFile { name = "robots.txt"; destination = "/robots.txt"; text = '' User-agent: * Disallow: / ''; }; }; services.traefik = { enable = true; environmentFiles = [ config.sops.templates."traefik.env".path ]; staticConfigOptions = { metrics = lib.mkIf config.xyno.services.monitoring.enable { otlp.http.endpoint = "http://localhost:8429/v1/metrics"; }; entryponits.web = { address = ":80"; redirections.entryPoint = { to = "websecure"; scheme = "https"; permanent = true; }; }; entrypoints.websecure = { address = ":443"; http.tls.certResolver = "letsencrypt"; http3 = { }; }; log.level = "INFO"; certificatesResolvers.letsencrypt.acme = { email = "ssl@xyno.systems"; caServer = "https://acme-v02.api.letsencrypt.org/directory"; dnsChallenge = { resolvers = [ "8.8.8.8" "1.1.1.1" ]; provider = "desec"; }; }; }; dynamicConfigOptions = { http = lib.mkMerge simpleProxyOpts; # tls.options.default = { # # mozilla modern # minVersion = "VersionTLS13"; # curvePreferences = [ # "X25519" # "CurveP256" # "CurveP384" # ]; # }; # tls.options.old = { # # mozilla intermediate # minVersion = "VersionTLS12"; # curvePreferences = [ # "X25519" # "CurveP256" # "CurveP384" # ]; # cipherSuites = [ # "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" # "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" # "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" # "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" # "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305" # "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305" # ]; # }; }; }; networking.firewall.allowedTCPPorts = [ 80 443 ]; networking.firewall.allowedUDPPorts = [ 443 ]; xyno.impermanence.directories = [ config.services.traefik.dataDir ]; sops.secrets."desec_token" = { }; sops.templates."traefik.env".content = '' DESEC_TOKEN=${config.sops.placeholder.desec_token} DESEC_PROPAGATION_TIMEOUT=1200 LEGO_DISABLE_CNAME_SUPPORT=true ''; sops.templates."traefik.env".reloadUnits = [ "traefik.service" ]; # services.borgmatic.settings.traefikql_databases = [ # { # name = "all"; # gets run as root anyways so can log in # } # ]; }; }