diff options
author | sefidel <contact@sefidel.net> | 2024-02-03 04:07:31 +0900 |
---|---|---|
committer | sefidel <contact@sefidel.net> | 2024-02-03 04:11:19 +0900 |
commit | 6dfa8b17f424baf833034f344ea39393854b14a3 (patch) | |
tree | f099a131c8354540672aaa18d80f6dcd74614fa5 | |
parent | 8cdd2eb6ec996d39a0def41a4f9df46e6863734d (diff) | |
download | nixrc-6dfa8b17f424baf833034f344ea39393854b14a3.tar.gz nixrc-6dfa8b17f424baf833034f344ea39393854b14a3.zip |
feat(modules): add expose
* This commit adds a module for exposing services to the net securely, by making use of Tailscale and Cloudflare Tunnels.
-rw-r--r-- | modules/expose.nix | 67 |
1 files changed, 67 insertions, 0 deletions
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; + }; + }; +} |