Fixed fatal error 'concurrent map iteration and map write' that caused
nginx-ui nodes to crash and become unresponsive.
The issue occurred when the sitecheck CollectSites() method iterated over
site.IndexedSites while the cache scanner's scanForSite() was concurrently
modifying the same map. This race condition caused sporadic crashes.
Solution:
- Added GetAllIndexedSites() function in internal/site/index.go that safely
returns a snapshot copy of the IndexedSites map while holding the read lock
- Modified CollectSites() in internal/sitecheck/checker.go to use this
thread-safe function instead of directly accessing the global map
Fixes#1673
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* feat(cert): Optimize certificate application operations. #1602
* chore(lang): Add language context for new DNS application
* chore(lang): Add language context for new DNS application
Locks in the v2.3.5 origin-validation fix for CVE-2026-34403 / GHSA-78mf-482w-62qj
with named regression cases for every bypass class documented in the advisory:
subdomain confusion, suffix confusion, scheme downgrade, port mismatch, default-
port normalization, ws/wss scheme equivalence, case-insensitive host, IPv6 literal,
RFC 7239 Forwarded parsing, multi-valued X-Forwarded-Host, scheme-only / malformed
origin rejection, node_secret query fallback, empty-secret regression, trailing-
slash tolerance on configured trusted origins.
17 table-driven subtests in a new file; zero production code changes; no new
dependencies.
Co-authored-by: Panguard AI <support@panguard.ai>
The site checker created a fresh http.Transport per request and per
EnhancedSiteChecker, with Go's default Happy-Eyeballs dialer. When
server_name entries resolved to ingress services returning many A
records (ngrok, AWS ALB, Cloudflare), each sweep opened enough flows
to exhaust conntrack tables on consumer routers (UniFi).
Introduce a package-level shared http.Transport with MaxConnsPerHost=2,
MaxIdleConnsPerHost=2 and FallbackDelay=-1 (disables IPv6 dial races),
plumb it through SiteChecker and EnhancedSiteChecker, and only build a
custom client when the per-site HealthCheckConfig truly diverges on
TLS. Reuse the response body fetched by the health check for favicon
extraction so each site is hit at most once per sweep, and dedupe sites
sharing the same host:port before fan-out.
Add a [site_check] settings section (Enabled, Concurrency, Interval-
Seconds) so operators can disable the checker entirely or tune the
sweep cadence; clamp Concurrency to [1, 20] and IntervalSeconds to
>=30. Document the new section in en, zh_CN and zh_TW guides and add
sidebar entries.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The site healthcheck built its request URL from the indexed site URL
(e.g. http://example.com) and never rewrote the scheme to match the
user-configured HealthCheckConfig.Protocol. As a result, sites
configured for HTTPS were probed over HTTP and always shown as
unreachable. TestHealthCheck compounded the issue by using
siteConfig.Scheme (default "http") instead of req.Config.Protocol.
Introduce rewriteCheckURLScheme which aligns only the URL scheme with
the configured protocol while preserving path, query, and port, and
call it from CheckSiteWithConfig. TestHealthCheck now passes the stored
site URL and relies on the same rewrite, so the "Test" button exercises
the same code path as the scheduled checker.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`ObtainCert.job()` called `issueCert()` synchronously after `step.value++`,
before Vue mounted `<ObtainCertLive>`, so `refObtainCertLive.value` was
null and the optional-chain call silently no-oped — no log entry, no
WebSocket connection, progress stuck at 0%. Add an `await nextTick()`
so the live component is mounted before its method is invoked.
Also harden the long-token WebSocket fallback: switch the frontend to
URL-safe base64 (avoids `+` being decoded as a space in query strings)
and accept both URL-safe and standard base64 in `getTokenWS` for
backward compatibility.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Introduced unique key generation for locations and directives using WeakMap to improve item identification in draggable lists.
- Updated the `item-key` binding in `LocationEditor.vue` and `DirectiveEditor.vue` to utilize the new key generation functions.
- Refactored the NgxConfig store to include a reset function, ensuring a clean state for ngxConfig when needed.
- Enhanced the `SiteAdd.vue` component to reset the ngxConfigStore during initialization.
- Added tests to verify backup and restore processes when the crypto secret changes, ensuring hash verification passes.
- Updated `writeManifestFiles` and `verifyBackupManifest` functions to accept an AES key for improved security.
- Implemented fallback mechanism for verifying manifest signatures using both AES-derived and legacy signing keys.
- Enhanced the overall robustness of the backup and restore system to handle legacy signatures and different crypto secrets.
* fix(mcp): prevent panic on nil interface conversion for tool arguments
Add safe argument extraction helper functions in internal/mcp/args.go
that handle nil values gracefully instead of panicking on direct type
assertions.
This fixes the issue where MCP config tools panic with:
'interface conversion: interface {} is nil, not string'
when called via Claude Desktop with protocol version 2025-11-25.
Affected tools:
- nginx_config_list
- nginx_config_get
- nginx_config_add
- nginx_config_modify
- nginx_config_rename
- nginx_config_mkdir
- nginx_config_history
- nginx_config_enable
Fixes #36ec
Co-authored-by: Jacky <me@jackyu.cn>
* Add required argument validation to MCP config handlers to prevent data loss
The safe argument extraction helpers (mcp.GetString, etc.) return zero values
for nil/missing arguments, which could cause silent data loss. This adds
explicit validation for required arguments in:
- config_modify: validate relative_path and content
- config_add: validate name and content
- config_rename: validate orig_name and new_name
- config_mkdir: validate folder_name
This follows the same pattern already used in handleNginxConfigEnable.
* Add required argument validation to config_get and config_history handlers
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
- Introduced SafeWebSocketWriter to ensure thread-safe writes to WebSocket connections.
- Updated WebSocket handling in certificate issuance, revocation, Nginx log tailing, and system upgrades to use the new writer.
- Enhanced WebSocket client management in the site navigation module for improved message handling and connection stability.
- Added `InitTokenRouter` to define the `/token/short` endpoint for issuing short tokens.
- Created `IssueShortToken` function to handle short token generation and response.
- Updated WebSocket middleware to require short token for authentication, preventing CSWSH attacks.
- Modified user store and login handling to integrate short token functionality.
- Enhanced documentation to reflect changes in WebSocket security requirements.