diff --git a/flake.lock b/flake.lock index 8073e81f..7745de73 100644 --- a/flake.lock +++ b/flake.lock @@ -153,11 +153,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1759235653, - "narHash": "sha256-sKFehUxXCzM6E1LcmnRa/O6HKsRI/TGtciG5ulAJt08=", + "lastModified": 1759348172, + "narHash": "sha256-ZPUJX2ZA0ndcHndIA/S/nRESIJV0rifPr91SUpzJtEM=", "owner": "chaotic-cx", "repo": "nyx", - "rev": "2bf7f138e42fa8b2133761edab64263505cb83bf", + "rev": "dd1af56ad79c965ee20c236ba6adbb2135ac02af", "type": "github" }, "original": { @@ -212,11 +212,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1759301100, - "narHash": "sha256-hmiTEoVAqLnn80UkreCNunnRKPucKvcg5T4/CELEtbw=", + "lastModified": 1759473677, + "narHash": "sha256-9AFDP5AhXe06aXXPsV7bDGH7KA9Wo4uUtK7pRrsxuFQ=", "owner": "nix-community", "repo": "fenix", - "rev": "0956bc5d1df2ea800010172c6bc4470d9a22cb81", + "rev": "8532b07ced6a95d57650124c79534a3c770431ab", "type": "github" }, "original": { @@ -332,11 +332,11 @@ ] }, "locked": { - "lastModified": 1756770412, - "narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=", + "lastModified": 1759362264, + "narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "4524271976b625a4a605beefd893f270620fd751", + "rev": "758cf7296bee11f1706a574c77d072b8a7baa881", "type": "github" }, "original": { @@ -392,11 +392,11 @@ ] }, "locked": { - "lastModified": 1756770412, - "narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=", + "lastModified": 1759362264, + "narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "4524271976b625a4a605beefd893f270620fd751", + "rev": "758cf7296bee11f1706a574c77d072b8a7baa881", "type": "github" }, "original": { @@ -610,11 +610,11 @@ ] }, "locked": { - "lastModified": 1759331616, - "narHash": "sha256-LVpodobJvJM5rmfh2sFBHPNX0PYpNbbHzx/gprlKGGg=", + "lastModified": 1759337100, + "narHash": "sha256-CcT3QvZ74NGfM+lSOILcCEeU+SnqXRvl1XCRHenZ0Us=", "owner": "nix-community", "repo": "home-manager", - "rev": "5890176f856dcaf55f3ab56b25d4138657531cbd", + "rev": "004753ae6b04c4b18aa07192c1106800aaacf6c3", "type": "github" }, "original": { @@ -652,11 +652,11 @@ ] }, "locked": { - "lastModified": 1759172751, - "narHash": "sha256-E8W8sRXfrvkFW26GuuiWq6QfReU7m5+cngwHuRo/3jc=", + "lastModified": 1759261733, + "narHash": "sha256-G104PUPKBgJmcu4NWs0LUaPpSOTD4jiq4mamLWu3Oc0=", "owner": "nix-community", "repo": "home-manager", - "rev": "12fa8548feefa9a10266ba65152fd1a787cdde8f", + "rev": "5a21f4819ee1be645f46d6b255d49f4271ef6723", "type": "github" }, "original": { @@ -803,11 +803,11 @@ "xdph": "xdph" }, "locked": { - "lastModified": 1759318697, - "narHash": "sha256-iCL/F+rlgzgBfG4QURfjBrxVBMPsXCzZKHXn1SNBshc=", + "lastModified": 1759399554, + "narHash": "sha256-FsFugHj7He5siEcmoRUdMKHB8uMzyneK/fynPS57W4E=", "owner": "hyprwm", "repo": "hyprland", - "rev": "e0c96276df75accc853a30186ae5de580b2c725f", + "rev": "3bcfa94ee4189faaa4daf661949e88cf28c00d94", "type": "github" }, "original": { @@ -1006,11 +1006,11 @@ ] }, "locked": { - "lastModified": 1758124489, - "narHash": "sha256-YiVF/8Me3vVKJBEgGpQhn0HF09EWfXZGaWLzAaJBrO4=", + "lastModified": 1759492718, + "narHash": "sha256-Mxi/LyyHE9VKUnBs4y1hXO+wRqukZJjbx/igqKQxkQk=", "owner": "hyprwm", "repo": "hyprlock", - "rev": "7f769fa993cb492982d7bf25676c68ddbcc0268e", + "rev": "3cb799b1842016c85cca2db66fa502b8179cf0fe", "type": "github" }, "original": { @@ -1191,11 +1191,11 @@ ] }, "locked": { - "lastModified": 1757230583, - "narHash": "sha256-4uqu7sFPOaVTCogsxaGMgbzZ2vK40GVGMfUmrvK3/LY=", + "lastModified": 1759217228, + "narHash": "sha256-P13ExJlhMVkrc5LxZLNkIJZhjNYo3LLXnxDsUNrdnMQ=", "owner": "Jovian-Experiments", "repo": "Jovian-NixOS", - "rev": "fc3960e6c32c9d4f95fff2ef84444284d24d3bea", + "rev": "e52c15ab25f7dc68dde527c8df5bfa9d80d8e64f", "type": "github" }, "original": { @@ -1229,11 +1229,11 @@ }, "mnw": { "locked": { - "lastModified": 1756659871, - "narHash": "sha256-v6Rh4aQ6RKjM2N02kK9Usn0Ix7+OY66vNpeklc1MnGE=", + "lastModified": 1758834834, + "narHash": "sha256-Y7IvY4F8vajZyp3WGf+KaiIVwondEkMFkt92Cr9NZmg=", "owner": "Gerg-L", "repo": "mnw", - "rev": "ed6cc3e48557ba18266e598a5ebb6602499ada16", + "rev": "cfbc7d1cc832e318d0863a5fc91d940a96034001", "type": "github" }, "original": { @@ -1278,11 +1278,11 @@ ] }, "locked": { - "lastModified": 1759110900, - "narHash": "sha256-fcu/r0ijvaYT2VHGkZGr0wq9uBMNFkiftVBy43/2oig=", + "lastModified": 1759455985, + "narHash": "sha256-8qDv7NXH3fj1CDXed7c7vJLtrRKDZSo0x6TaWSfelVg=", "owner": "fufexan", "repo": "nix-gaming", - "rev": "2ac6a49266e9159ccb001b4c8cb1f50f67d502ae", + "rev": "eb5ab503cbd3cb386e8d85a55a9faed73ec7dc37", "type": "github" }, "original": { @@ -1443,11 +1443,11 @@ }, "nixpkgs_8": { "locked": { - "lastModified": 1759036355, - "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "lastModified": 1759381078, + "narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee", "type": "github" }, "original": { @@ -1459,11 +1459,11 @@ }, "nixpkgs_9": { "locked": { - "lastModified": 1756696532, - "narHash": "sha256-6FWagzm0b7I/IGigOv9pr6LL7NQ86mextfE8g8Q6HBg=", + "lastModified": 1759386674, + "narHash": "sha256-wg1Lz/1FC5Q13R+mM5a2oTV9TA9L/CHHTm3/PiLayfA=", "owner": "nixos", "repo": "nixpkgs", - "rev": "58dcbf1ec551914c3756c267b8b9c8c86baa1b2f", + "rev": "625ad6366178f03acd79f9e3822606dd7985b657", "type": "github" }, "original": { @@ -1482,11 +1482,11 @@ "systems": "systems_5" }, "locked": { - "lastModified": 1758271661, - "narHash": "sha256-ENqd2/33uP5vB44ClDjjAV+J78oF8q1er4QUZuT8Z7g=", + "lastModified": 1759469269, + "narHash": "sha256-DP833ejGUNRRHsJOB3WRTaWWXLNucaDga2ju/fGe+sc=", "owner": "notashelf", "repo": "nvf", - "rev": "b7571df4d6e9ac08506a738ddceeec0b141751b0", + "rev": "e48638aef3a95377689de0ef940443c64f870a09", "type": "github" }, "original": { @@ -1626,11 +1626,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1759245522, - "narHash": "sha256-H4Hx/EuMJ9qi1WzPV4UG2bbZiDCdREtrtDvYcHr0kmk=", + "lastModified": 1759301569, + "narHash": "sha256-7StxDed3v2fAWLkl+Hse9FlpjT7Dk7Cn/4vxTFyEhIg=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "a6bc4a4bbe6a65b71cbf76a0cf528c47a8d9f97f", + "rev": "472037b789cf593172d6adf3b8d9f7a429f6cd9b", "type": "github" }, "original": { @@ -1648,11 +1648,11 @@ ] }, "locked": { - "lastModified": 1759113356, - "narHash": "sha256-xm4kEUcV2jk6u15aHazFP4YsMwhq+PczA+Ul/4FDKWI=", + "lastModified": 1759286284, + "narHash": "sha256-JLdGGc4XDutzSD1L65Ni6Ye+oTm8kWfm0KTPMcyl7Y4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "be3b8843a2be2411500f6c052876119485e957a2", + "rev": "f6f2da475176bb7cff51faae8b3fe879cd393545", "type": "github" }, "original": { diff --git a/hosts/sobotka/server.nix b/hosts/sobotka/server.nix index 08323b7c..0c81b416 100644 --- a/hosts/sobotka/server.nix +++ b/hosts/sobotka/server.nix @@ -8,7 +8,7 @@ uid = 994; gid = 993; - traefik = { + nginx = { enable = true; }; gitea = { @@ -42,7 +42,11 @@ enable = true; }; jellyfin = { - enable = true; + enable = false; + cloudflared = { + tunnelId = "234811e2-bc86-44b2-9abd-493686e25704"; + credentialsFile = config.age.secrets.jellyfinCloudflared.path; + }; }; uptime-kuma = { enable = true; @@ -56,7 +60,7 @@ }; }; www = { - enable = true; + enable = false; url = "cnst.dev"; cloudflared = { tunnelId = "e5076186-efb7-405a-998c-6155af7fb221"; @@ -88,14 +92,14 @@ enable = true; gluetun.enable = true; qbittorrent = { - enable = true; - port = 8080; + enable = false; + port = 8387; }; slskd = { enable = true; }; pihole = { - enable = true; + enable = false; port = 8053; }; }; diff --git a/modules/default.nix b/modules/default.nix index f1c8d7be..66df1a3e 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -141,7 +141,7 @@ ./server/keepalived ./server/gitea ./server/postgres - ./server/traefik + ./server/nginx ./server/www ./server/authentik ]; diff --git a/modules/nixos/programs/pkgs/default.nix b/modules/nixos/programs/pkgs/default.nix index 1c11ca03..1b0747ff 100644 --- a/modules/nixos/programs/pkgs/default.nix +++ b/modules/nixos/programs/pkgs/default.nix @@ -3,17 +3,16 @@ config, lib, ... -}: -let - inherit (lib) +}: let + inherit + (lib) mkIf mkOption mkMerge types ; cfg = config.nixos.programs.pkgs; -in -{ +in { options = { nixos.programs.pkgs = { enable = mkOption { @@ -51,8 +50,7 @@ in }; config = mkIf cfg.enable { - environment.systemPackages = - with pkgs; + environment.systemPackages = with pkgs; mkMerge [ [ pciutils @@ -114,6 +112,8 @@ in helix zfs zfstools + cron + acme-sh ]) (mkIf cfg.dev.enable [ diff --git a/modules/nixos/services/agenix/default.nix b/modules/nixos/services/agenix/default.nix index 3e70707a..de67e4a4 100644 --- a/modules/nixos/services/agenix/default.nix +++ b/modules/nixos/services/agenix/default.nix @@ -70,7 +70,7 @@ in { secrets = { cloudflareFirewallApiKey.file = "${self}/secrets/cloudflareFirewallApiKey.age"; cloudflareDnsApiToken.file = "${self}/secrets/cloudflareDnsApiToken.age"; - cloudflareDnsCredentials.file = "${self}/secrets/cloudflareDnsCredentials.age"; + # cloudflareDnsCredentials.file = "${self}/secrets/cloudflareDnsCredentials.age"; wgCredentials.file = "${self}/secrets/wgCredentials.age"; wgSobotkaPrivateKey.file = "${self}/secrets/wgSobotkaPrivateKey.age"; gluetunEnvironment.file = "${self}/secrets/gluetunEnvironment.age"; diff --git a/modules/server/authentik/default.nix b/modules/server/authentik/default.nix index c6e3871c..90fa1628 100644 --- a/modules/server/authentik/default.nix +++ b/modules/server/authentik/default.nix @@ -53,15 +53,9 @@ in { age.secrets = { authentikEnv = { file = "${self}/secrets/authentikEnv.age"; - owner = "authentik"; - group = "authentik"; - mode = "0400"; }; authentikCloudflared = { file = "${self}/secrets/authentikCloudflared.age"; - owner = "authentik"; - group = "authentik"; - mode = "0400"; }; }; @@ -110,15 +104,9 @@ in { "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" ]; + timeout = "10s"; }; }; }; diff --git a/modules/server/default.nix b/modules/server/default.nix index 3f7362e4..c0496efa 100644 --- a/modules/server/default.nix +++ b/modules/server/default.nix @@ -17,14 +17,14 @@ in { default = ""; type = types.str; description = '' - Email name to be used to access the server services via Caddy reverse proxy + Email name to be used to access the server services via NGINX reverse proxy ''; }; domain = mkOption { default = ""; type = types.str; description = '' - Domain name to be used to access the server services via Caddy reverse proxy + Domain name to be used to access the server services via NGINX reverse proxy ''; }; user = lib.mkOption { @@ -65,6 +65,11 @@ in { }; config = lib.mkIf cfg.enable { + _module.args = { + myLib = import ./lib.nix { + inherit lib config pkgs; + }; + }; users = { groups.${cfg.group} = { gid = cfg.gid; @@ -93,7 +98,7 @@ in { "render" "input" "authentik" - "traefik" + "nginx" ]; }; }; diff --git a/modules/server/fail2ban/default.nix b/modules/server/fail2ban/default.nix index 237bf5e5..1c0615cb 100644 --- a/modules/server/fail2ban/default.nix +++ b/modules/server/fail2ban/default.nix @@ -58,14 +58,14 @@ in { jails = lib.attrsets.mapAttrs (name: value: { settings = { - bantime = "24h"; - findtime = "10m"; + bantime = "168h"; + findtime = "30m"; enabled = true; backend = "systemd"; journalmatch = "_SYSTEMD_UNIT=${value.serviceName}.service"; port = "http,https"; filter = "${name}"; - maxretry = 3; + maxretry = value.maxRetry or 5; action = "cloudflare-token-agenix"; }; }) diff --git a/modules/server/gitea/default.nix b/modules/server/gitea/default.nix index 171cca9f..b9412c34 100644 --- a/modules/server/gitea/default.nix +++ b/modules/server/gitea/default.nix @@ -99,23 +99,6 @@ in { }; }; - services.traefik = { - dynamicConfigOptions = { - http = { - services.gitea.loadBalancer.servers = [{url = "http://127.0.0.1:5003";}]; - routers = { - gitea = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`)"; - service = "gitea"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; - server.postgresql.databases = [ { database = "gitea"; diff --git a/modules/server/homepage-dashboard/default.nix b/modules/server/homepage-dashboard/default.nix index 7ca11976..d7d2a1c2 100644 --- a/modules/server/homepage-dashboard/default.nix +++ b/modules/server/homepage-dashboard/default.nix @@ -212,25 +212,6 @@ in { } ]; }; - - traefik = { - dynamicConfigOptions = { - http = { - services.homepage.loadBalancer.servers = [ - {url = "http://127.0.0.1:${toString config.services.${unit}.listenPort}";} - ]; - routers = { - homepage = { - entryPoints = ["websecure"]; - rule = "Host(`cnix.dev`)"; - service = "homepage"; - tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; - }; - }; - }; - }; - }; }; }; } diff --git a/modules/server/jellyfin/default.nix b/modules/server/jellyfin/default.nix index 133028c1..ebfcdd9c 100644 --- a/modules/server/jellyfin/default.nix +++ b/modules/server/jellyfin/default.nix @@ -2,23 +2,38 @@ config, lib, pkgs, + self, ... }: let - service = "jellyfin"; - cfg = config.server.${service}; + unit = "jellyfin"; + cfg = config.server.${unit}; srv = config.server; in { - options.server.${service} = { + options.server.${unit} = { enable = lib.mkEnableOption { - description = "Enable ${service}"; + description = "Enable ${unit}"; }; configDir = lib.mkOption { type = lib.types.str; - default = "/var/lib/${service}"; + default = "/var/lib/${unit}"; }; url = lib.mkOption { type = lib.types.str; - default = "jellyfin.${srv.domain}"; + default = "jellyfin.${srv.www.url}"; + }; + cloudflared = { + credentialsFile = lib.mkOption { + type = lib.types.str; + example = lib.literalExpression '' + pkgs.writeText "cloudflare-credentials.json" ''' + {"AccountTag":"secret"."TunnelSecret":"secret","TunnelID":"secret"} + ''' + ''; + }; + tunnelId = lib.mkOption { + type = lib.types.str; + example = "00000000-0000-0000-0000-000000000000"; + }; }; homepage.name = lib.mkOption { type = lib.types.str; @@ -38,17 +53,62 @@ in { }; }; config = lib.mkIf cfg.enable { - services.${service} = { - enable = true; - user = srv.user; - group = srv.group; + age.secrets = { + jellyfinCloudflared = { + file = "${self}/secrets/jellyfinCloudflared.age"; + owner = "${srv.user}"; + group = "${srv.group}"; + mode = "0400"; + }; }; + + services = { + cloudflared = { + enable = true; + tunnels.${cfg.cloudflared.tunnelId} = { + credentialsFile = cfg.cloudflared.credentialsFile; + default = "http_status:404"; + ingress."${cfg.url}".service = "http://127.0.0.1:8096"; + }; + }; + + ${unit} = { + enable = true; + user = srv.user; + group = srv.group; + }; + }; + environment.systemPackages = with pkgs; [ jellyfin-ffmpeg ]; services.traefik = { dynamicConfigOptions = { http = { + middlewares = { + secureHeaders = { + headers = { + stsSeconds = 31536000; + forceSTSHeader = true; + stsIncludeSubdomains = true; + stsPreload = true; + browserXssFilter = true; + frameDeny = true; + referrerPolicy = "no-referrer"; + contentTypeNosniff = true; + customResponseHeaders = { + "Content-Security-Policy" = "default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"; + }; + }; + }; + ratelimit = { + rateLimit = { + average = 10; + burst = 20; + }; + }; + }; + services.jellyfin.loadBalancer.servers = [{url = "http://127.0.0.1:8096";}]; routers = { jellyfin = { @@ -56,7 +116,7 @@ in { rule = "Host(`${cfg.url}`)"; service = "jellyfin"; tls.certResolver = "letsencrypt"; - # middlewares = ["authentik"]; + middlewares = ["authentik" "secureHeaders" "ratelimit"]; }; }; }; diff --git a/modules/server/lib.nix b/modules/server/lib.nix new file mode 100644 index 00000000..3cf5cbd0 --- /dev/null +++ b/modules/server/lib.nix @@ -0,0 +1,148 @@ +# from @jtojnar +{ + config, + lib, + pkgs, + ... +}: { + mkVirtualHost = { + path ? null, + config ? "", + acme ? null, + redirect ? null, + ... + } @ args: + ( + if lib.isString acme + then { + useACMEHost = acme; + forceSSL = true; + } + else {} + ) + // ( + if lib.isBool acme + then { + enableACME = acme; + forceSSL = true; + } + else {} + ) + // ( + if redirect != null + then { + globalRedirect = redirect; + } + else {} + ) + // ( + if path != null + then { + root = "/var/www/" + path; + } + else {} + ) + // { + extraConfig = config; + } + // builtins.removeAttrs args [ + "path" + "config" + "acme" + "redirect" + ]; + mkPhpPool = { + user, + debug ? false, + settings ? {}, + ... + } @ args: + { + inherit user; + settings = + { + "listen.owner" = "nginx"; + "listen.group" = "root"; + "pm" = "dynamic"; + "pm.max_children" = 5; + "pm.start_servers" = 2; + "pm.min_spare_servers" = 1; + "pm.max_spare_servers" = 3; + } + // ( + lib.optionalAttrs debug { + # log worker's stdout, but this has a performance hit + "catch_workers_output" = true; + } + // settings + ); + } + // builtins.removeAttrs args [ + "user" + "debug" + "settings" + ]; + enablePHP = sockName: '' + fastcgi_pass unix:${config.services.phpfpm.pools.${sockName}.socket}; + include ${config.services.nginx.package}/conf/fastcgi.conf; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; + ''; + + /* + Adds extra options to ssh key that will only allow it to be used for rsync. + See sshd(8) manual page for details. + */ + restrictToRsync = directory: key: ''command="${pkgs.rrsync}/bin/rrsync -wo ${directory}",restrict ${key}''; + + /* + Emulate systemd credentials. + Those will only be available to the user the service is running under, + not being aware of dropped euid. + http://systemd.io/CREDENTIALS/ + */ + emulateCredentials = let + parseCredential = credential: let + matches = builtins.match "(.+):(.+)" credential; + in + assert lib.assertMsg (matches != null) "A credential needs to match “id:value” format"; { + id = builtins.elemAt matches 0; + value = builtins.elemAt matches 1; + }; + + parseCredentials = credentials: + builtins.map parseCredential ( + if builtins.isList credentials + then credentials + else lib.splitString "," credentials + ); + in + serviceConfig: + lib.mkMerge [ + (builtins.removeAttrs serviceConfig [ + "SetCredential" + "LoadCredential" + ]) + { + Environment = [ + "CREDENTIALS_DIRECTORY=${ + pkgs.runCommand "credentials" {} '' + mkdir "$out" + ${lib.concatMapStringsSep "\n" ({ + id, + value, + }: ''ln -s "${value}" "$out/${id}"'') ( + parseCredentials serviceConfig.LoadCredential or [] + )} + ${lib.concatMapStringsSep "\n" ({ + id, + value, + }: ''echo -n "${value}" > "$out/${id}"'') ( + parseCredentials serviceConfig.SetCredential or [] + )} + '' + }" + ]; + } + ]; +} diff --git a/modules/server/nginx/default.nix b/modules/server/nginx/default.nix new file mode 100644 index 00000000..6cae2f38 --- /dev/null +++ b/modules/server/nginx/default.nix @@ -0,0 +1,79 @@ +{ + lib, + config, + pkgs, + self, + myLib, + ... +}: let + inherit (myLib) mkVirtualHost; + inherit (lib) mkEnableOption mkIf types; + + unit = "nginx"; + cfg = config.server.nginx; + srv = config.server; +in { + options.server.nginx = { + enable = mkEnableOption "Enable global NGINX reverse proxy with ACME"; + }; + + config = mkIf cfg.enable { + age.secrets = { + nginxEnv = { + file = "${self}/secrets/nginxEnv.age"; + }; + }; + + networking.firewall.allowedTCPPorts = [80 443]; + + security = { + acme = { + acceptTerms = true; + defaults.email = config.server.email; + certs.${srv.domain} = { + reloadServices = ["nginx.service"]; + domain = "${srv.domain}"; + extraDomainNames = ["*.${srv.domain}"]; + dnsProvider = "cloudflare"; + dnsPropagationCheck = true; + group = config.services.nginx.group; + environmentFile = config.age.secrets.nginxEnv.path; + }; + }; + dhparams = { + enable = true; + params.nginx = {}; + }; + }; + + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + resolver.addresses = config.networking.nameservers; + sslDhparam = config.security.dhparams.params.nginx.path; + appendHttpConfig = '' + proxy_headers_hash_max_size 512; + proxy_headers_hash_bucket_size 128; + ''; + + virtualHosts = let + labDomain = "cnix.dev"; + labCert = { + useACMEHost = "cnix.dev"; + forceSSL = true; + }; + in { + # proxies on domain with https, only accessible within local network + "${labDomain}" = + labCert + // { + locations."/".proxyPass = "http://127.0.0.1:${toString config.services.homepage-dashboard.listenPort}"; + }; + }; + }; + users.users.nginx.extraGroups = ["acme"]; + }; +} diff --git a/modules/server/podman/default.nix b/modules/server/podman/default.nix index 9f523d96..0dc00cf1 100644 --- a/modules/server/podman/default.nix +++ b/modules/server/podman/default.nix @@ -34,7 +34,7 @@ in { }; port = lib.mkOption { type = lib.types.int; - default = 8080; + default = 8387; description = "The port to host qBittorrent on."; }; homepage.name = lib.mkOption { @@ -143,62 +143,6 @@ in { ]; }; - services.traefik = lib.mkMerge [ - (lib.mkIf cfg.pihole.enable { - dynamicConfigOptions = { - http = { - services = { - pihole.loadBalancer.servers = [{url = "http://localhost:${toString cfg.pihole.port}";}]; - }; - routers = { - pihole = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.pihole.url}`)"; - service = "pihole"; - tls.certResolver = "letsencrypt"; - }; - }; - }; - }; - }) - - (lib.mkIf cfg.qbittorrent.enable { - dynamicConfigOptions = { - http = { - services = { - qbittorrent.loadBalancer.servers = [{url = "http://localhost:${toString cfg.qbittorrent.port}";}]; - }; - routers = { - qbittorrent = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.qbittorrent.url}`)"; - service = "qbittorrent"; - tls.certResolver = "letsencrypt"; - }; - }; - }; - }; - }) - - (lib.mkIf cfg.slskd.enable { - dynamicConfigOptions = { - http = { - services = { - slskd.loadBalancer.servers = [{url = "http://localhost:${toString cfg.slskd.port}";}]; - }; - routers = { - slskd = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.slskd.url}`)"; - service = "slskd"; - tls.certResolver = "letsencrypt"; - }; - }; - }; - }; - }) - ]; - virtualisation.oci-containers.containers = lib.mkMerge [ (lib.mkIf cfg.gluetun.enable { gluetun = { @@ -206,7 +150,7 @@ in { ports = [ "8388:8388" "58846:58846" - "8080:8080" + "8387:8387" "5030:5030" "5031:5031" "50300:50300" @@ -235,7 +179,7 @@ in { autoStart = true; dependsOn = ["gluetun"]; ports = [ - "8080:8080" + "8387:8387" "58846:58846" ]; extraOptions = [ diff --git a/modules/server/sonarr/default.nix b/modules/server/sonarr/default.nix index aa77ae3f..7078f10e 100644 --- a/modules/server/sonarr/default.nix +++ b/modules/server/sonarr/default.nix @@ -43,15 +43,16 @@ in { group = srv.group; }; services.traefik = { + # staticConfigOptions.entryPoints.${unit}.address = "127.0.0.1:8989"; dynamicConfigOptions = { http = { - services.sonarr.loadBalancer.servers = [{url = "http://127.0.0.1:8989";}]; + # services.sonarr.loadBalancer.servers = [{url = "http://127.0.0.1:8989";}]; routers = { sonarr = { entryPoints = ["websecure"]; rule = "Host(`${cfg.url}`)"; service = "sonarr"; - tls.certResolver = "letsencrypt"; + # tls.certResolver = "letsencrypt"; # middlewares = ["authentik"]; }; }; diff --git a/modules/server/traefik/default.nix b/modules/server/traefik/default.nix deleted file mode 100644 index 34225bc0..00000000 --- a/modules/server/traefik/default.nix +++ /dev/null @@ -1,100 +0,0 @@ -{ - lib, - config, - pkgs, - self, - ... -}: let - inherit (lib) mkEnableOption mkIf types; - - cfg = config.server.traefik; - getCloudflareCredentials = hostname: - if hostname == "ziggy" - then config.age.secrets.cloudflareDnsCredentialsZiggy.path - else if hostname == "sobotka" - then config.age.secrets.cloudflareDnsCredentials.path - else throw "Unknown hostname: ${hostname}"; -in { - options.server.traefik = { - enable = mkEnableOption "Enable global Traefik reverse proxy with ACME"; - }; - - config = mkIf cfg.enable { - age.secrets.traefikEnv = { - file = "${self}/secrets/traefikEnv.age"; - mode = "640"; - owner = "root"; - group = "traefik"; - }; - - systemd.services.traefik = { - serviceConfig = { - EnvironmentFile = [config.age.secrets.traefikEnv.path]; - }; - }; - networking.firewall.allowedTCPPorts = [80 443]; - - services = { - tailscale.permitCertUid = "traefik"; - traefik = { - enable = true; - - staticConfigOptions = { - log = { - level = "DEBUG"; - }; - - tracing = {}; - api = { - dashboard = true; - }; - certificatesResolvers = { - tailscale.tailscale = {}; - letsencrypt = { - acme = { - email = "adam@cnst.dev"; - storage = "/var/lib/traefik/cert.json"; - dnsChallenge = { - provider = "cloudflare"; - resolvers = [ - "1.1.1.1:53" - "1.0.0.1:53" - ]; - }; - }; - }; - }; - - entryPoints = { - redis = { - address = "0.0.0.0:6381"; - }; - postgres = { - address = "0.0.0.0:5433"; - }; - web = { - address = "0.0.0.0:80"; - http.redirections.entryPoint = { - to = "websecure"; - scheme = "https"; - permanent = true; - }; - }; - websecure = { - address = "0.0.0.0:443"; - http.tls = { - certResolver = "letsencrypt"; - domains = [ - { - main = "cnix.dev"; - sans = ["*.cnix.dev"]; - } - ]; - }; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/server/www/default.nix b/modules/server/www/default.nix index 45f6f420..862fe017 100644 --- a/modules/server/www/default.nix +++ b/modules/server/www/default.nix @@ -114,18 +114,5 @@ in { Disallow: / ''; }; - - services.traefik.dynamicConfigOptions.http = { - routers.webfinger = { - entryPoints = ["websecure"]; - rule = "Host(`${cfg.url}`) && Path(`/.well-known/webfinger`)"; - service = "webfinger"; - tls.certResolver = "letsencrypt"; - }; - - services.webfinger.loadBalancer.servers = [ - {url = "http://127.0.0.1:8283";} - ]; - }; }; } diff --git a/secrets/jellyfinCloudflared.age b/secrets/jellyfinCloudflared.age new file mode 100644 index 00000000..187a75c8 --- /dev/null +++ b/secrets/jellyfinCloudflared.age @@ -0,0 +1,11 @@ +age-encryption.org/v1 +-> ssh-ed25519 t9iOEg dULaZVrpkeN1HqwM+HSU46XJjQznBOxa+wUDSSciK1w +FxqlulfViojLLoskKu56Utiq57rkDC6TocoaFQOgahc +-> ssh-ed25519 KUYMFA QYnb24Av6Vy17Lb5gC6dzQszOxa5bRP2PgFetmqhfCA +MaIes8V0NkyTAzFzbRuCMzMLwhYljYHwhXPKEZEdlC4 +-> ssh-ed25519 76RhUQ NLoyNp/z5MXE3Hrmv7CCDYB1X8R1OORm2MZVMbgNDQs +6U8r3wpXNtQdkIH+lo7cDrnjPw9B6eF/jMeb+iW9T1I +-> ssh-ed25519 Jf8sqw GOWsvhSzrHJGJFOgsXr+O1jq7jnjyzKc82Mu7ntrI2Q +P3n32btGFtUxB28/a0iz6lBTM6UP3EYUrq0y8BR49IM +--- geE2uS2TMspihaNwgzH4FQKYoUFFRhjTS6V7F/cdTE0 +%^E3򿈾ggٸk;lE+bxҜ-XEt'&^=c9v* ~u[J!ra4O亘cJWx Rяn Q+cL#}x.]$!f2D6|;l҂{2z2P:/('0`Ѳj6 SHa'5NvJk]\a \ No newline at end of file diff --git a/secrets/nginxEnv.age b/secrets/nginxEnv.age new file mode 100644 index 00000000..4281ed08 --- /dev/null +++ b/secrets/nginxEnv.age @@ -0,0 +1,12 @@ +age-encryption.org/v1 +-> ssh-ed25519 t9iOEg SCmn/7gQOD3fRn0PKPXxNBDKNSqUcnejG8tPwjoNT1I +vd57xXCQ16PPPMlNysw6BWSIP/EgB3HrWlesBiQUVA0 +-> ssh-ed25519 KUYMFA WVn1OhFDxMMEHFa1yHpBmWskbcRgHyBiDWZjPv8NfX8 +8BCp/iUFkRsAGnc+0MEfpRXo1/Xla4WO3qYZMSl7LGw +-> ssh-ed25519 76RhUQ ZfdS1EWtBOwd7+hVnJ/JqxUCDTYmfEs1OwyzXajFMmg +eE0ahMgW76RFu9y9Z+PE6IzaLX3ydhhJvjk5vu2EgXo +-> ssh-ed25519 Jf8sqw Pr/LkT5RQysShzjqEZlXFRHim3WpAav04c/GGftDTiA +TWWRURLPnkpsxxSdSQem9sAoUoLt5wQe0CYl1xl1RaA +--- cfGjcdOvwxLDlANhDe44U5JXGAyRYPZMVtC48PuhLNo +Zyr#*K  +$FB$^BT#Dv3