From 358f2296ce00c8f39a8b955dba19a71596d03d79 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Tue, 14 Apr 2026 10:46:15 +0200 Subject: [PATCH] fix: nas wake up on traffic --- hosts/fw/modules/nas-wake-on-access.nix | 63 +++++++++++++++++++++++++ hosts/fw/modules/web/proxies.nix | 33 +++++++++++++ 2 files changed, 96 insertions(+) diff --git a/hosts/fw/modules/nas-wake-on-access.nix b/hosts/fw/modules/nas-wake-on-access.nix index 7b6cea9..e150e57 100644 --- a/hosts/fw/modules/nas-wake-on-access.nix +++ b/hosts/fw/modules/nas-wake-on-access.nix @@ -89,6 +89,47 @@ let date +%s > "${lastSeenFile}" fi ''; + + fwIp = "${config.networkPrefix}.97.1"; + + nasWakeHtml = pkgs.writeText "nas-wake.html" '' + + + + + + Waking up NAS... + + + +
+

NAS is waking up…

+

A wake-on-LAN packet has been sent.
This page will refresh automatically in 15 seconds.

+
+ + + ''; + + nasWakeHttpScript = pkgs.writeShellScript "nas-wake-http" '' + # Trigger WOL (reuses cooldown/holdoff from wakeScript) + ${wakeScript} >&2 || true + + BODY=$(cat ${nasWakeHtml}) + LENGTH=''${#BODY} + + printf "HTTP/1.1 503 Service Unavailable\r\n" + printf "Content-Type: text/html; charset=utf-8\r\n" + printf "Content-Length: %d\r\n" "$LENGTH" + printf "Retry-After: 15\r\n" + printf "Connection: close\r\n" + printf "\r\n" + printf "%s" "$BODY" + ''; in { systemd.services.nas-wake-journal = { @@ -130,4 +171,26 @@ in AccuracySec = "1s"; }; }; + + # Allow web-02 (bridged to server) to reach the wake HTTP endpoint + networking.firewall.interfaces."server".allowedTCPPorts = [ 9800 ]; + + # HTTP endpoint for nginx error_page → WOL trigger. + # When nginx on web-arm gets a 502/504 from a NAS-proxied vhost, it + # proxies the request here. We send WOL and return a "waking up" page. + systemd.services.nas-wake-http = { + description = "HTTP endpoint to wake NAS on reverse-proxy failure"; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + path = with pkgs; [ coreutils ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.socat}/bin/socat TCP-LISTEN:9800,bind=${fwIp},reuseaddr,fork EXEC:${nasWakeHttpScript}"; + Restart = "always"; + RestartSec = "5s"; + RuntimeDirectory = "nas-wake-on-access"; + RuntimeDirectoryPreserve = "yes"; + }; + }; } diff --git a/hosts/fw/modules/web/proxies.nix b/hosts/fw/modules/web/proxies.nix index 421ea1a..4a45bf3 100644 --- a/hosts/fw/modules/web/proxies.nix +++ b/hosts/fw/modules/web/proxies.nix @@ -1,4 +1,16 @@ { config, lib, ... }: { + # Catch-all default server: drop connections from bots/scanners hitting + # by IP or unknown Host header. Without this, the alphabetically first + # vhost (audiobooks) becomes the implicit default — and its @nas_wake + # error handler wakes the NAS on every random internet probe. + services.nginx.virtualHosts."_" = { + default = true; + rejectSSL = true; + extraConfig = '' + return 444; + ''; + }; + services.nginx.virtualHosts."git.cloonar.com" = { forceSSL = true; enableACME = true; @@ -45,12 +57,17 @@ allow ${config.networkPrefix}.97.0/24; allow ${config.networkPrefix}.98.0/24; deny all; + proxy_connect_timeout 3s; + error_page 502 504 = @nas_wake; ''; locations."/" = { proxyPass = "http://${config.networkPrefix}.97.11:8000"; proxyWebsockets = true; }; + locations."@nas_wake" = { + proxyPass = "http://${config.networkPrefix}.97.1:9800"; + }; }; services.nginx.virtualHosts."jellyfin.cloonar.com" = { @@ -58,6 +75,11 @@ enableACME = true; acmeRoot = null; + extraConfig = '' + proxy_connect_timeout 3s; + error_page 502 504 = @nas_wake; + ''; + locations."/" = { proxyPass = "http://${config.networkPrefix}.97.11:8096"; proxyWebsockets = true; @@ -73,6 +95,9 @@ proxy_buffering off; ''; }; + locations."@nas_wake" = { + proxyPass = "http://${config.networkPrefix}.97.1:9800"; + }; }; services.nginx.virtualHosts."audiobooks.cloonar.com" = { @@ -80,6 +105,11 @@ enableACME = true; acmeRoot = null; + extraConfig = '' + proxy_connect_timeout 3s; + error_page 502 504 = @nas_wake; + ''; + locations."/" = { proxyPass = "http://${config.networkPrefix}.97.11:13378"; proxyWebsockets = true; @@ -94,6 +124,9 @@ proxy_buffering off; ''; }; + locations."@nas_wake" = { + proxyPass = "http://${config.networkPrefix}.97.1:9800"; + }; }; services.nginx.virtualHosts."moltbot.cloonar.com" = {