From 6dfa8b17f424baf833034f344ea39393854b14a3 Mon Sep 17 00:00:00 2001 From: sefidel Date: Sat, 3 Feb 2024 04:07:31 +0900 Subject: feat(modules): add expose * This commit adds a module for exposing services to the net securely, by making use of Tailscale and Cloudflare Tunnels. --- modules/expose.nix | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 modules/expose.nix diff --git a/modules/expose.nix b/modules/expose.nix new file mode 100644 index 0000000..29531c5 --- /dev/null +++ b/modules/expose.nix @@ -0,0 +1,67 @@ +{ config, lib, ... }: + +with lib; +let + cfg = config.modules.expose; +in +{ + options.modules.expose = { + enable = mkEnableOption "expose services to network"; + + routes = mkOption { + type = types.attrsOf types.str; + }; + + ssl = { + enable = mkEnableOption "SSL on all routes"; + acmeHost = mkOption { + type = types.nullOr types.str; + default = null; + description = "ACMEHost for the certificate"; + }; + }; + + webmasterEmail = mkOption { type = types.str; description = "Email of the webmaster to be contacted for ACME events"; }; + tailscaleIp = mkOption { type = types.str; description = "Tailscale IP for this node"; }; + cloudflareUUID = mkOption { type = types.str; description = "UUID of the Cloudflare Tunnel"; }; + secrets = { + acme-credentials = mkOption { type = types.path; description = "Path to the acme environment file"; }; + cloudflare-credentials = mkOption { type = types.path; description = "Path to the cloudflare tunnel credentials"; }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.ssl.enable -> cfg.ssl.acmeHost != null; + message = "ssl.acmeHost must be set when enabling SSL"; + } + ]; + + services.nginx.virtualHosts = mapAttrs (_: internal: { + locations."/".proxyPass = internal; + } // optionalAttrs (cfg.ssl.enable) { + forceSSL = true; + useACMEHost = cfg.ssl.acmeHost; + }) (filterAttrs + # Assume that reverse proxy is configured externally + (_: v: (!hasSuffix ":80" v) && (!hasSuffix ":443" v)) + cfg.routes); + + # Discard non-localhost mappings, and replace destination with tailscale IP + services.blocky.settings.customDNS.mapping = mapAttrs + (_: v: cfg.tailscaleIp) + (filterAttrs + (_: v: hasInfix "localhost" v) + cfg.routes); + + services.cloudflared.tunnels."${cfg.cloudflareUUID}" = { + credentialsFile = cfg.secrets.cloudflare-credentials; + ingress = cfg.routes; + } // optionalAttrs (cfg.ssl.enable) { + # TODO: This seems to have no effect. Remove? + originRequest.originServerName = "*.${cfg.ssl.acmeHost}"; + originRequest.caPool = config.security.acme.certs.${cfg.ssl.acmeHost}.directory; + }; + }; +} -- cgit 1.4.1