From 66928072293bf73596690d578ec6f3be6675ebe9 Mon Sep 17 00:00:00 2001 From: Bryan Ramos Date: Mon, 13 Apr 2026 15:57:27 -0400 Subject: [PATCH] updated --- .sops.yaml | 5 ++ secrets/system/searxng.yaml | 16 ++++++ .../machines/server/modules/nginx/default.nix | 55 ++++++++++++++++++- .../server/modules/wireguard/default.nix | 1 + .../server/modules/wstunnel/default.nix | 37 +++++++++++++ system/machines/server/system.nix | 29 +++++++++- 6 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 secrets/system/searxng.yaml create mode 100644 system/machines/server/modules/wstunnel/default.nix diff --git a/.sops.yaml b/.sops.yaml index 831afe6..fdad7d3 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -17,3 +17,8 @@ creation_rules: key_groups: - age: - *server + # Server secrets (searxng) + - path_regex: secrets/system/searxng\.yaml$ + key_groups: + - age: + - *server diff --git a/secrets/system/searxng.yaml b/secrets/system/searxng.yaml new file mode 100644 index 0000000..2834056 --- /dev/null +++ b/secrets/system/searxng.yaml @@ -0,0 +1,16 @@ +SEARXNG_TOKEN: ENC[AES256_GCM,data:6hI9+Gk9D7OjgcNV7WHUkcT8Kzta+QbJ8bq5uDv1AU/n1lpD/41RSWAZ91v5f0VSAldKvDMIuRdjxmKaE0ITOA==,iv:LURC0t6YwectCMllBBx8TIGxM80vXS84pkvczmWtO6U=,tag:ZSRbU7B+LKsLw8R7Yi9uzg==,type:str] +sops: + age: + - recipient: age198jg29ryg3c0qj3yg6y9ha4ce2ue4hjdaa9kalf49fxju74dhchsquvjzp + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNWkRjc0xWR3o5ZFlnUXRY + bnNKUWhNL3dFcnJ0a0tLZHNuRU1obEwzam44CjczSEEvdVUyUXN5MEpsYnA5Y3NQ + M2N6VXZpM3ZxMHd0ZWV1MG5qT3ZnZzgKLS0tIGN3UG01eW4xZ1A4bkF3TzNKVkdv + eE9uRmpId2R5VTJSeDhRVUkvSWt4RHMK/oXVHDAWN5SY/4hPCm0QsTo2ubBD+uBf + fOZr/4HNDOyq8AIfbRVbilC7l/Ozg8snu8chRo1keCjqHp+Pt+Yzhw== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-04-13T19:21:23Z" + mac: ENC[AES256_GCM,data:qxBRF1wSTXeFEvjs+5HiRmk6wqt1Rtx1kNFigpqicfd/IJsZTJY/6g3SmZXrJNkpkYwyOBNlblGfLAfKMWm6/eg1KYBJHlSAAqkH/xECdDqKY0rTkMj3rfPTZzLGmi4kVp6v8jg9OO5SwK8sLGtbaK2S/VjdTI0NXlMMnsB30Sg=,iv:GacmqywEsxCyKQKmCPu42uyqy6Q0JhR7STDAMFvW7kQ=,tag:G1Lbu6DKuO5yLWs6kXjwHA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.12.1 diff --git a/system/machines/server/modules/nginx/default.nix b/system/machines/server/modules/nginx/default.nix index ca4fbf0..e423815 100644 --- a/system/machines/server/modules/nginx/default.nix +++ b/system/machines/server/modules/nginx/default.nix @@ -21,6 +21,8 @@ in CIDR ranges allowed to access private vhosts (LAN + WireGuard). ''; }; + + searxng.enable = mkEnableOption "Publicly exposed SearXNG endpoint with secret path via sops"; }; config = mkIf cfg.enable { @@ -28,6 +30,36 @@ in systemd.services.nginx.serviceConfig.LimitNOFILE = 65536; + environment.etc."fail2ban/filter.d/nginx-404.conf".text = '' + [Definition] + failregex = ^ - .+ "(GET|POST|HEAD|PUT|DELETE|PATCH) .+ HTTP/[0-9.]+" 404 + ignoreregex = + ''; + + environment.etc."fail2ban/filter.d/nginx-401.conf".text = '' + [Definition] + failregex = ^ - .+ "(GET|POST|HEAD|PUT|DELETE|PATCH) .+ HTTP/[0-9.]+" 401 + ignoreregex = + ''; + + services.fail2ban.jails.nginx-404 = '' + enabled = true + filter = nginx-404 + logpath = /var/log/nginx/access.log + maxretry = 10 + findtime = 10m + bantime = 24h + ''; + + services.fail2ban.jails.nginx-401 = '' + enabled = true + filter = nginx-401 + logpath = /var/log/nginx/access.log + maxretry = 5 + findtime = 10m + bantime = 24h + ''; + security.acme = { acceptTerms = true; defaults.email = config.user.email; @@ -71,6 +103,28 @@ in }; }; + virtualHosts."wg.${domain}" = { + useACMEHost = domain; + forceSSL = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString config.modules.system.wstunnel.listenPort}"; + proxyWebsockets = true; + extraConfig = '' + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + ''; + }; + }; + + virtualHosts."searxng.${domain}" = mkIf cfg.searxng.enable { + useACMEHost = domain; + forceSSL = true; + locations."/".return = "404"; + extraConfig = '' + include ${config.sops.templates."nginx-searxng-location.conf".path}; + ''; + }; + virtualHosts."chat.${domain}" = { useACMEHost = domain; forceSSL = true; @@ -87,7 +141,6 @@ in locations."/" = { proxyPass = "http://192.168.0.23:8000"; proxyWebsockets = true; - extraConfig = privateAccessRules; }; }; diff --git a/system/machines/server/modules/wireguard/default.nix b/system/machines/server/modules/wireguard/default.nix index ff3f516..04c6324 100644 --- a/system/machines/server/modules/wireguard/default.nix +++ b/system/machines/server/modules/wireguard/default.nix @@ -65,6 +65,7 @@ in config = mkIf cfg.enable { networking.firewall.allowedUDPPorts = [ cfg.listenPort ]; + networking.firewall.trustedInterfaces = [ "wg0" ]; networking.nat.internalInterfaces = mkAfter [ "wg0" ]; systemd.tmpfiles.rules = [ diff --git a/system/machines/server/modules/wstunnel/default.nix b/system/machines/server/modules/wstunnel/default.nix new file mode 100644 index 0000000..035f65b --- /dev/null +++ b/system/machines/server/modules/wstunnel/default.nix @@ -0,0 +1,37 @@ +{ pkgs, lib, config, ... }: + +with lib; +let + cfg = config.modules.system.wstunnel; +in +{ + options.modules.system.wstunnel = { + enable = mkEnableOption "wstunnel WebSocket transport for WireGuard"; + + listenPort = mkOption { + type = types.port; + default = 8080; + description = "Local port wstunnel server listens on (nginx proxies to this)"; + }; + + wireguardPort = mkOption { + type = types.port; + default = 51820; + description = "Local WireGuard port to forward traffic to"; + }; + }; + + config = mkIf cfg.enable { + systemd.services.wstunnel = { + description = "wstunnel WebSocket server for WireGuard transport"; + after = [ "network.target" "wireguard-wg0.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${pkgs.wstunnel}/bin/wstunnel server ws://127.0.0.1:${toString cfg.listenPort} --restrict-to 127.0.0.1:${toString cfg.wireguardPort}"; + Restart = "on-failure"; + RestartSec = 5; + DynamicUser = true; + }; + }; + }; +} diff --git a/system/machines/server/system.nix b/system/machines/server/system.nix index 9c9e639..b8674dd 100644 --- a/system/machines/server/system.nix +++ b/system/machines/server/system.nix @@ -7,18 +7,38 @@ modules.system.sops.enable = true; # Camera RTSP credentials (used by frigate/go2rtc) - sops.secrets = let cameras = { sopsFile = ../../../secrets/system/cameras.yaml; }; in { + sops.secrets = let + cameras = { sopsFile = ../../../secrets/system/cameras.yaml; }; + searxng = { sopsFile = ../../../secrets/system/searxng.yaml; }; + in { "RTSP_USER" = cameras; "RTSP_PASS" = cameras; + "SEARXNG_TOKEN" = searxng; + }; + + sops.templates."nginx-searxng-location.conf" = { + content = '' + location /${config.sops.placeholder."SEARXNG_TOKEN"}/ { + proxy_pass http://192.168.0.23:8080/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + ''; + owner = "nginx"; }; modules.system = { - nginx.enable = true; + nginx = { + enable = true; + searxng.enable = true; + }; sandpack.enable = true; forgejo.enable = true; frigate.enable = true; immich.enable = true; webdav.enable = false; + wstunnel.enable = true; wireguard = { enable = true; peers = [ @@ -207,6 +227,11 @@ enable = true; maxretry = 5; bantime = "1h"; + ignoreIP = [ + "127.0.0.1/8" + "192.168.0.0/24" + "10.8.0.0/24" + ]; }; services.openssh = {