Skip to main content

nebula-mesh CVE-2026-55513

| EUVDEUVD-2026-71719 MEDIUM
Insufficient Session Expiration (CWE-613)
2026-07-14 https://github.com/forgekeep/nebula-mesh GHSA-g4x6-jcvr-9m3g
5.4
CVSS 3.1 · Vendor: https://github.com/forgekeep/nebula-mesh
Share

Severity by source

Vendor (https://github.com/forgekeep/nebula-mesh) PRIMARY
5.4 MEDIUM
AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
vuln.today AI
5.4 MEDIUM

Network-accessible Web UI requires authenticated operator session (PR:L); no scope change; limited C/I from extended enrollment token validity enabling out-of-policy host enrollment.

3.1 AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N
SUSE
MEDIUM
qualitative

Primary rating from Vendor (https://github.com/forgekeep/nebula-mesh).

CVSS VectorVendor: https://github.com/forgekeep/nebula-mesh

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
Low
Availability
None

Lifecycle Timeline

3
Source Code Evidence Fetched
Jul 22, 2026 - 12:37 vuln.today
Analysis Generated
Jul 22, 2026 - 12:37 vuln.today
CVE Published
Jul 14, 2026 - 20:26 github-advisory
MEDIUM 5.4

DescriptionCVE.org

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.

AnalysisAI

Authenticated operator access in nebula-mesh v0.3.0-v0.3.8 allows bypassing the configured enrollment token lifetime policy: the Web UI host-creation path (POST /ui/hosts) hardcodes a 24-hour bearer token expiry in internal/web/handlers.go:879, silently ignoring both server-wide and per-network enrollment_token_ttl settings that the API sibling path (internal/api/hosts.go:190-196) correctly honors via the tokenTTLFor resolver. Enrollment tokens are bearer credentials for the public POST /api/v1/enroll endpoint; a token with a longer-than-intended TTL widens the window in which a copied, logged, or leaked token can enroll a pending Nebula host and retrieve its signed certificate and configuration. Publicly available exploit code (a reproducible Go unit test) confirms the bypass on v0.3.8; no public exploit identified at time of analysis beyond this PoC; a vendor-released patch exists in v0.5.0.

Technical ContextAI

The affected package is github.com/forgekeep/nebula-mesh (CPE: pkg:go/github.com_forgekeep_nebula-mesh), a Go-based management server ('nebula-mgmt') for Nebula overlay networks. Enrollment tokens are single-use bearer credentials that authorize the public POST /api/v1/enroll endpoint; possession of a valid token causes the server to return a signed Nebula certificate and network configuration for the pending host. CWE-613 (Insufficient Session Expiration) describes the root cause class: the Web UI handler at internal/web/handlers.go:879 sets ExpiresAt: now.Add(24 * time.Hour) unconditionally, bypassing the tokenTTLFor() resolver defined in internal/api/server.go:77 that applies per-network enrollment_token_ttl overrides, then the server-level default, then a 24-hour fallback. The fix in v0.5.0 (commit 514006029e09f1991122b86a80e7b25970bcfa98) extracts this resolution logic into a shared internal/enrollment/ttl.go package (enrollment.TokenTTL()), wires both the API and Web UI through it, and adds regression tests; the CLI serve path (internal/cli/serve.go:214) is also updated to pass the configured TTL to the Web UI server via WithEnrollmentTokenTTL().

RemediationAI

Upgrade to nebula-mesh v0.5.0, which is the vendor-released patch confirmed by the GitHub release at https://github.com/forgekeep/nebula-mesh/releases/tag/v0.5.0 and fix commit https://github.com/forgekeep/nebula-mesh/commit/514006029e09f1991122b86a80e7b25970bcfa98. The fix refactors TTL resolution into the shared internal/enrollment/ttl.go package and wires both the API and Web UI host-creation paths through enrollment.TokenTTL(), eliminating the Web UI hardcode. If immediate upgrade is not possible, the primary compensating control is to restrict Web UI operator access so that only fully trusted personnel can create hosts via POST /ui/hosts - this reduces the population of users who can mint the over-long tokens, but does not eliminate the policy bypass for those who retain access. A secondary control is to use only the API path (POST /api/v1/hosts) for host creation and restrict browser access to the Web UI host-creation form at the reverse-proxy or firewall level; note this trades operational convenience for policy enforcement and does not help if operators use both paths. Neither workaround resolves the root cause; upgrade to v0.5.0 is the definitive fix.

Vendor StatusVendor

SUSE

Severity: Moderate
Product Status
SUSE Linux Enterprise Server 16.1 Affected
SUSE Linux Enterprise Server for SAP applications 16.1 Affected
SUSE Linux Enterprise Module for Package Hub 15 SP5 Affected
SUSE Linux Enterprise Module for Package Hub 15 SP6 Affected
openSUSE Leap 15.5 Affected

Share

CVE-2026-55513 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy