CVE-2026-55513: nebula-mesh Web UI Host Creation Ignores Vulnerability

MODERATE
Advisory Identifier: GHSA-g4x6-jcvr-9m3g
CVSS 5.4
CWE-613
Published
Advisory Summary

CVE-2026-55513 is a MODERATE (CVSS 5.4) web ui host creation ignores vulnerability (CWE-613: Insufficient Session Expiration) affecting nebula-mesh libraries in the Go ecosystem. The issue involves web ui host creation ignores configured enrollment token ttl and mints 24-hour bearer enrollment tokens. Remediation is available by upgrading affected packages to version 0.5.0.

CVE-2026-55513 at a Glance

CVE Identifier CVE-2026-55513
GHSA Identifier GHSA-g4x6-jcvr-9m3g
Severity MODERATE
CVSS v3.1 Base Score 5.4
CWE Classification CWE-613: Insufficient Session Expiration
Affected Product nebula-mesh
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 5.4

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality Low (C:L)
Integrity Low (I:L)
Availability None (A:N)

How to Fix CVE-2026-55513

To remediate this vulnerability, upgrade all affected nebula-mesh dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/forgekeep/nebula-mesh fixed: 0.5.0
Upgrade github.com/forgekeep/nebula-mesh to 0.5.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/forgekeep/nebula-mesh

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-55513, inspect your project dependencies for nebula-mesh:

inspect dependencies for github.com/forgekeep/nebula-mesh

Analyze the terminal output to locate any version of nebula-mesh in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.5.0), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-613: Insufficient Session Expiration, where according to wasc, "insufficient session expiration is when a web site permits an attacker to reuse old session credentials or session ids for authorization.". In nebula-mesh, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### 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.

CVE-2026-55513 Affected nebula-mesh Versions

1 component(s)
Fixed Versions:
0.5.0
Product / Package Ecosystem Affected Versions Fixed Version
github.com/forgekeep/nebula-mesh
Go
≥ 0.3.0
< 0.5.0
0.5.0

References

Frequently Asked Questions

What is CVE-2026-55513?
CVE-2026-55513 is a security vulnerability in nebula-mesh involving Web UI host creation ignores configured enrollment token TTL and mints 24-hour bearer enrollment tokens under CWE-613: Insufficient Session Expiration.
Which nebula-mesh versions are affected?
Affected versions include releases prior to 0.5.0 across affected packages: github.com/forgekeep/nebula-mesh.
What version fixes CVE-2026-55513?
Upgrading to 0.5.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/forgekeep/nebula-mesh affected?
Yes, github.com/forgekeep/nebula-mesh is affected in releases prior to 0.5.0. Upgrade your dependency to 0.5.0 or later.
What is the CVSS score of CVE-2026-55513?
CVE-2026-55513 has a CVSS v3.1 base score of 5.4 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N.
What CWE is associated with CVE-2026-55513?
CVE-2026-55513 is classified under CWE-613: Insufficient Session Expiration (According to WASC, "Insufficient Session Expiration is when a web site permits an attacker to reuse old session credentials or session IDs for authorization.").
How can I check Go dependencies for CVE-2026-55513?
Run the dependency tree command: 'inspect dependencies for github.com/forgekeep/nebula-mesh'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-g4x6-jcvr-9m3g",
  "modified" : "2026-07-14T20:26:21Z",
  "published" : "2026-07-14T20:26:21Z",
  "aliases" : [ "CVE-2026-55513" ],
  "summary" : "nebula-mesh: Web UI host creation ignores configured enrollment token TTL and mints 24-hour bearer enrollment tokens",
  "details" : "### Summary\nThe `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.\n\n### Details\nEnrollment 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:\n\n- `internal/config/server.go:82` defines `EnrollmentTokenTTL` as the default lifetime for freshly minted enrollment tokens.\n- `internal/config/server.go:83` documents per-network overrides in `network_config` under `enrollment_token_ttl`.\n\nThe API server implements and consistently uses this resolver:\n\n- `internal/api/server.go:77` defines `tokenTTLFor`, with precedence of per-network `enrollment_token_ttl`, then server default, then 24h fallback.\n- `internal/api/server.go:82` reads `network_config.enrollment_token_ttl`.\n- `internal/api/server.go:89` falls back to the configured server default.\n- `internal/api/hosts.go:190` through `internal/api/hosts.go:196` use `now.Add(s.tokenTTLFor(r.Context(), host.NetworkID))` for API host creation.\n\nThe Web UI sibling path does not call the resolver and instead always sets a 24-hour expiry:\n\n- `internal/web/handlers.go:874` mints the raw token for `POST /ui/hosts`.\n- `internal/web/handlers.go:879` sets `ExpiresAt: now.Add(24 * time.Hour)`.\n\nThis 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.\n\nAffected 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.\n\n### PoC\nSafe 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.\n\n1. Create a temporary test file `internal/web/security_audit_poc_test.go` in package `web`.\n2. 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`.\n3. Log in as the seeded test admin through the normal Web UI helper and obtain a CSRF token from `GET /ui/hosts/new`.\n4. 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`.\n5. Parse the one-shot enrollment token from the returned host-detail page and read the token row with `GetEnrollmentToken`.\n6. Compare the observed expiry to the configured 30-minute network override.\n\nCommand run:\n\n```bash\ngo test ./internal/web -run 'TestSecurityAuditPOC' -count=1 -v\n```\n\nObserved vulnerable output from this environment:\n\n```text\n=== RUN   TestSecurityAuditPOC_UIHostCreateIgnoresNetworkEnrollmentTokenTTL\nPOC_UI_TTL_BYPASS observed_token_ttl=24h0m0s configured_network_ttl=30m expires_at=2026-06-13T14:51:45Z\n--- PASS: TestSecurityAuditPOC_UIHostCreateIgnoresNetworkEnrollmentTokenTTL (0.05s)\n```\n\nThe 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.\n\n### Impact\nAn 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.\n\nSuggested 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.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/forgekeep/nebula-mesh"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0.3.0"
      }, {
        "fixed" : "0.5.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/forgekeep/nebula-mesh/security/advisories/GHSA-g4x6-jcvr-9m3g"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/forgekeep/nebula-mesh/commit/514006029e09f1991122b86a80e7b25970bcfa98"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/forgekeep/nebula-mesh"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/forgekeep/nebula-mesh/releases/tag/v0.5.0"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-613" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-14T20:26:21Z",
    "nvd_published_at" : null
  }
}