Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ When enabled, bearer token protection secures **all data APIs**, including:
- Functions (`/api/v3/function`, `/api/v3/functions`)
- Dynamic configuration (`/api/v3/config`)

It also changes local MCP behavior: MCP requests over HTTP, SSE, and WebSocket require the local MCP API key when bearer token protection is enabled.

## What Remains Public

**Static web files** (HTML, CSS, JavaScript) in Netdata's web directory are **not protected**. This means:
Expand All @@ -86,6 +88,15 @@ These APIs are required for the authentication flow and dashboard initialization

**Note:** Other v1 and v2 APIs (like `/api/v2/info`, `/api/v3/versions`, `/api/v3/progress`) **are protected** by bearer token - only the specific endpoints listed above bypass protection.

## MCP Behavior with Bearer Protection

For local Agent/Parent MCP endpoints (`/mcp`, `/sse`, MCP WebSocket):

- If `[web].bearer token protection = yes`: anonymous MCP requests are rejected, and clients must pass the local MCP API key (`Authorization: Bearer <mcp_key>`).
- If `[web].bearer token protection = no`: anonymous MCP requests continue to work with the default open-source access model (the MCP API key is still supported and unlocks sensitive operations).

MCP network exposure is still controlled independently by `[web].allow mcp from`.

## Requirements

- Agent must be claimed to Netdata Cloud
Expand Down
3 changes: 3 additions & 0 deletions docs/netdata-agent/securing-netdata-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,15 @@ While `allow connections from` globally controls access to all Netdata services,
[web]
allow connections from = localhost *
allow dashboard from = localhost *
allow mcp from = localhost *
allow badges from = *
allow streaming from = *
allow netdata.conf from = localhost fd* 10.* 192.168.* 172.16.* 172.17.* 172.18.* 172.19.* 172.20.* 172.21.* 172.22.* 172.23.* 172.24.* 172.25.* 172.26.* 172.27.* 172.28.* 172.29.* 172.30.* 172.31.*
allow management from = localhost
```

When `[web].bearer token protection = yes` is enabled, MCP requests also require the local MCP API key (`Authorization: Bearer <mcp_key>`). Without the key, anonymous MCP requests are rejected.

**Additional Security Options:**

- Review detailed access list options in the [Web Server documentation](/src/web/server/README.md#access-lists)
Expand Down
28 changes: 25 additions & 3 deletions docs/netdata-ai/mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ AI assistants have different visibility depending on where they connect:
| **Scope** | All nodes | Single agent/parent |
| **Endpoint** | `app.netdata.cloud/api/v1/mcp` | `YOUR_IP:19999/mcp` |
| **Transport** | Streamable HTTP | HTTP, SSE, WebSocket |
| **Authentication** | Cloud API token (`scope:mcp`) | Local MCP API key |
| **Authentication** | Cloud API token (`scope:mcp`) | Local MCP API key (mandatory when bearer protection is enabled) |
| **Network access** | Internet only | Direct access to Netdata IP required |
| **Local setup** | None | Bridge may be needed for some clients |

Expand Down Expand Up @@ -177,7 +177,10 @@ Replace `YOUR_NETDATA_CLOUD_API_TOKEN` with your

Connect directly to any Netdata Agent or Parent on your network. All Netdata Agents and Parents (v2.6.0+) include a built-in MCP server at `http://YOUR_IP:19999/mcp`.

Some MCP features — such as live process information, network connections, and full log access — are considered sensitive. These are protected by a local API key that Netdata generates automatically on startup. Without this key, AI assistants can still access metrics, alerts, and node information, but sensitive functions remain locked.
Netdata generates a local MCP API key automatically on startup. Authentication behavior depends on `[web].bearer token protection`:

- **Bearer protection disabled (`no`)**: anonymous MCP access works for non-sensitive operations (metrics, alerts, node info). The API key unlocks sensitive operations.
- **Bearer protection enabled (`yes`)**: anonymous MCP access is rejected on all MCP transports (HTTP, SSE, WebSocket). The API key is required for all MCP requests.

### Transport Options

Expand All @@ -191,6 +194,25 @@ Netdata implements the MCP protocol with multiple transport options:

- **Direct Connection** (v2.7.2+): AI clients that support HTTP or SSE transports can connect directly to Netdata
- **Bridge Required**: AI clients that only support stdio need the `nd-mcp` (stdio-to-websocket) or `mcp-remote` (stdio-to-http or stdio-to-sse) bridge
- **Preferred authentication**: pass the MCP API key via `Authorization: Bearer <mcp_key>`.
- **Backward compatibility**: WebSocket also accepts `?api_key=<mcp_key>`. Keep this only for legacy clients, since URL query strings are more likely to be logged by proxies and tools.

### MCP Access Control in `netdata.conf`

Use a dedicated MCP ACL to control network-level MCP access independently from dashboard and streaming:

```ini
[web]
allow connections from = 10.* 192.168.* localhost
allow dashboard from = 10.* 192.168.* localhost
allow mcp from = 10.* 192.168.* localhost
allow streaming from = *
allow mcp by dns = heuristic
```

- `allow mcp from` controls `/mcp`, `/sse`, and MCP WebSocket protocol access.
- `allow connections from` is still the global first gate for all features.
- For internet-facing Parents, keep `allow mcp from` restricted to trusted networks.

#### Official MCP Remote Client (mcp-remote)

Expand Down Expand Up @@ -485,7 +507,7 @@ For more details, see the [official mcp-remote documentation](https://github.com

### Finding Your API Key

To access sensitive functions like logs and live system information, you need an API key. Netdata automatically generates an API key on startup. The key is stored in a file on the Netdata server you want to connect to.
You need the MCP API key when bearer protection is enabled. When bearer protection is disabled, the key is still needed for sensitive functions like logs and live system information. Netdata automatically generates an API key on startup. The key is stored in a file on the Netdata server you want to connect to.

You need the API key of the Netdata you will connect to (usually a Netdata Parent).

Expand Down
2 changes: 1 addition & 1 deletion docs/netdata-ai/mcp/mcp-clients/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Claude Code supports multiple MCP transport types, giving you flexibility in how
- **v2.6.0 - v2.7.1**: Only WebSocket transport available, requires `nd-mcp` bridge
- **v2.7.2+**: Direct HTTP/SSE support available (recommended)
3. **For WebSocket or stdio connections: `nd-mcp` bridge** - The stdio-to-websocket bridge. [Find its absolute path](/docs/netdata-ai/mcp/README.md#finding-the-nd-mcp-bridge). Not needed for direct HTTP connections on v2.7.2+.
4. **Optionally, the Netdata MCP API key** that unlocks full access to sensitive observability data (protected functions, full access to logs) on your Netdata. Each Netdata Agent or Parent has its own unique API key for MCP - [Find your Netdata MCP API key](/docs/netdata-ai/mcp/README.md#finding-your-api-key)
4. **Netdata MCP API key** - required when `[web].bearer token protection = yes`. When bearer protection is disabled, it is optional and unlocks sensitive observability data (protected functions, full access to logs). Each Netdata Agent or Parent has its own unique MCP API key - [Find your Netdata MCP API key](/docs/netdata-ai/mcp/README.md#finding-your-api-key)

## Configuration Methods

Expand Down
2 changes: 1 addition & 1 deletion docs/netdata-ai/mcp/mcp-clients/codex-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Codex CLI supports both stdio launchers and direct Streamable HTTP when the RMCP
- **v2.6.0 - v2.7.1**: Only WebSocket transport available, requires `nd-mcp` bridge
- **v2.7.2+**: Can use `npx mcp-remote` bridge for HTTP/SSE support
3. **Launcher** – Run Netdata through `nd-mcp` (always) or `npx mcp-remote` (useful when you want a single stdio launcher for multiple MCP clients). Direct HTTP is also available for v2.7.2+ when you enable the RMCP client.
4. **Optionally, the Netdata MCP API key** that unlocks full access to sensitive observability data (protected functions, full access to logs) on your Netdata. Each Netdata Agent or Parent has its own unique API key for MCP - [Find your Netdata MCP API key](/docs/netdata-ai/mcp/README.md#finding-your-api-key)
4. **Netdata MCP API key** - required when `[web].bearer token protection = yes`. When bearer protection is disabled, it is optional and unlocks sensitive observability data (protected functions, full access to logs). Each Netdata Agent or Parent has its own unique MCP API key - [Find your Netdata MCP API key](/docs/netdata-ai/mcp/README.md#finding-your-api-key)

## Installation

Expand Down
2 changes: 1 addition & 1 deletion docs/netdata-ai/mcp/mcp-clients/crush.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Crush has comprehensive MCP transport support, making it highly flexible for con
- **v2.6.0 - v2.7.1**: Only WebSocket transport available, requires `nd-mcp` bridge
- **v2.7.2+**: Direct HTTP/SSE support available (recommended)
3. **For WebSocket or stdio connections: `nd-mcp` bridge** - The stdio-to-websocket bridge. [Find its absolute path](/docs/netdata-ai/mcp/README.md#finding-the-nd-mcp-bridge). Not needed for direct HTTP/SSE connections on v2.7.2+.
4. **Optionally, the Netdata MCP API key** that unlocks full access to sensitive observability data (protected functions, full access to logs) on your Netdata. Each Netdata Agent or Parent has its own unique API key for MCP - [Find your Netdata MCP API key](/docs/netdata-ai/mcp/README.md#finding-your-api-key)
4. **Netdata MCP API key** - required when `[web].bearer token protection = yes`. When bearer protection is disabled, it is optional and unlocks sensitive observability data (protected functions, full access to logs). Each Netdata Agent or Parent has its own unique MCP API key - [Find your Netdata MCP API key](/docs/netdata-ai/mcp/README.md#finding-your-api-key)

> Export `ND_MCP_BEARER_TOKEN` with your MCP key before launching Crush so credentials never appear in command-line arguments or config files:
> ```bash
Expand Down
2 changes: 1 addition & 1 deletion docs/netdata-ai/mcp/mcp-clients/gemini-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Gemini CLI supports all major MCP transport types, giving you maximum flexibilit
- **v2.6.0 - v2.7.1**: Only WebSocket transport available, requires `nd-mcp` bridge
- **v2.7.2+**: Direct HTTP/SSE support available (recommended)
3. **For WebSocket or stdio connections: `nd-mcp` bridge** - The stdio-to-websocket bridge. [Find its absolute path](/docs/netdata-ai/mcp/README.md#finding-the-nd-mcp-bridge). Not needed for direct HTTP/SSE connections on v2.7.2+.
4. **Optionally, the Netdata MCP API key** that unlocks full access to sensitive observability data (protected functions, full access to logs) on your Netdata. Each Netdata Agent or Parent has its own unique API key for MCP - [Find your Netdata MCP API key](/docs/netdata-ai/mcp/README.md#finding-your-api-key)
4. **Netdata MCP API key** - required when `[web].bearer token protection = yes`. When bearer protection is disabled, it is optional and unlocks sensitive observability data (protected functions, full access to logs). Each Netdata Agent or Parent has its own unique MCP API key - [Find your Netdata MCP API key](/docs/netdata-ai/mcp/README.md#finding-your-api-key)

## Installation

Expand Down
2 changes: 1 addition & 1 deletion docs/netdata-ai/mcp/mcp-clients/opencode.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ OpenCode supports both local and remote MCP servers:
- **v2.6.0 - v2.7.1**: Only WebSocket transport available, requires `nd-mcp` bridge
- **v2.7.2+**: Direct HTTP/SSE support available (recommended)
3. **For WebSocket or stdio connections: `nd-mcp` bridge** - The stdio-to-websocket bridge. [Find its absolute path](/docs/netdata-ai/mcp/README.md#finding-the-nd-mcp-bridge). Not needed for direct HTTP connections on v2.7.2+.
4. **Optionally, the Netdata MCP API key** that unlocks full access to sensitive observability data (protected functions, full access to logs) on your Netdata. Each Netdata Agent or Parent has its own unique API key for MCP - [Find your Netdata MCP API key](/docs/netdata-ai/mcp/README.md#finding-your-api-key)
4. **Netdata MCP API key** - required when `[web].bearer token protection = yes`. When bearer protection is disabled, it is optional and unlocks sensitive observability data (protected functions, full access to logs). Each Netdata Agent or Parent has its own unique MCP API key - [Find your Netdata MCP API key](/docs/netdata-ai/mcp/README.md#finding-your-api-key)

> Export `ND_MCP_BEARER_TOKEN` with your MCP key before launching OpenCode to keep secrets out of configuration files:
> ```bash
Expand Down
2 changes: 2 additions & 0 deletions docs/netdata-oss-limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ When accessing Netdata via Agent/Parent MCP:

- **Without Cloud connection**: MCP can access public functions and metrics, but sensitive functions follow the same restrictions as the dashboard
- **With Cloud connection**: MCP inherits the user's Cloud permissions, enabling access to sensitive functions for authenticated users
- **With `[web].bearer token protection = yes`**: local MCP requires the local MCP API key on all transports (HTTP, SSE, WebSocket); anonymous MCP requests are rejected
- **Network ACL**: local MCP exposure is controlled by `[web].allow mcp from` (in addition to the global `[web].allow connections from`)

For MCP setup and configuration, see the [MCP documentation](/docs/netdata-ai/mcp/README.md).

Expand Down
10 changes: 8 additions & 2 deletions src/daemon/config/netdata-conf-web.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,17 @@ void netdata_conf_section_web(void) {
NULL, SIMPLE_PATTERN_EXACT, true);
web_allow_connections_dns =
make_dns_decision(CONFIG_SECTION_WEB, "allow connections by dns", "heuristic", web_allow_connections_from);
const char *allow_dashboard_default =
inicfg_get(&netdata_config, CONFIG_SECTION_WEB, "allow dashboard from", "localhost *");
web_allow_dashboard_from =
simple_pattern_create(inicfg_get(&netdata_config, CONFIG_SECTION_WEB, "allow dashboard from", "localhost *"),
NULL, SIMPLE_PATTERN_EXACT, true);
simple_pattern_create(allow_dashboard_default, NULL, SIMPLE_PATTERN_EXACT, true);
web_allow_dashboard_dns =
make_dns_decision(CONFIG_SECTION_WEB, "allow dashboard by dns", "heuristic", web_allow_dashboard_from);
web_allow_mcp_from =
simple_pattern_create(inicfg_get(&netdata_config, CONFIG_SECTION_WEB, "allow mcp from", allow_dashboard_default),
NULL, SIMPLE_PATTERN_EXACT, true);
web_allow_mcp_dns =
make_dns_decision(CONFIG_SECTION_WEB, "allow mcp by dns", "heuristic", web_allow_mcp_from);
web_allow_badges_from =
simple_pattern_create(inicfg_get(&netdata_config, CONFIG_SECTION_WEB, "allow badges from", "*"), NULL, SIMPLE_PATTERN_EXACT,
true);
Expand Down
33 changes: 17 additions & 16 deletions src/go/pkg/l2topology/l2_pipeline_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,22 +93,23 @@ func (s *l2BuildState) buildResult(identityAliasStats identityAliasReconcileStat
collectedAt = collectedAt.UTC()
}

stats := newL2ResultStats()
stats["devices_total"] = len(s.devices)
stats["links_total"] = len(s.adjacencies)
stats["links_lldp"] = s.linksLLDP
stats["links_cdp"] = s.linksCDP
stats["links_stp"] = s.linksSTP
stats["attachments_total"] = len(s.attachments)
stats["attachments_fdb"] = s.attachmentsFDB
stats["enrichments_total"] = len(s.enrichments)
stats["enrichments_arp_nd"] = s.enrichmentsARPND
stats["bridge_domains_total"] = len(s.bridgeDomains)
stats["endpoints_total"] = len(s.endpointIDs)
stats["identity_alias_endpoints_mapped"] = identityAliasStats.endpointsMapped
stats["identity_alias_endpoints_ambiguous_mac"] = identityAliasStats.endpointsAmbiguousMAC
stats["identity_alias_ips_merged"] = identityAliasStats.ipsMerged
stats["identity_alias_ips_conflict_skipped"] = identityAliasStats.ipsConflictSkipped
stats := ResultStats{
DevicesTotal: len(s.devices),
LinksTotal: len(s.adjacencies),
LinksLLDP: s.linksLLDP,
LinksCDP: s.linksCDP,
LinksSTP: s.linksSTP,
AttachmentsTotal: len(s.attachments),
AttachmentsFDB: s.attachmentsFDB,
EnrichmentsTotal: len(s.enrichments),
EnrichmentsARPND: s.enrichmentsARPND,
BridgeDomainsTotal: len(s.bridgeDomains),
EndpointsTotal: len(s.endpointIDs),
IdentityAliasEndpointsMapped: identityAliasStats.endpointsMapped,
IdentityAliasEndpointsAmbiguousMAC: identityAliasStats.endpointsAmbiguousMAC,
IdentityAliasIPsMerged: identityAliasStats.ipsMerged,
IdentityAliasIPsConflictSkipped: identityAliasStats.ipsConflictSkipped,
}

return Result{
CollectedAt: collectedAt,
Expand Down
53 changes: 35 additions & 18 deletions src/go/pkg/l2topology/l2_pipeline_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,39 @@

package l2topology

func newL2ResultStats() map[string]any {
return map[string]any{
"devices_total": 0,
"links_total": 0,
"links_lldp": 0,
"links_cdp": 0,
"links_stp": 0,
"attachments_total": 0,
"attachments_fdb": 0,
"enrichments_total": 0,
"enrichments_arp_nd": 0,
"bridge_domains_total": 0,
"endpoints_total": 0,
"identity_alias_endpoints_mapped": 0,
"identity_alias_endpoints_ambiguous_mac": 0,
"identity_alias_ips_merged": 0,
"identity_alias_ips_conflict_skipped": 0,
}
type ResultStats struct {
DevicesTotal int
LinksTotal int
LinksLLDP int
LinksCDP int
LinksSTP int
AttachmentsTotal int
AttachmentsFDB int
EnrichmentsTotal int
EnrichmentsARPND int
BridgeDomainsTotal int
EndpointsTotal int
IdentityAliasEndpointsMapped int
IdentityAliasEndpointsAmbiguousMAC int
IdentityAliasIPsMerged int
IdentityAliasIPsConflictSkipped int
}

type ProjectionStats struct {
ResultStats

DevicesDiscovered int
LinksBidirectional int
LinksUnidirectional int
LinksFDB int
LinksFDBEndpointCandidates int
LinksFDBEndpointEmitted int
LinksFDBEndpointSuppressed int
EndpointsAmbiguousSegments int
LinksARP int
LinksProbable int
SegmentsSuppressed int
ActorsTotal int
ActorsUnlinkedSuppressed int
InferenceStrategy string
}
Loading
Loading