# 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;
  nginx_uid = 62;
in
{
  config = {
    users.nginx = {
      uid = nginx_uid;
      gid = nginx_uid;
      dir = "/run/";
      shell = "/bin/false";
    };
    groups.nginx = {
      gid = nginx_uid;
      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}
        '';
      };
  };
}