let domains = [ "audiobookshelf" "cache" "gitea" "gems" "jellyfin" "share" ]; extraDomains = [ "www.norn-sight.com" ]; makeCert = name: { name = "${name}.tmmworkshop.com"; value = { webroot = "/var/lib/acme/.challenges"; group = "acme"; reloadServices = [ "haproxy.service" ]; }; }; makeExtraCert = name: { inherit name; value = { webroot = "/var/lib/acme/.challenges"; group = "acme"; reloadServices = [ "haproxy.service" ]; }; }; acmeServices = map (domain: "acme-${domain}.tmmworkshop.com.service") domains ++ map (domain: "acme-${domain}.service") extraDomains; in { networking.firewall.allowedTCPPorts = [ 80 443 2223 ]; # Global robots.txt served by HAProxy for every vhost (see haproxy.cfg). environment.etc."haproxy/robots.txt".source = ./robots.txt; services = { haproxy = { enable = true; config = builtins.readFile ./haproxy.cfg; }; nginx = { enable = true; virtualHosts."acme-challenge" = { listen = [ { addr = "127.0.0.1"; port = 8402; } ]; locations."/.well-known/acme-challenge/" = { root = "/var/lib/acme/.challenges"; }; }; }; }; security.acme = { acceptTerms = true; defaults.email = "Richie@tmmworkshop.com"; certs = builtins.listToAttrs ((map makeCert domains) ++ (map makeExtraCert extraDomains)); }; systemd = { services.haproxy = { # HAProxy needs the initial certificates and Tailscale connectivity before # it can serve requests to its backends. after = acmeServices ++ [ "tailscaled-autoconnect.service" ]; wants = acmeServices ++ [ "tailscaled-autoconnect.service" ]; }; 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 = { haproxy.extraGroups = [ "acme" ]; nginx.extraGroups = [ "acme" ]; }; }