From ffe8830b5e4ff33527ce9213079cf832bc6899ef Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Tue, 19 May 2026 23:14:04 +0200 Subject: [PATCH] Tunnel argocd/ha/longhorn/lingo/blog/serpbear through Hetzner Refactor node001 nginx to drive both the SNI map and the port-80 vhosts from a single tunneledHosts list so adding a service is one line. --- dns/domains/rubenhensen.nl.yaml | 12 +-- nix-infra-machine/nodes/node001.nix | 159 ++++++++++++---------------- 2 files changed, 74 insertions(+), 97 deletions(-) diff --git a/dns/domains/rubenhensen.nl.yaml b/dns/domains/rubenhensen.nl.yaml index bd53137..1271c4a 100644 --- a/dns/domains/rubenhensen.nl.yaml +++ b/dns/domains/rubenhensen.nl.yaml @@ -26,7 +26,7 @@ records: - name: "argocd" expire: 300 type: A - content: "62.41.87.114" + content: "46.224.26.65" - name: "vault" expire: 300 type: A @@ -38,23 +38,23 @@ records: - name: "ha" expire: 300 type: A - content: "62.41.87.114" + content: "46.224.26.65" - name: "longhorn" expire: 300 type: A - content: "62.41.87.114" + content: "46.224.26.65" - name: "lingo" expire: 300 type: A - content: "62.41.87.114" + content: "46.224.26.65" - name: "blog" expire: 300 type: A - content: "62.41.87.114" + content: "46.224.26.65" - name: "serpbear" expire: 300 type: A - content: "62.41.87.114" + content: "46.224.26.65" - name: "@" expire: 300 type: MX diff --git a/nix-infra-machine/nodes/node001.nix b/nix-infra-machine/nodes/node001.nix index db78658..31264a3 100644 --- a/nix-infra-machine/nodes/node001.nix +++ b/nix-infra-machine/nodes/node001.nix @@ -3,6 +3,39 @@ let # Public IP of the home network where the k8s cluster ingress lives. # Keep in sync with dns/domains/rubenhensen.nl.yaml. homeIP = "62.41.87.114"; + + # Hosts tunneled to the home k8s cluster. Each entry gets: + # * an SNI map entry for TCP passthrough on :443 + # * an HTTP vhost on :80 that reverse-proxies to the cluster + # The cluster's nginx-ingress terminates TLS with cert-manager. + tunneledHosts = [ + "rss.rubenhensen.nl" + "authentik.rubenhensen.nl" + "vault.rubenhensen.nl" + "ynab.rubenhensen.nl" + "argocd.rubenhensen.nl" + "ha.rubenhensen.nl" + "longhorn.rubenhensen.nl" + "lingo.rubenhensen.nl" + "blog.rubenhensen.nl" + "serpbear.rubenhensen.nl" + ]; + + sniMapEntries = + lib.concatMapStringsSep "\n" + (h: " ${h} ${homeIP}:443;") + tunneledHosts; + + tunneledVhosts = lib.listToAttrs (map (h: { + name = h; + value = { + listen = [ + { addr = "0.0.0.0"; port = 80; } + { addr = "[::]"; port = 80; } + ]; + locations."/".proxyPass = "http://${homeIP}"; + }; + }) tunneledHosts); in { # ────────────────────────────────────────────── @@ -53,11 +86,8 @@ in # - tunneled hosts → home cluster ingress on 443 streamConfig = '' map $ssl_preread_server_name $tunnel_upstream { - rss.rubenhensen.nl ${homeIP}:443; - authentik.rubenhensen.nl ${homeIP}:443; - vault.rubenhensen.nl ${homeIP}:443; - ynab.rubenhensen.nl ${homeIP}:443; - default 127.0.0.1:8443; +${sniMapEntries} + default 127.0.0.1:8443; } server { @@ -68,95 +98,42 @@ in } ''; - # Tunneled hosts: forward plain HTTP to the home cluster so the - # cluster's nginx-ingress handles HTTP→HTTPS redirects and - # cert-manager HTTP-01 ACME challenges. - virtualHosts."rss.rubenhensen.nl" = { - listen = [ - { addr = "0.0.0.0"; port = 80; } - { addr = "[::]"; port = 80; } - ]; - locations."/" = { - proxyPass = "http://${homeIP}"; + # Port 80: tunneled hosts reverse-proxy to the home cluster so its + # nginx-ingress handles HTTP→HTTPS redirects and cert-manager + # HTTP-01 ACME challenges. Mail-related hosts serve ACME challenges + # locally for stalwart's cert and redirect everything else to HTTPS. + virtualHosts = tunneledVhosts // { + "mail.rubenhensen.nl" = { + listen = [ + { addr = "0.0.0.0"; port = 80; } + { addr = "[::]"; port = 80; } + ]; + locations."/.well-known/acme-challenge/".root = "/var/lib/acme/acme-challenge"; + locations."/".return = "301 https://$host$request_uri"; }; - }; - - virtualHosts."authentik.rubenhensen.nl" = { - listen = [ - { addr = "0.0.0.0"; port = 80; } - { addr = "[::]"; port = 80; } - ]; - locations."/" = { - proxyPass = "http://${homeIP}"; + "autoconfig.rubenhensen.nl" = { + listen = [ + { addr = "0.0.0.0"; port = 80; } + { addr = "[::]"; port = 80; } + ]; + locations."/.well-known/acme-challenge/".root = "/var/lib/acme/acme-challenge"; + locations."/".return = "301 https://$host$request_uri"; }; - }; - - virtualHosts."vault.rubenhensen.nl" = { - listen = [ - { addr = "0.0.0.0"; port = 80; } - { addr = "[::]"; port = 80; } - ]; - locations."/" = { - proxyPass = "http://${homeIP}"; + "autodiscover.rubenhensen.nl" = { + listen = [ + { addr = "0.0.0.0"; port = 80; } + { addr = "[::]"; port = 80; } + ]; + locations."/.well-known/acme-challenge/".root = "/var/lib/acme/acme-challenge"; + locations."/".return = "301 https://$host$request_uri"; }; - }; - - virtualHosts."ynab.rubenhensen.nl" = { - listen = [ - { addr = "0.0.0.0"; port = 80; } - { addr = "[::]"; port = 80; } - ]; - locations."/" = { - proxyPass = "http://${homeIP}"; - }; - }; - - virtualHosts."mail.rubenhensen.nl" = { - listen = [ - { addr = "0.0.0.0"; port = 80; } - { addr = "[::]"; port = 80; } - ]; - locations."/.well-known/acme-challenge/" = { - root = "/var/lib/acme/acme-challenge"; - }; - locations."/" = { - return = "301 https://$host$request_uri"; - }; - }; - virtualHosts."autoconfig.rubenhensen.nl" = { - listen = [ - { addr = "0.0.0.0"; port = 80; } - { addr = "[::]"; port = 80; } - ]; - locations."/.well-known/acme-challenge/" = { - root = "/var/lib/acme/acme-challenge"; - }; - locations."/" = { - return = "301 https://$host$request_uri"; - }; - }; - virtualHosts."autodiscover.rubenhensen.nl" = { - listen = [ - { addr = "0.0.0.0"; port = 80; } - { addr = "[::]"; port = 80; } - ]; - locations."/.well-known/acme-challenge/" = { - root = "/var/lib/acme/acme-challenge"; - }; - locations."/" = { - return = "301 https://$host$request_uri"; - }; - }; - virtualHosts."rubenhensen.nl" = { - listen = [ - { addr = "0.0.0.0"; port = 80; } - { addr = "[::]"; port = 80; } - ]; - locations."/.well-known/acme-challenge/" = { - root = "/var/lib/acme/acme-challenge"; - }; - locations."/" = { - return = "301 https://$host$request_uri"; + "rubenhensen.nl" = { + listen = [ + { addr = "0.0.0.0"; port = 80; } + { addr = "[::]"; port = 80; } + ]; + locations."/.well-known/acme-challenge/".root = "/var/lib/acme/acme-challenge"; + locations."/".return = "301 https://$host$request_uri"; }; }; };