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",