about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsefidel <contact@sefidel.net>2024-02-03 04:07:31 +0900
committersefidel <contact@sefidel.net>2024-02-03 04:11:19 +0900
commit6dfa8b17f424baf833034f344ea39393854b14a3 (patch)
treef099a131c8354540672aaa18d80f6dcd74614fa5
parent8cdd2eb6ec996d39a0def41a4f9df46e6863734d (diff)
downloadnixrc-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.nix67
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;
+    };
+  };
+}