diff --git a/modules/nixos/cloudflared.nix b/modules/nixos/cloudflared.nix index 350676a0..52d13ec9 100644 --- a/modules/nixos/cloudflared.nix +++ b/modules/nixos/cloudflared.nix @@ -1,38 +1,67 @@ -{ +let + tunnelModule = {pkgs, config, lib, ...}: let + inherit (lib.options) mkOption mkEnableOption; + settingsFormat = pkgs.formats.json {}; + in { + options = with lib.types; { + extraArgs = mkOption { + type = listOf str; + default = []; + }; + extraTunnel = { + enable = + mkEnableOption "extra tunnels" + // { + default = config.extraTunnel.ingress != {}; + }; + ingress = mkOption { + inherit (settingsFormat) type; + default = {}; + }; + }; + }; + }; +in { pkgs, config, utils, + gensokyo-zone, lib, ... }: let inherit (lib.attrsets) mapAttrsToList mapAttrs' nameValuePair filterAttrsRecursive; inherit (lib.lists) singleton; inherit (lib.modules) mkIf mkMerge mkForce; - inherit (lib.options) mkOption mkEnableOption; + inherit (lib.options) mkOption; cfg = config.services.cloudflared; - settingsFormat = pkgs.formats.json {}; in { options.services.cloudflared = with lib.types; { - tunnels = let - tunnelModule = {config, ...}: { - options = { - extraTunnel = { - enable = - mkEnableOption "extra tunnels" - // { - default = config.extraTunnel.ingress != {}; - }; - ingress = mkOption { - inherit (settingsFormat) type; - default = {}; - }; - }; + metricsPort = mkOption { + type = nullOr port; + default = null; + }; + metricsBind = mkOption { + type = str; + default = "127.0.0.1"; + }; + extraArgs = mkOption { + type = listOf str; + default = []; + }; + tunnels = mkOption { + type = attrsOf (submoduleWith { + modules = [tunnelModule]; + shorthandOnlyDefinesConfig = true; + specialArgs = { + inherit pkgs utils gensokyo-zone; }; - }; - in - mkOption { - type = attrsOf (submodule tunnelModule); - }; + }); + }; + }; + config.services.cloudflared = { + extraArgs = mkIf (cfg.metricsPort != null) [ + "--metrics" "${cfg.metricsBind}:${toString cfg.metricsPort}" + ]; }; config.systemd.services = let filterConfig = filterAttrsRecursive (_: v: ! builtins.elem v [null [] {}]); @@ -44,29 +73,38 @@ in { in mkIf cfg.enable (mapAttrs' (uuid: tunnel: let RuntimeDirectory = "cloudflared-tunnel-${uuid}"; - configPath = "/run/${RuntimeDirectory}/config.yml"; settings = { tunnel = uuid; credentials-file = tunnel.credentialsFile; + warp-routing = filterConfig tunnel.warp-routing; + originRequest = filterConfig tunnel.originRequest; ingress = mapAttrsToList mapIngress tunnel.ingress ++ mapAttrsToList mapIngress tunnel.extraTunnel.ingress ++ singleton {service = tunnel.default;}; }; + configPath = + if tunnel.extraTunnel.enable + then "/run/${RuntimeDirectory}/config.yml" + else pkgs.writeText "cloudflared.yml" (builtins.toJSON settings); + args = [ + "--config=${configPath}" + "--no-autoupdate" + ] ++ cfg.extraArgs ++ tunnel.extraArgs; in nameValuePair "cloudflared-tunnel-${uuid}" (mkMerge [ { after = mkIf config.services.tailscale.enable ["tailscale-autoconnect.service"]; serviceConfig = { RestartSec = 10; + ExecStart = mkForce [ + "${cfg.package}/bin/cloudflared tunnel ${utils.escapeSystemdExecArgs args} run" + ]; }; } (mkIf tunnel.extraTunnel.enable { serviceConfig = { inherit RuntimeDirectory; - ExecStart = mkForce [ - "${cfg.package}/bin/cloudflared tunnel --config=${configPath} --no-autoupdate run" - ]; ExecStartPre = [ (pkgs.writeShellScript "cloudflared-tunnel-${uuid}-prepare" '' ${utils.genJqSecretsReplacementSnippet settings configPath} diff --git a/modules/system/exports/cloudflared.nix b/modules/system/exports/cloudflared.nix new file mode 100644 index 00000000..9ecfded9 --- /dev/null +++ b/modules/system/exports/cloudflared.nix @@ -0,0 +1,43 @@ +{ + lib, + gensokyo-zone, + ... +}: let + inherit (gensokyo-zone.lib) mkAlmostOptionDefault; + inherit (lib.modules) mkIf; +in { + config.exports.services.cloudflared = {config, systemConfig, ...}: let + assertMetrics = nixosConfig: let + cfg = nixosConfig.services.cloudflared; + metricsPort = + if config.ports.metrics.enable + then config.ports.metrics.port + else null; + in { + assertion = metricsPort == cfg.metricsPort; + message = "metricsPort mismatch"; + }; + in { + displayName = mkAlmostOptionDefault "Cloudflare Tunnel/${systemConfig.name}"; + nixos = { + serviceAttr = "cloudflared"; + assertions = mkIf config.enable [ + assertMetrics + ]; + }; + defaults.port.listen = mkAlmostOptionDefault "lan"; + ports = { + metrics = { + port = mkAlmostOptionDefault 3011; + protocol = "http"; + status = { + enable = true; + gatus.http = { + statusCondition = mkAlmostOptionDefault "[STATUS] == 404"; + }; + }; + prometheus.exporter.enable = mkAlmostOptionDefault true; + }; + }; + }; +} diff --git a/modules/system/exports/home-assistant.nix b/modules/system/exports/home-assistant.nix index 84b1eca2..89585c18 100644 --- a/modules/system/exports/home-assistant.nix +++ b/modules/system/exports/home-assistant.nix @@ -3,9 +3,8 @@ gensokyo-zone, ... }: let - inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; + inherit (gensokyo-zone.lib) mkAlmostOptionDefault; inherit (lib.modules) mkIf; - inherit (lib.attrsets) mapAttrs; inherit (lib.lists) all imap0; inherit (lib.trivial) id; in { diff --git a/nixos/cloudflared.nix b/nixos/cloudflared.nix index 7171518c..2ada8194 100644 --- a/nixos/cloudflared.nix +++ b/nixos/cloudflared.nix @@ -7,7 +7,16 @@ cfg = config.services.cloudflared; in { config = { - services.cloudflared.enable = mkDefault true; + services.cloudflared = { + enable = mkDefault true; + metricsPort = mkDefault 3011; + metricsBind = "[::]"; + }; + networking.firewall = mkIf cfg.enable { + interfaces.lan.allowedTCPPorts = mkIf (cfg.metricsPort != null) [ + cfg.metricsPort + ]; + }; boot.kernel.sysctl = mkIf (!config.boot.isContainer && cfg.enable) { # https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes "net.core.rmem_max" = mkDefault 2500000; diff --git a/systems/hakurei/default.nix b/systems/hakurei/default.nix index 797c5111..85274bca 100644 --- a/systems/hakurei/default.nix +++ b/systems/hakurei/default.nix @@ -19,6 +19,7 @@ exports = { services = { tailscale.enable = true; + cloudflared.enable = true; samba.enable = true; syncplay.enable = true; vouch-proxy = { diff --git a/systems/keycloak/default.nix b/systems/keycloak/default.nix index 3597fa6c..736d1c36 100644 --- a/systems/keycloak/default.nix +++ b/systems/keycloak/default.nix @@ -12,6 +12,7 @@ _: { keycloak.enable = true; vouch-proxy.enable = true; vaultwarden.enable = true; + cloudflared.enable = true; nginx = { enable = true; ports.proxied.enable = true; diff --git a/systems/kuwubernetes/default.nix b/systems/kuwubernetes/default.nix index 9fa4bb67..4ba55601 100644 --- a/systems/kuwubernetes/default.nix +++ b/systems/kuwubernetes/default.nix @@ -23,6 +23,7 @@ _: { }; exports = { services = { + cloudflared.enable = true; }; }; } diff --git a/systems/mediabox/default.nix b/systems/mediabox/default.nix index 3725eb57..2662748d 100644 --- a/systems/mediabox/default.nix +++ b/systems/mediabox/default.nix @@ -13,6 +13,7 @@ _: { enable = true; ports.proxied.enable = true; }; + cloudflared.enable = true; plex.enable = true; invidious.enable = true; deluge.enable = true; diff --git a/systems/tei/default.nix b/systems/tei/default.nix index 70b4e62f..c8dff582 100644 --- a/systems/tei/default.nix +++ b/systems/tei/default.nix @@ -13,6 +13,7 @@ _: { enable = true; ports.proxied.enable = true; }; + cloudflared.enable = true; tailscale.enable = true; home-assistant.enable = true; zigbee2mqtt.enable = true; diff --git a/systems/utsuho/default.nix b/systems/utsuho/default.nix index a56f8af6..dd1f00c3 100644 --- a/systems/utsuho/default.nix +++ b/systems/utsuho/default.nix @@ -13,6 +13,7 @@ _: { enable = true; ports.proxied.enable = true; }; + cloudflared.enable = true; unifi.enable = true; mosquitto.enable = true; dnsmasq.enable = true;