CVE-2026-55513
mediumpriority · track · 0EPSS 0.003 · 25 %ileUSEPSS · scored 2026-10-27
nebula-mesh is a self-hosted control plane for Slack Nebula mesh VPN. From version 0.3.0 to before version 0.5.0, the nebula-mgmt Web UI host-creation path ignores both the server-wide enrollment_token_ttl security setting and per-network network_config.enrollment_token_ttl overrides. API host creation and token-regeneration paths use the configured TTL resolver, but POST /ui/hosts hardcodes now.Add(24 * time.Hour) for newly minted agent enrollment tokens. In deployments that intentionally reduce enrollment-token lifetime, any authenticated operator who can create a host through the Web UI can still mint a bearer enrollment token valid for about 24 hours. This issue has been patched in version 0.5.0.
cvss
Technical severity, 0–10. One row per scoring source.
base score 0–10 · higher = more severe · hover a vector to decode
weaknesses
The CWE weakness classes this flaw belongs to.
affected products (3)
Products and version ranges known to be affected.
advisories (2)
Vendor and ecosystem advisories describing this.
- GHSA-g4x6-jcvr-9m3gnebula-mesh: Web UI host creation ignores configured enrollment token TTL and mints 24-hour bearer enrollment tokens2026-07-14ghsa · osvUS
### Summary The `nebula-mgmt` Web UI host-creation path ignores both the server-wide `enrollment_token_ttl` security setting and per-network `network_config.enrollment_token_ttl` overrides. API host creation and token-regeneration paths use the configured TTL resolver, but `POST /ui/hosts` hardcodes `now.Add(24 * time.Hour)` for newly minted agent enrollment tokens. In deployments that intentionally reduce enrollment-token lifetime, any authenticated operator who can create a host through the Web UI can still mint a bearer enrollment token valid for about 24 hours. ### Details Enrollment tokens are bearer credentials for the public `POST /api/v1/enroll` endpoint: possession of a valid token allows enrolling the pending host and receiving a signed Nebula certificate/config for that host. The server configuration documents a security knob for their default lifetime and per-network overrides: - `internal/config/server.go:82` defines `EnrollmentTokenTTL` as the default lifetime for freshly minted enrollment tokens. - `internal/config/server.go:83` documents per-network overrides in `network_config` under `enrollment_token_ttl`. The API server implements and consistently uses this resolver: - `internal/api/server.go:77` defines `tokenTTLFor`, with precedence of per-network `enrollment_token_ttl`, then server default, then 24h fallback. - `internal/api/server.go:82` reads `network_config.enrollment_token_ttl`. - `internal/api/server.go:89` falls back to the configured server default. - `internal/api/hosts.go:190` through `internal/api/hosts.go:196` use `now.Add(s.tokenTTLFor(r.Context(), host.NetworkID))` for API host creation. The Web UI sibling path does not call the resolver and instead always sets a 24-hour expiry: - `internal/web/handlers.go:874` mints the raw token for `POST /ui/hosts`. - `internal/web/handlers.go:879` sets `ExpiresAt: now.Add(24 * time.Hour)`. This creates inconsistent behavior between API and Web UI host creation and bypasses an operator-configured token lifetime policy. The issue is reachable by an authenticated Web UI operator who can create hosts. Admins can create hosts in any network; non-admin operators can create hosts in networks whose CA they own. Affected version evidence: the configurable enrollment-token TTL feature was introduced by commit `6c344a6` (`feat(api): configurable enrollment-token TTL + regenerate endpoint (#75) (#79)`), and `git tag --contains 6c344a6 --sort=version:refname` returns `v0.3.0` through `v0.3.8`. Pattern checks across all release tags showed the TTL config/API resolver and the Web UI 24-hour hardcode are present in every `v0.3.x` release from `v0.3.0` to `v0.3.8`, and are not meaningfully applicable to `v0.1.x`/`v0.2.0` because the TTL policy knob was not present there. The current checkout at commit `d92dd9a60de291e2bc1caf73b4e9a99567b31ec0` (`git describe`: `v0.3.8-1-gd92dd9a`) remains affected. ### PoC Safe local PoC run from a clean checkout at commit `d92dd9a60de291e2bc1caf73b4e9a99567b31ec0` on 2026-06-12. The PoC is a temporary Go test that uses only in-memory SQLite and `httptest`; it does not start a real server and does not contact external services. 1. Create a temporary test file `internal/web/security_audit_poc_test.go` in package `web`. 2. In the test, create an in-memory Web UI with `newTestWeb(t)`, create a network `audit-poc-net` with CIDR `10.77.0.0/24`, and set `network_config.enrollment_token_ttl` to `30m`. 3. Log in as the seeded test admin through the normal Web UI helper and obtain a CSRF token from `GET /ui/hosts/new`. 4. Submit `POST /ui/hosts` with `network_id=audit-poc-net`, `name=audit-poc-host`, `nebula_ips=10.77.0.10`, `role=host`, and `kind=agent`. 5. Parse the one-shot enrollment token from the returned host-detail page and read the token row with `GetEnrollmentToken`. 6. Compare the observed expiry to the configured 30-minute network override. Command run: ```bash go test ./internal/web -run 'TestSecurityAuditPOC' -count=1 -v ``` Observed vulnerable output from this environment: ```text === RUN TestSecurityAuditPOC_UIHostCreateIgnoresNetworkEnrollmentTokenTTL POC_UI_TTL_BYPASS observed_token_ttl=24h0m0s configured_network_ttl=30m expires_at=2026-06-13T14:51:45Z --- PASS: TestSecurityAuditPOC_UIHostCreateIgnoresNetworkEnrollmentTokenTTL (0.05s) ``` The meaningful control is the API sibling: `internal/api/hosts.go:190` through `internal/api/hosts.go:196` uses `s.tokenTTLFor(...)`, and existing tests in `internal/api/hosts_token_ttl_test.go` verify API-created/regenerated enrollment tokens honor server-default and per-network TTLs. Variant review also found API regenerate-token, API re-enroll, and signed-poll rekey token minting use the resolver rather than a hardcoded 24h value. After recording the output, the temporary test file was removed and `git status --short` returned clean. The PoC was re-run after drafting this report and produced the output shown above. ### Impact An authenticated Web UI operator can bypass a configured enrollment-token lifetime policy and obtain a token valid for approximately 24 hours even when the deployment or network is configured for a much shorter lifetime such as 30 minutes. Because enrollment tokens are bearer credentials for the public enrollment endpoint, longer-than-intended validity increases the window in which a copied, logged, shared, or otherwise exposed token can be used to enroll the pending host and obtain its Nebula certificate/config. This weakens confidentiality and integrity for deployments relying on short token lifetimes to reduce enrollment-token exposure. Suggested remediation: refactor the Web UI host-creation path to use the same TTL resolution as the API path, or move the resolver into a shared package/service used by both API and Web UI. Add a regression test under `internal/web` that sets `network_config.enrollment_token_ttl = "30m"`, creates an agent host through `POST /ui/hosts`, and asserts the persisted enrollment token expires within the configured 30-minute window rather than 24 hours.
- GO-2026-5987Insecure enrollment token TTL in nebula-mesh in github.com/forgekeep/nebula-mesh2026-07-23vendor · osvUS
The nebula-mesh Web UI host creation ignores the configured enrollment token TTL and instead mints 24-hour bearer enrollment tokens. This can lead to tokens remaining valid for longer than intended, increasing the window of opportunity for an attacker to use an intercepted token.
also known as (2)
Other names and identifiers for this entity.
- GHSA-g4x6-jcvr-9m3gosv
- GO-2026-5987osv
sources
Which sources assert this record, and when.
references (3) nvd
External citations — patches, advisories, analysis.
Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.
Free scan