From 33a7017375fff96388d3c2e266ee9bba446f0d1f Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Mon, 4 Aug 2025 21:18:59 +0200 Subject: [PATCH] convert caddy to nixos builtin --- nix/apps/does-it-build/default.nix | 10 ++ nix/apps/forgejo/default.nix | 8 ++ nix/apps/hugo-chat/default.nix | 60 ++++++++ nix/apps/old-redirects/default.nix | 41 ++++++ nix/apps/openolat/default.nix | 18 +++ nix/apps/upload-files/default.nix | 11 ++ nix/apps/website/default.nix | 34 +++++ nix/hive.nix | 5 + nix/modules/caddy/base.Caddyfile | 59 -------- nix/modules/caddy/default.nix | 132 +++++++----------- nix/modules/caddy/vps1.Caddyfile | 111 --------------- nix/modules/caddy/vps3.Caddyfile | 5 - nix/modules/caddy/vps4.Caddyfile | 5 - nix/modules/prometheus/default.nix | 8 ++ nix/modules/registry/default.nix | 7 + nix/my-projects.json | 2 +- nix/packages/caddy-static-prepare/default.nix | 13 ++ nix/packages/caddy-static-prepare/prepare.py | 60 ++++++++ 18 files changed, 326 insertions(+), 263 deletions(-) create mode 100644 nix/apps/old-redirects/default.nix create mode 100644 nix/apps/website/default.nix delete mode 100644 nix/modules/caddy/base.Caddyfile delete mode 100644 nix/modules/caddy/vps1.Caddyfile delete mode 100644 nix/modules/caddy/vps3.Caddyfile delete mode 100644 nix/modules/caddy/vps4.Caddyfile create mode 100644 nix/packages/caddy-static-prepare/default.nix create mode 100644 nix/packages/caddy-static-prepare/prepare.py diff --git a/nix/apps/does-it-build/default.nix b/nix/apps/does-it-build/default.nix index fad6023..b7053de 100644 --- a/nix/apps/does-it-build/default.nix +++ b/nix/apps/does-it-build/default.nix @@ -6,6 +6,16 @@ let }); in { + services.caddy.virtualHosts = { + "does-it-build.noratrieb.dev" = { + logFormat = ""; + extraConfig = '' + encode zstd gzip + reverse_proxy * localhost:3000 + ''; + }; + }; + systemd.services.does-it-build = { description = "https://github.com/Noratrieb/does-it-build"; wantedBy = [ "multi-user.target" ]; diff --git a/nix/apps/forgejo/default.nix b/nix/apps/forgejo/default.nix index 933fe24..bf6a407 100644 --- a/nix/apps/forgejo/default.nix +++ b/nix/apps/forgejo/default.nix @@ -43,6 +43,14 @@ }; }; + services.caddy.virtualHosts."git.noratrieb.dev" = { + logFormat = ""; + extraConfig = '' + encode zstd gzip + reverse_proxy * localhost:5015 + ''; + }; + services.custom-backup.jobs = [{ app = "forgejo"; file = "/var/lib/forgejo/data/forgejo.db"; diff --git a/nix/apps/hugo-chat/default.nix b/nix/apps/hugo-chat/default.nix index 9f78f6b..14d89c9 100644 --- a/nix/apps/hugo-chat/default.nix +++ b/nix/apps/hugo-chat/default.nix @@ -5,6 +5,11 @@ let "https://github.com/C0RR1T/HugoChat/releases/download/2024-08-05/HugoServer.jar"; hash = "sha256-hCe2UPqrSR6u3/UxsURI2KzRxN5saeTteCRq5Zfay4M="; }; + hugo-chat-client = fetchTarball { + url = + "https://github.com/C0RR1T/HugoChat/releases/download/2024-08-05/hugo-client.tar.xz"; + sha256 = "sha256:121ai8q6bm7gp0pl1ajfk0k2nrfg05zid61i20z0j5gpb2qyhsib"; + }; in { age.secrets.hugochat_db_password.file = ../../secrets/hugochat_db_password.age; @@ -36,6 +41,61 @@ in }; }; + services.caddy.virtualHosts = { + "hugo-chat.noratrieb.dev" = { + logFormat = ""; + extraConfig = '' + encode zstd gzip + root * ${import ../../packages/caddy-static-prepare { + name = "hugo-chat-client"; + src = hugo-chat-client; + inherit pkgs lib; + }} + try_files {path} /index.html + file_server { + etag_file_extensions .sha256 + precompressed zstd gzip br + } + ''; + }; + "api.hugo-chat.noratrieb.dev" = + let + cors = pkgs.writeText "cors" '' + # https://gist.github.com/ryanburnette/d13575c9ced201e73f8169d3a793c1a3 + @cors_preflight{args[0]} method OPTIONS + @cors{args[0]} header Origin {args[0]} + + handle @cors_preflight{args[0]} { + header { + Access-Control-Allow-Origin "{args[0]}" + Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" + Access-Control-Allow-Credentials "false" + Access-Control-Allow-Headers "$${args[1]}" + Access-Control-Max-Age "86400" + defer + } + respond "" 204 + } + + handle @cors{args[0]} { + header { + Access-Control-Allow-Origin "{args[0]}" + Access-Control-Expose-Headers * + defer + } + } + ''; + in + { + logFormat = ""; + extraConfig = '' + import ${cors} https://hugo-chat.noratrieb.dev "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type" + encode zstd gzip + reverse_proxy * localhost:5001 + ''; + }; + }; + services.custom-backup.jobs = [ { app = "hugo-chat"; diff --git a/nix/apps/old-redirects/default.nix b/nix/apps/old-redirects/default.nix new file mode 100644 index 0000000..68d3f42 --- /dev/null +++ b/nix/apps/old-redirects/default.nix @@ -0,0 +1,41 @@ +{ ... }: +let + permanent = [ + { from = "www.noratrieb.dev"; to = "noratrieb.dev"; } + { from = "blog.noratrieb.dev"; to = "noratrieb.dev/blog"; } + { from = "nilstrieb.dev"; to = "noratrieb.dev"; } + { from = "www.nilstrieb.dev"; to = "noratrieb.dev"; } + { from = "blog.nilstrieb.dev"; to = "noratrieb.dev/blog"; } + { from = "bisect-rustc.nilstrieb.dev"; to = "bisect-rustc.noratrieb.dev"; } + { from = "docker.nilstrieb.dev"; to = "docker.noratrieb.dev"; } + { from = "hugo-chat.nilstrieb.dev"; to = "hugo-chat.noratrieb.dev"; } + { from = "api.hugo-chat.nilstrieb.dev"; to = "api.hugo-chat.noratrieb.dev"; } + { from = "uptime.nilstrieb.dev"; to = "uptime.noratrieb.dev"; } + { from = "olat.nilstrieb.dev"; to = "olat.noratrieb.dev"; } + { from = "olat.nilstrieb.dev:8088"; to = "olat.noratrieb.dev"; } + ]; +in +{ + services.caddy.virtualHosts = ( + { + "bisect-rustc.noratrieb.dev" = { + logFormat = ""; + extraConfig = "redir https://github.com/Noratrieb/cargo-bisect-rustc-service?tab=readme-ov-file#cargo-bisect-rustc-service"; + }; + "uptime.noratrieb.dev" = { + logFormat = ""; + extraConfig = "redir https://github.com/Noratrieb/uptime?tab=readme-ov-file#uptime"; + }; + } + ) // ( + builtins.listToAttrs (map + (redirect: { + name = redirect.from; + value = { + logFormat = ""; + extraConfig = "redir https://${redirect.to}{uri} permanent"; + }; + }) + permanent) + ); +} diff --git a/nix/apps/openolat/default.nix b/nix/apps/openolat/default.nix index 0c2672f..6ad5c68 100644 --- a/nix/apps/openolat/default.nix +++ b/nix/apps/openolat/default.nix @@ -44,6 +44,24 @@ in }; }; + services.caddy.virtualHosts = { + "olat.noratrieb.dev" = { + logFormat = ""; + extraConfig = '' + encode zstd gzip + reverse_proxy * localhost:5011 + ''; + }; + # unsure if necessary... something was misconfigured in the past here... + "olat.noratrieb.dev:8088" = { + logFormat = ""; + extraConfig = '' + encode zstd gzip + reverse_proxy * localhost:5011 + ''; + }; + }; + services.custom-backup.jobs = [ { app = "openolat-db"; diff --git a/nix/apps/upload-files/default.nix b/nix/apps/upload-files/default.nix index 526adf3..4cd5ff5 100644 --- a/nix/apps/upload-files/default.nix +++ b/nix/apps/upload-files/default.nix @@ -18,4 +18,15 @@ let upload-files = import (fetchTarball "https://github.com/Noratrieb/upload.fil EnvironmentFile = [ config.age.secrets.upload_files_s3_secret.path ]; }; }; + + services.caddy.virtualHosts."upload.files.noratrieb.dev" = { + logFormat = ""; + extraConfig = '' + encode zstd gzip + # we need HTTP/2 here because the server doesn't work with HTTP/1.1 + # because it will send early 401 responses during the upload without consuming the body + # (this has been mostly fixed but still keep it) + reverse_proxy * h2c://localhost:3050 + ''; + }; } diff --git a/nix/apps/website/default.nix b/nix/apps/website/default.nix new file mode 100644 index 0000000..d67f7fe --- /dev/null +++ b/nix/apps/website/default.nix @@ -0,0 +1,34 @@ +{ pkgs, lib, my-projects-versions, ... }: +let + website = import (fetchTarball "https://github.com/Noratrieb/website/archive/${my-projects-versions.website}.tar.gz"); + blog = fetchTarball "https://github.com/Noratrieb/blog/archive/${my-projects-versions.blog}.tar.gz"; + slides = fetchTarball "https://github.com/Noratrieb/slides/archive/${my-projects-versions.slides}.tar.gz"; + website-build = website { inherit pkgs slides blog; }; +in +{ + services.caddy.virtualHosts = { + "noratrieb.dev" = { + logFormat = ""; + extraConfig = '' + encode zstd gzip + header -Last-Modified + root * ${import ../../packages/caddy-static-prepare { + name = "website"; + src = website-build; + inherit pkgs lib; + }} + file_server { + etag_file_extensions .sha256 + precompressed zstd gzip br + } + ''; + }; + "files.noratrieb.dev" = { + logFormat = ""; + extraConfig = '' + encode zstd gzip + reverse_proxy * localhost:3902 + ''; + }; + }; +} diff --git a/nix/hive.nix b/nix/hive.nix index 1d61ebc..a7b70b3 100644 --- a/nix/hive.nix +++ b/nix/hive.nix @@ -180,6 +180,8 @@ ./modules/backup # apps + ./apps/website + ./apps/old-redirects ./apps/widetom ./apps/hugo-chat ./apps/killua @@ -211,6 +213,8 @@ ./modules/caddy ./modules/garage ./modules/prometheus + + ./apps/website ]; system.stateVersion = "23.11"; @@ -225,6 +229,7 @@ ./modules/backup # apps + ./apps/website ./apps/does-it-build ]; diff --git a/nix/modules/caddy/base.Caddyfile b/nix/modules/caddy/base.Caddyfile deleted file mode 100644 index 69a626b..0000000 --- a/nix/modules/caddy/base.Caddyfile +++ /dev/null @@ -1,59 +0,0 @@ -{ - email noratrieb@proton.me - auto_https disable_redirects - - storage s3 { - host "localhost:3900" - bucket "caddy-store" - # access_id ENV S3_ACCESS_ID - # secret_key ENV S3_SECRET_KEY - - insecure true - } - - servers { - metrics - } - - log default { - output stdout - format json - } -} - -# https://gist.github.com/ryanburnette/d13575c9ced201e73f8169d3a793c1a3 -(cors) { - @cors_preflight{args[0]} method OPTIONS - @cors{args[0]} header Origin {args[0]} - - handle @cors_preflight{args[0]} { - header { - Access-Control-Allow-Origin "{args[0]}" - Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" - Access-Control-Allow-Credentials "false" - Access-Control-Allow-Headers "${args[1]}" - Access-Control-Max-Age "86400" - defer - } - respond "" 204 - } - - handle @cors{args[0]} { - header { - Access-Control-Allow-Origin "{args[0]}" - Access-Control-Expose-Headers * - defer - } - } -} - -http:// { - log - respond "This is an HTTPS-only server, silly you. Go to https:// instead." 418 -} - -# HTTP -:9010 { - log - metrics /metrics -} diff --git a/nix/modules/caddy/default.nix b/nix/modules/caddy/default.nix index c2f132f..f046d2c 100644 --- a/nix/modules/caddy/default.nix +++ b/nix/modules/caddy/default.nix @@ -1,4 +1,4 @@ -{ pkgs, config, lib, name, my-projects-versions, ... }: +{ pkgs, config, lib, name, ... }: let caddy = pkgs.callPackage ./caddy-build.nix { @@ -11,15 +11,6 @@ let ]; vendorHash = "sha256-KP9bYitM/Pocw4DxOXPVBigWh4IykNf8yKJiBlTFZmI="; }; - website = import (fetchTarball "https://github.com/Noratrieb/website/archive/${my-projects-versions.website}.tar.gz"); - blog = fetchTarball "https://github.com/Noratrieb/blog/archive/${my-projects-versions.blog}.tar.gz"; - slides = fetchTarball "https://github.com/Noratrieb/slides/archive/${my-projects-versions.slides}.tar.gz"; - website-build = website { inherit pkgs slides blog; }; - hugo-chat-client = fetchTarball { - url = - "https://github.com/C0RR1T/HugoChat/releases/download/2024-08-05/hugo-client.tar.xz"; - sha256 = "sha256:121ai8q6bm7gp0pl1ajfk0k2nrfg05zid61i20z0j5gpb2qyhsib"; - }; in { environment.systemPackages = [ caddy ]; @@ -43,79 +34,56 @@ in services.caddy = { enable = true; package = caddy; - configFile = pkgs.writeTextFile { - name = "Caddyfile"; - text = ( - builtins.readFile ./base.Caddyfile + - '' - ${config.networking.hostName}.infra.noratrieb.dev { - log - encode zstd gzip - header -Last-Modified - root * ${import ./caddy-static-prepare { - name = "debugging-page"; - src = ./debugging-page; - inherit pkgs lib; - }} - file_server { - etag_file_extensions .sha256 - precompressed zstd gzip br - } + logFormat = '' + output stdout + format json + ''; + globalConfig = '' + email noratrieb@proton.me + auto_https disable_redirects + + storage s3 { + host "localhost:3900" + bucket "caddy-store" + # access_id ENV S3_ACCESS_ID + # secret_key ENV S3_SECRET_KEY + + insecure true + } + + servers { + metrics + } + ''; + virtualHosts = { + "http://" = { + logFormat = ""; + extraConfig = '' + respond "This is an HTTPS-only server, silly you. Go to https:// instead." 418 + ''; + }; + ":9010" = { + logFormat = "output discard"; + extraConfig = '' + metrics /metrics + ''; + }; + "${name}.infra.noratrieb.dev" = { + logFormat = ""; + extraConfig = '' + encode zstd gzip + header -Last-Modified + root * ${import ./caddy-static-prepare { + name = "debugging-page"; + src = ./debugging-page; + inherit pkgs lib; + }} + file_server { + etag_file_extensions .sha256 + precompressed zstd gzip br } - - ${ - if name == "vps1" || name == "vps3" || name == "vps4" then '' - noratrieb.dev { - log - encode zstd gzip - header -Last-Modified - root * ${import ./caddy-static-prepare { - name = "website"; - src = website-build; - inherit pkgs lib; - }} - file_server { - etag_file_extensions .sha256 - precompressed zstd gzip br - } - } - - files.noratrieb.dev { - log - encode zstd gzip - - reverse_proxy * localhost:3902 - } - '' else "" - } - - ${if name == "vps1" then '' - hugo-chat.noratrieb.dev { - log - encode zstd gzip - root * ${import ./caddy-static-prepare { - name = "hugo-chat-client"; - src = hugo-chat-client; - inherit pkgs lib; - }} - try_files {path} /index.html - file_server { - etag_file_extensions .sha256 - precompressed zstd gzip br - } - } - '' else ""} - - ${ - if name == "vps1" || name == "vps3" || name == "vps4" then - builtins.readFile ./${name}.Caddyfile else "" - } - '' - ); - checkPhase = '' - ${lib.getExe caddy} --version - ${lib.getExe caddy} validate --adapter=caddyfile --config=$out - ''; + ''; + }; }; }; } diff --git a/nix/modules/caddy/vps1.Caddyfile b/nix/modules/caddy/vps1.Caddyfile deleted file mode 100644 index 11712b2..0000000 --- a/nix/modules/caddy/vps1.Caddyfile +++ /dev/null @@ -1,111 +0,0 @@ -www.noratrieb.dev { - log - redir https://noratrieb.dev{uri} permanent -} - -api.hugo-chat.noratrieb.dev { - log - import cors https://hugo-chat.noratrieb.dev "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type" - encode zstd gzip - reverse_proxy * localhost:5001 -} - -docker.noratrieb.dev { - log - reverse_proxy * localhost:5000 -} - -git.noratrieb.dev { - log - encode zstd gzip - reverse_proxy * localhost:5015 -} - -olat.noratrieb.dev { - log - encode zstd gzip - reverse_proxy * localhost:5011 -} - -# unsure if necessary... something was misconfigured in the past here... -olat.noratrieb.dev:8088 { - log - encode zstd gzip - reverse_proxy * localhost:5011 -} - -upload.files.noratrieb.dev { - log - encode zstd gzip - # we need HTTP/2 here because the server doesn't work with HTTP/1.1 - # because it will send early 401 responses during the upload without consuming the body - reverse_proxy * h2c://localhost:3050 -} - -################################################################ -# retired - -bisect-rustc.noratrieb.dev { - log - redir https://github.com/Noratrieb/cargo-bisect-rustc-service?tab=readme-ov-file#cargo-bisect-rustc-service -} - -uptime.noratrieb.dev { - log - redir https://github.com/Noratrieb/uptime?tab=readme-ov-file#uptime -} - -blog.noratrieb.dev { - log - redir https://noratrieb.dev/blog{uri} permanent -} - -nilstrieb.dev { - log - redir https://noratrieb.dev{uri} permanent -} - -www.nilstrieb.dev { - log - redir https://noratrieb.dev{uri} permanent -} - -blog.nilstrieb.dev { - log - redir https://noratrieb.dev/blog{uri} permanent -} - -bisect-rustc.nilstrieb.dev { - log - redir https://bisect-rustc.noratrieb.dev/blog{uri} permanent -} - -docker.nilstrieb.dev { - log - redir https://docker.noratrieb.dev{uri} permanent -} - -hugo-chat.nilstrieb.dev { - log - redir https://hugo-chat.noratrieb.dev{uri} permanent -} - -api.hugo-chat.nilstrieb.dev { - log - redir https://api.hugo-chat.noratrieb.dev{uri} permanent -} - -uptime.nilstrieb.dev { - log - redir https://uptime.noratrieb.dev{uri} permanent -} - -olat.nilstrieb.dev { - log - redir https://olat.noratrieb.dev{uri} permanent -} - -olat.nilstrieb.dev:8088 { - log - redir https://olat.noratrieb.dev{uri} permanent -} diff --git a/nix/modules/caddy/vps3.Caddyfile b/nix/modules/caddy/vps3.Caddyfile deleted file mode 100644 index d233f8d..0000000 --- a/nix/modules/caddy/vps3.Caddyfile +++ /dev/null @@ -1,5 +0,0 @@ -grafana.noratrieb.dev { - log - encode zstd gzip - reverse_proxy * localhost:3000 -} diff --git a/nix/modules/caddy/vps4.Caddyfile b/nix/modules/caddy/vps4.Caddyfile deleted file mode 100644 index 325daca..0000000 --- a/nix/modules/caddy/vps4.Caddyfile +++ /dev/null @@ -1,5 +0,0 @@ -does-it-build.noratrieb.dev { - log - encode zstd gzip - reverse_proxy * localhost:3000 -} diff --git a/nix/modules/prometheus/default.nix b/nix/modules/prometheus/default.nix index a1af908..705f15f 100644 --- a/nix/modules/prometheus/default.nix +++ b/nix/modules/prometheus/default.nix @@ -94,6 +94,14 @@ }; }; + services.caddy.virtualHosts."grafana.noratrieb.dev" = { + logFormat = ""; + extraConfig = '' + encode zstd gzip + reverse_proxy * localhost:3000 + ''; + }; + networking.firewall.interfaces.wg0.allowedTCPPorts = [ config.services.loki.configuration.server.http_listen_port 4040 # pyroscope diff --git a/nix/modules/registry/default.nix b/nix/modules/registry/default.nix index a18c67c..1cdde03 100644 --- a/nix/modules/registry/default.nix +++ b/nix/modules/registry/default.nix @@ -60,4 +60,11 @@ }; }; }; + + services.caddy.virtualHosts."docker.noratrieb.dev" = { + logFormat = ""; + extraConfig = '' + reverse_proxy * localhost:5000 + ''; + }; } diff --git a/nix/my-projects.json b/nix/my-projects.json index bf6a386..daebc68 100644 --- a/nix/my-projects.json +++ b/nix/my-projects.json @@ -5,7 +5,7 @@ "pretense": "270b01fc1118dfd713c1c41530d1a7d98f04527d", "quotdd": "e922229e1d9e055be35dabd112bafc87a0686548", "does-it-build": "81790825173d87f89656f66f12a123bc99e2f6f1", - "upload.files.noratrieb.dev": "0124fa5ba5446cb463fb6b3c4f52e7e6b84e5077", + "upload.files.noratrieb.dev": "9f31fe53f040f73edbbdc8afcc9bd3cdbc1cd8ab", "cluelessh": "c711cd405da4b7951e554577d09c9576bedf7970", "widetom": "33d1738799618d72fe2b86896f766cbfea58dc76" } diff --git a/nix/packages/caddy-static-prepare/default.nix b/nix/packages/caddy-static-prepare/default.nix new file mode 100644 index 0000000..da26ede --- /dev/null +++ b/nix/packages/caddy-static-prepare/default.nix @@ -0,0 +1,13 @@ +{ pkgs, lib, name, src ? null, ... }: pkgs.stdenv.mkDerivation { + inherit name src; + + buildInputs = with pkgs; [ python311 python311Packages.zstandard python311Packages.brotli ]; + + buildPhase = '' + mkdir -p $out + cp -r $src/* $out/ + chmod -R +w $out + ${lib.getExe pkgs.python311} ${./prepare.py} $out + chmod -R -w $out + ''; +} diff --git a/nix/packages/caddy-static-prepare/prepare.py b/nix/packages/caddy-static-prepare/prepare.py new file mode 100644 index 0000000..e87e3ac --- /dev/null +++ b/nix/packages/caddy-static-prepare/prepare.py @@ -0,0 +1,60 @@ +import os +import sys +import gzip +import brotli +import zstandard +import hashlib + + +def usage(): + print("usage: prepare.py [SRC]") + + +def write_etag(path, content): + shasum = hashlib.sha256(content) + etag_path = path+".sha256" + with open(etag_path, "w") as f: + print(f"Writing ETag {etag_path}") + f.write(f'"{shasum.hexdigest()}"') + + +def main(): + if len(sys.argv) < 2: + usage() + exit(1) + + src_dir = sys.argv[1] + + for root, dirs, files in os.walk(src_dir): + for file in files: + path = os.path.join(root, file) + + # Ignore etags + if path.endswith(".sha256") or path.endswith(".b3sum"): + continue + + # Ignore already compressed files + if path.endswith(".gz") or path.endswith(".zst") or path.endswith(".br"): + continue + + with open(path, "rb") as f: + content = f.read() + + compressions = [ + (".gz", gzip), + (".zst", zstandard), + (".br", brotli), + ] + + for ext, alg in compressions: + new_path = path+ext + with open(new_path, "wb") as out: + print(f"Writing {new_path}") + compressed = alg.compress(content) + out.write(compressed) + write_etag(new_path, compressed) + + write_etag(path, content) + +if __name__ == "__main__": + main() \ No newline at end of file