From 2e513eb4a7982341866f29918c1b7abe9007aec8 Mon Sep 17 00:00:00 2001
From: Daniel Barlow <dan@telent.net>
Date: Sun, 29 Dec 2024 23:34:15 +0000
Subject: [PATCH] example sni proxy using nginx

---
 examples/module-https-proxy.nix | 81 +++++++++++++++++++++++++++++++++
 1 file changed, 81 insertions(+)
 create mode 100644 examples/module-https-proxy.nix

diff --git a/examples/module-https-proxy.nix b/examples/module-https-proxy.nix
new file mode 100644
index 0000000..0e37bd0
--- /dev/null
+++ b/examples/module-https-proxy.nix
@@ -0,0 +1,81 @@
+# This is "contrib"-level code. This module solves a particular
+# problem for my particular setup and is provided as is, as an example
+# of how you might write something similar if you had a similar
+# problem. Don't expect it to work unmolested in your setup (you will
+# at the absolute minimum have to change the domain name), nor even to
+# continue to exist without possibly being changed beyond recognition.
+
+# The computers on my LAN have globally unique routable IPv6
+# addresses, but I have only one public IPv4 address. I want to expose
+# HTTPS services to the Internet _whatever_ machine is hosting them,
+# so I publish an AAAA record to the machine itself, and an A record
+# to the public v4 address of the router which is running this nginx.
+# This nginx checks the SNI in the incoming connection and forwards
+# the connection to the (IPv6 address of the) same hostname
+
+# See https://ww.telent.net/2020/12/2/six_into_4_won_t_go for
+# the original solution to this problem, which used sniproxy (now
+# unmaintained) instead of nginx
+
+{ config, pkgs, ... }:
+let
+  inherit (pkgs.liminix.services)  longrun;
+  inherit (pkgs) writeText;
+in {
+  config = {
+    users.nginx = {
+      uid = 52; gid= 52;
+      dir = "/run/";
+      shell = "/bin/false";
+    };
+    groups.nginx = {
+      gid= 52;
+      usernames = ["nginx"];
+    };
+
+    services.sniproxy =
+      let
+        nginx = pkgs.nginx-small.override {
+          pcre = null;
+          zlib = null;
+          options = [
+            "stream"
+            "stream_ssl_module" "stream_ssl_preread_module"
+            "stream_map_module"
+          ];
+        };
+        conf = writeText "nginx.conf" ''
+          worker_processes auto;
+          error_log /proc/self/fd/1 info;
+          pid /dev/null;
+          user nginx;
+          daemon off;
+          events {
+              worker_connections 1024;
+          }
+
+          stream {
+              log_format proxy '$remote_addr -> $ssl_target';
+              access_log /proc/self/fd/1 proxy;
+              map $ssl_preread_server_name $ssl_target {
+                  hostnames;
+                  .telent.net    $ssl_preread_server_name:443;
+              }
+
+              server {
+                  listen 443;
+                  resolver 127.0.0.1 ipv6=on ipv4=off;
+                  resolver_timeout 1s;
+                  proxy_pass $ssl_target;
+                  ssl_preread on;
+              }
+          }
+      '';
+      in longrun {
+        name = "sniproxy";
+        run = ''
+          ${nginx}/bin/nginx -c ${conf}
+        '';
+      };
+  };
+}