From 482c4e300ef8d1e1fb1863157e3e717b3ae0e0a2 Mon Sep 17 00:00:00 2001 From: archipelago Date: Fri, 7 Aug 2026 16:54:38 -0400 Subject: [PATCH] fix(bootstrap): self-heal stale /aiui/api/web-search proxy to the gated daemon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live on archi-dev-box: the node still proxied web-search straight to SearXNG :8888 unauthenticated — the repo conf was fixed in d0c9ea6e but existing nodes' /etc/nginx/sites-enabled never gets rewritten by a source edit. Added to the nginx self-heal battery: stale 8888/search proxy_pass → session-gated 5678 with the Cookie forwarded (heal_stale_web_search_block, pure + idempotent + tested). Fresh ISOs already ship the gated block. Co-Authored-By: Claude --- core/archipelago/src/bootstrap.rs | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/core/archipelago/src/bootstrap.rs b/core/archipelago/src/bootstrap.rs index 5a4c7b59..4e7643a2 100644 --- a/core/archipelago/src/bootstrap.rs +++ b/core/archipelago/src/bootstrap.rs @@ -1055,6 +1055,22 @@ async fn run_nginx() -> Result { /// rejected. Stripped during nginx bootstrap so the backend solely owns CORS. const NGINX_LND_DUP_CORS: &str = " add_header Access-Control-Allow-Origin $http_origin always;\n add_header Access-Control-Allow-Credentials \"true\" always;\n"; +/// S4 follow-up (2026-08-07): pre-fix nodes proxy /aiui/api/web-search +/// STRAIGHT to SearXNG (127.0.0.1:8888) with no session check — anyone on the +/// LAN can run searches attributed to the node's IP. The canonical conf routes +/// it through the session-gated daemon (5678) and forwards the Cookie. The +/// stale target string is unique to that block, so a plain replace is safe. +/// Pure and testable; `None` when the stale shape is absent. +fn heal_stale_web_search_block(content: &str) -> Option { + if !content.contains("proxy_pass http://127.0.0.1:8888/search;") { + return None; + } + Some(content.replace( + "proxy_pass http://127.0.0.1:8888/search;\n proxy_http_version 1.1;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;", + "proxy_pass http://127.0.0.1:5678;\n proxy_http_version 1.1;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header Cookie $http_cookie;", + )) +} + async fn patch_nginx_conf(path: &str) -> Result { let content = fs::read_to_string(path) .await @@ -1084,6 +1100,7 @@ async fn patch_nginx_conf(path: &str) -> Result { content.contains("listen 80 default_server;") && !content.contains("listen [::]:80"); let missing_v6_https = content.contains("listen 443 ssl default_server;") && !content.contains("listen [::]:443"); + let stale_web_search = heal_stale_web_search_block(&content).is_some(); if !missing_app_catalog && !missing_bitcoin_status && !missing_lnd_proxy @@ -1093,12 +1110,18 @@ async fn patch_nginx_conf(path: &str) -> Result { && !needs_fedimint_css && !missing_v6_http && !missing_v6_https + && !stale_web_search { return Ok(false); } let mut patched = content.clone(); + if let Some(p) = heal_stale_web_search_block(&patched) { + patched = p; + } + + if missing_v6_http { patched = patched.replace( "listen 80 default_server;", @@ -1270,6 +1293,19 @@ mod tests { let outcome = PodmanHealOutcome::Unhealthy; assert_ne!(outcome, PodmanHealOutcome::Healthy); } + + #[test] + fn stale_web_search_block_is_gated_and_idempotent() { + let stale = " location /aiui/api/web-search {\n proxy_pass http://127.0.0.1:8888/search;\n proxy_http_version 1.1;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_connect_timeout 30s;\n }"; + let healed = heal_stale_web_search_block(stale).expect("stale block must heal"); + assert!(healed.contains("proxy_pass http://127.0.0.1:5678;")); + assert!(healed.contains("proxy_set_header Cookie $http_cookie;")); + assert!(!healed.contains("8888")); + // Second pass is a no-op (idempotent self-heal). + assert!(heal_stale_web_search_block(&healed).is_none()); + // A config without the block is untouched. + assert!(heal_stale_web_search_block("location / { try_files $uri /index.html; }").is_none()); + } } /// Repair this node's own systemd restart policy.