From 385587a30619be1383b3e5a3d3371d3108727cb3 Mon Sep 17 00:00:00 2001 From: Richie Cahill Date: Mon, 13 Apr 2026 13:25:41 -0400 Subject: [PATCH 1/4] moving off cloudflare tunnel --- systems/jeeves/services/acme.nix | 63 +++++++++++++++++++ .../jeeves/services/cloud_flare_tunnel.nix | 17 ----- systems/jeeves/services/haproxy.cfg | 33 +++++----- systems/jeeves/services/validate_system.toml | 1 - 4 files changed, 82 insertions(+), 32 deletions(-) create mode 100644 systems/jeeves/services/acme.nix delete mode 100644 systems/jeeves/services/cloud_flare_tunnel.nix diff --git a/systems/jeeves/services/acme.nix b/systems/jeeves/services/acme.nix new file mode 100644 index 0000000..cbbcfdd --- /dev/null +++ b/systems/jeeves/services/acme.nix @@ -0,0 +1,63 @@ +{ + users.users.haproxy.extraGroups = [ "acme" ]; + + security.acme = { + acceptTerms = true; + defaults.email = "Richie@tmmworkshop.com"; + + certs."gitea.tmmworkshop.com" = { + webroot = "/var/lib/acme/.challenges"; + group = "acme"; + reloadServices = [ "haproxy.service" ]; + }; + + certs."audiobookshelf.tmmworkshop.com" = { + webroot = "/var/lib/acme/.challenges"; + group = "acme"; + reloadServices = [ "haproxy.service" ]; + }; + + certs."cache.tmmworkshop.com" = { + webroot = "/var/lib/acme/.challenges"; + group = "acme"; + reloadServices = [ "haproxy.service" ]; + }; + + certs."jellyfin.tmmworkshop.com" = { + webroot = "/var/lib/acme/.challenges"; + group = "acme"; + reloadServices = [ "haproxy.service" ]; + }; + + certs."share.tmmworkshop.com" = { + webroot = "/var/lib/acme/.challenges"; + group = "acme"; + reloadServices = [ "haproxy.service" ]; + }; + }; + + # Minimal nginx to serve ACME HTTP-01 challenge files for HAProxy + services.nginx = { + enable = true; + virtualHosts."acme-challenge" = { + listen = [ + { + addr = "127.0.0.1"; + port = 8402; + } + ]; + locations."/.well-known/acme-challenge/" = { + root = "/var/lib/acme/.challenges"; + }; + }; + }; + + # Ensure the challenge directory exists with correct permissions + systemd.tmpfiles.rules = [ + "d /var/lib/acme/.challenges 0750 acme acme - -" + "d /var/lib/acme/.challenges/.well-known 0750 acme acme - -" + "d /var/lib/acme/.challenges/.well-known/acme-challenge 0750 acme acme - -" + ]; + + users.users.nginx.extraGroups = [ "acme" ]; +} diff --git a/systems/jeeves/services/cloud_flare_tunnel.nix b/systems/jeeves/services/cloud_flare_tunnel.nix deleted file mode 100644 index 33a7bd6..0000000 --- a/systems/jeeves/services/cloud_flare_tunnel.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ pkgs, ... }: -let - vars = import ../vars.nix; -in -{ - systemd.services.cloud_flare_tunnel = { - description = "cloud_flare_tunnel proxy's traffic through cloudflare"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - Type = "simple"; - EnvironmentFile = "${vars.secrets}/docker/cloud_flare_tunnel"; - ExecStart = "${pkgs.cloudflared}/bin/cloudflared --no-autoupdate tunnel run"; - Restart = "on-failure"; - }; - }; -} diff --git a/systems/jeeves/services/haproxy.cfg b/systems/jeeves/services/haproxy.cfg index a8f22c9..f01ad17 100644 --- a/systems/jeeves/services/haproxy.cfg +++ b/systems/jeeves/services/haproxy.cfg @@ -6,6 +6,7 @@ global defaults log global mode http + option httplog retries 3 maxconn 2000 timeout connect 5s @@ -22,25 +23,37 @@ defaults #Application Setup frontend ContentSwitching bind *:80 v4v6 - bind *:443 v4v6 ssl crt /zfs/storage/secrets/docker/cloudflare.pem + bind *:443 v4v6 ssl crt /var/lib/acme/audiobookshelf.tmmworkshop.com/full.pem crt /var/lib/acme/cache.tmmworkshop.com/full.pem crt /var/lib/acme/jellyfin.tmmworkshop.com/full.pem crt /var/lib/acme/share.tmmworkshop.com/full.pem crt /var/lib/acme/gitea.tmmworkshop.com/full.pem mode http + + # ACME challenge routing (must be first) + acl is_acme path_beg /.well-known/acme-challenge/ + use_backend acme_challenge if is_acme + # tmmworkshop.com acl host_audiobookshelf hdr(host) -i audiobookshelf.tmmworkshop.com acl host_cache hdr(host) -i cache.tmmworkshop.com acl host_jellyfin hdr(host) -i jellyfin.tmmworkshop.com acl host_share hdr(host) -i share.tmmworkshop.com - acl host_gcw hdr(host) -i gcw.tmmworkshop.com - acl host_n8n hdr(host) -i n8n.tmmworkshop.com acl host_gitea hdr(host) -i gitea.tmmworkshop.com + # Hosts allowed to serve plain HTTP (add entries to skip the HTTPS redirect) + acl allow_http hdr(host) -i __none__ + # acl allow_http hdr(host) -i example.tmmworkshop.com + + # Redirect all HTTP to HTTPS unless on the allow list or ACME challenge + http-request redirect scheme https code 301 if !{ ssl_fc } !allow_http !is_acme + use_backend audiobookshelf_nodes if host_audiobookshelf use_backend cache_nodes if host_cache use_backend jellyfin if host_jellyfin use_backend share_nodes if host_share - use_backend gcw_nodes if host_gcw - use_backend n8n if host_n8n use_backend gitea if host_gitea +backend acme_challenge + mode http + server acme 127.0.0.1:8402 + backend audiobookshelf_nodes mode http server server 127.0.0.1:8000 @@ -60,14 +73,6 @@ backend share_nodes mode http server server 127.0.0.1:8091 -backend gcw_nodes - mode http - server server 127.0.0.1:8092 - -backend n8n - mode http - server server 127.0.0.1:5678 - backend gitea mode http - server server 127.0.0.1:6443 \ No newline at end of file + server server 127.0.0.1:6443 diff --git a/systems/jeeves/services/validate_system.toml b/systems/jeeves/services/validate_system.toml index d422154..81979c1 100644 --- a/systems/jeeves/services/validate_system.toml +++ b/systems/jeeves/services/validate_system.toml @@ -1,7 +1,6 @@ zpool = ["root_pool", "storage", "media"] services = [ "audiobookshelf", - "cloud_flare_tunnel", "haproxy", "docker", "home-assistant", From 400b8e55ec1792daa6897b993c2f174ba5bc4848 Mon Sep 17 00:00:00 2001 From: Richie Cahill Date: Mon, 13 Apr 2026 15:09:12 -0400 Subject: [PATCH 2/4] updated gitea ssh settings --- .vscode/settings.json | 1 - systems/jeeves/services/gitea.nix | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a50732f..5d3a962 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -40,7 +40,6 @@ "cgroupdriver", "charliermarsh", "Checkpointing", - "cloudflared", "codellama", "codezombiech", "compactmode", diff --git a/systems/jeeves/services/gitea.nix b/systems/jeeves/services/gitea.nix index 6803cc8..fb1bd71 100644 --- a/systems/jeeves/services/gitea.nix +++ b/systems/jeeves/services/gitea.nix @@ -2,7 +2,7 @@ let vars = import ../vars.nix; in { - networking.firewall.allowedTCPPorts = [ 6443 ]; + networking.firewall.allowedTCPPorts = [ 6443 2223 ]; services.gitea = { enable = true; @@ -24,7 +24,7 @@ in ROOT_URL = "https://gitea.tmmworkshop.com/"; HTTP_PORT = 6443; SSH_PORT = 2223; - SSH_LISTEN_PORT = 2224; + SSH_LISTEN_PORT = 2223; START_SSH_SERVER = true; PUBLIC_URL_DETECTION = "auto"; }; From 4e0932c3505de1a473dbda99f8265cedc2992d2f Mon Sep 17 00:00:00 2001 From: Richie Cahill Date: Mon, 13 Apr 2026 18:29:50 -0400 Subject: [PATCH 3/4] ran tree fmt --- systems/jeeves/services/gitea.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/systems/jeeves/services/gitea.nix b/systems/jeeves/services/gitea.nix index fb1bd71..5f511e8 100644 --- a/systems/jeeves/services/gitea.nix +++ b/systems/jeeves/services/gitea.nix @@ -2,7 +2,10 @@ let vars = import ../vars.nix; in { - networking.firewall.allowedTCPPorts = [ 6443 2223 ]; + networking.firewall.allowedTCPPorts = [ + 6443 + 2223 + ]; services.gitea = { enable = true; From 8d4ba8ed19ed47590b64dd321db58da35214b49a Mon Sep 17 00:00:00 2001 From: Richie Cahill Date: Mon, 13 Apr 2026 18:53:27 -0400 Subject: [PATCH 4/4] made web_services dir --- systems/jeeves/default.nix | 1 + .../{services => web_services}/acme.nix | 57 +++++++++---------- systems/jeeves/web_services/default.nix | 9 +++ .../{services => web_services}/haproxy.cfg | 0 .../{services => web_services}/haproxy.nix | 0 5 files changed, 38 insertions(+), 29 deletions(-) rename systems/jeeves/{services => web_services}/acme.nix (58%) create mode 100644 systems/jeeves/web_services/default.nix rename systems/jeeves/{services => web_services}/haproxy.cfg (100%) rename systems/jeeves/{services => web_services}/haproxy.nix (100%) diff --git a/systems/jeeves/default.nix b/systems/jeeves/default.nix index 3dbbbb8..1f1b294 100644 --- a/systems/jeeves/default.nix +++ b/systems/jeeves/default.nix @@ -15,6 +15,7 @@ in "${inputs.self}/common/optional/zerotier.nix" ./docker ./services + ./web_services ./hardware.nix ./networking.nix ./programs.nix diff --git a/systems/jeeves/services/acme.nix b/systems/jeeves/web_services/acme.nix similarity index 58% rename from systems/jeeves/services/acme.nix rename to systems/jeeves/web_services/acme.nix index cbbcfdd..538c1b6 100644 --- a/systems/jeeves/services/acme.nix +++ b/systems/jeeves/web_services/acme.nix @@ -1,39 +1,30 @@ -{ - users.users.haproxy.extraGroups = [ "acme" ]; - - security.acme = { - acceptTerms = true; - defaults.email = "Richie@tmmworkshop.com"; - - certs."gitea.tmmworkshop.com" = { - webroot = "/var/lib/acme/.challenges"; - group = "acme"; - reloadServices = [ "haproxy.service" ]; - }; - - certs."audiobookshelf.tmmworkshop.com" = { - webroot = "/var/lib/acme/.challenges"; - group = "acme"; - reloadServices = [ "haproxy.service" ]; - }; +let + domains = [ + "audiobookshelf" + "cache" + "gitea" + "jellyfin" + "share" + ]; - certs."cache.tmmworkshop.com" = { + makeCert = name: { + name = "${name}.tmmworkshop.com"; + value = { webroot = "/var/lib/acme/.challenges"; group = "acme"; reloadServices = [ "haproxy.service" ]; }; + }; - certs."jellyfin.tmmworkshop.com" = { - webroot = "/var/lib/acme/.challenges"; - group = "acme"; - reloadServices = [ "haproxy.service" ]; - }; + acmeServices = map (domain: "acme-${domain}.tmmworkshop.com.service") domains; +in +{ + users.users.haproxy.extraGroups = [ "acme" ]; - certs."share.tmmworkshop.com" = { - webroot = "/var/lib/acme/.challenges"; - group = "acme"; - reloadServices = [ "haproxy.service" ]; - }; + security.acme = { + acceptTerms = true; + defaults.email = "Richie@tmmworkshop.com"; + certs = builtins.listToAttrs (map makeCert domains); }; # Minimal nginx to serve ACME HTTP-01 challenge files for HAProxy @@ -60,4 +51,12 @@ ]; users.users.nginx.extraGroups = [ "acme" ]; + + # HAProxy needs certs to exist before it can bind :443. + # NixOS's acme module generates self-signed placeholders on first boot + # via acme-.service — just make HAProxy wait for them. + systemd.services.haproxy = { + after = acmeServices; + wants = acmeServices; + }; } diff --git a/systems/jeeves/web_services/default.nix b/systems/jeeves/web_services/default.nix new file mode 100644 index 0000000..1133fcb --- /dev/null +++ b/systems/jeeves/web_services/default.nix @@ -0,0 +1,9 @@ +{ lib, ... }: +{ + imports = + let + files = builtins.attrNames (builtins.readDir ./.); + nixFiles = builtins.filter (name: lib.hasSuffix ".nix" name && name != "default.nix") files; + in + map (file: ./. + "/${file}") nixFiles; +} diff --git a/systems/jeeves/services/haproxy.cfg b/systems/jeeves/web_services/haproxy.cfg similarity index 100% rename from systems/jeeves/services/haproxy.cfg rename to systems/jeeves/web_services/haproxy.cfg diff --git a/systems/jeeves/services/haproxy.nix b/systems/jeeves/web_services/haproxy.nix similarity index 100% rename from systems/jeeves/services/haproxy.nix rename to systems/jeeves/web_services/haproxy.nix