Commit Graph
221 Commits
Author SHA1 Message Date
Viktor Liu ea5cf822eb Merge main into poc/certificate-posture 2026-10-05 19:09:43 +02:00
Maycon Santos 19c54b8226 [management] Refresh only affected peers on DNS zone and record changes (#8050)
Zone and record changes refreshed every peer in the account, and zone
create/update passed the request context to the update goroutine, so it
could be cancelled when the handler returned. They now compute affected
peers from the zone's distribution groups inside the transaction and
dispatch through ExpandAndUpdateAffected, which detaches the context.

The resolver did not know about zones, so changing a group referenced only
by a zone never pushed the zone to its added or removed members. It now
folds the distribution groups of shipped zones on whole-group changes.
2026-10-05 12:48:53 +02:00
Maycon Santos 3c4358dd36 [management] Require a private proxy cluster for cluster and direct upstream targets (#7984)
* [management] Require a private proxy cluster for cluster and direct upstream targets

Cluster targets and direct upstream targets make the proxy dial the
upstream from its own host network instead of through the embedded
NetBird client. Only clusters running in private mode are meant to do
that, but the service API accepted these targets on any cluster.

Service create and update now reject such targets unless the service's
proxy cluster reports the private capability. An unreported capability
is treated as unsupported.

* [management] Require every proxy in the cluster to be private

The private capability is aggregated as any-true, so a cluster where
only one proxy runs in private mode passed the check. The mapping is
delivered to every proxy in the cluster, so the non-private ones would
serve cluster and direct upstream targets from their host network too.

Validate these targets against a unanimous aggregation instead. The
existing any-true lookup stays as is for the dashboard flags and the
agent network gateway.
2026-10-02 19:03:00 +02:00
Allan ELKAIM e2678d4e05 [management] expose peer MAC addresses and make peers searchable by MAC (#6553) 2026-10-02 15:00:26 +02:00
Brad Ison fd1a0203c7 [management] Clean up after account deletion (#7812)
Deleting an account left state behind that DeleteAccount's store
associations don't reach. The Agent Network tables outlived the account,
keeping its gateway domain claimed and its provider API keys stored. The
proxies kept serving its gateway until they next resynced. Cloud-side
state, such as managed proxy deployments, had no way to be cleaned up at
all.

Account deletion now runs registered hooks after the permission check
and before any users or data are removed. A failing hook aborts the
deletion. Agent Network registers one that tells the proxies to drop the
account's gateway mappings. The account's settings, providers, policies,
guardrails and budget rules are deleted in the account's transaction.
Consumption counters, and the access logs of deleted accounts, are left
to the background cleanup; usage records are kept.
2026-09-30 18:25:38 +02:00
Pascal Fischer 39de33ceca [management] handle db conn close on errpr (#7740) 2026-09-30 11:35:18 +02:00
Bethuel Mmbaga 30dd076b36 [management,proxy] Use single-use codes for OIDC session handoff (#7635)
* Generalize PKCE verifier store into SingleUseStore

* Generalize PKCE verifier store into SingleUseStore

* Extend single-use store to generate one-time retrieval codes

* Hand off proxy OIDC session via one-time code instead of URL token

* Use the single-use store in integration tests

* Read active proxy versions by cluster

* Detect proxy clusters that support session codes

* Bind OIDC session handoff mode to signed state

* Deprecate legacy OIDC session token handoff

* Remove unrelated session code test stub

* fix tests

* fix merge

* Fix session code compatibility detection

* Isolate proxy session codes in shared cache

* bump min session version
2026-09-29 18:29:55 +03:00
Pascal Fischer 93cb226a5e [management] fix login filter (#7739) 2026-09-29 13:29:24 +02:00
Bethuel Mmbaga 082aca1556 [management] Refactor PKCE verifier store into a reusable single-use store (#7634)
* Generalize PKCE verifier store into SingleUseStore

* Use the single-use store in integration tests

* Remove unrelated session code test stub
2026-09-29 13:25:56 +03:00
Brad Ison e830903a94 [management] Add a revocation guard hook to the proxy token API (#7732)
Let an embedding binary refuse DELETE /api/reverse-proxies/proxy-tokens/{id}
through an optional proxytoken.RevocationGuard passed to NewAPIHandler.
It is checked after the ownership check, so another account's token
still returns 404 without reaching the guard, and before the token is
revoked. A status error from the guard is written with util.WriteError;
any other error becomes a generic 500.

Nothing installs a guard here, so OSS behavior is unchanged.
2026-09-29 09:28:30 +02:00
Pascal Fischer 782c943410 [management] extract shared db conn + data repository (#7649)
* extract shared db conn + data repository

* extract repository interface

* protect against nested transactions

* fix mysql and db conn creation

* fix context management

* remove query warpper

* remove withContext and withLock wrapper

* remove context from function call

* remove in memory mode

* fix nested transaction handling

* use db directly

* remove leftover test

* remove pool close on error during conn creation
2026-09-29 00:45:55 +02:00
Pascal Fischer 5d92c89227 [management] move rate limiter to shared package (#7727) 2026-09-28 15:44:52 +02:00
Maycon Santos 979571a99f [management] Prevent deleting custom domains used by services (#7515)
Deleting a custom domain released its name while services still pointed
at it, leaving them on a namespace the account no longer held.

Deletion now refuses with 412 when a service in the same account uses the
domain or a subdomain, including disabled ones. Service writes revalidate
authorization inside their transaction and hold a shared lock on the
matching registrations, so a delete racing a create cannot strand either.
The dependency lookup is account-scoped: registrations are unique by name,
so another account can hold team.example.com under example.com and its
services are authorized by its own registration.
2026-09-28 14:14:32 +02:00
Pascal Fischer 7009add7a9 [management,signal,proxy] add pyroscope profiling (#7536) 2026-09-23 18:01:35 +02:00
Pascal Fischer 40dffc69ae [management] record proxy version on connect (#7630) 2026-09-23 17:59:58 +02:00
Riccardo Manfrin cb7ca8ef3f [client,management] Skip route firewall rule computation when no firewall (#7624)
* [client,management] Skip route firewall rule computation when no firewall

A peer that runs with the firewall disabled has no ACL manager and no
firewall to program, so nothing ever reads RoutesFirewallRules: the only
consumers are acl.Manager, which is reached solely when e.acl is set, and
the legacy-management probe in updateNetworkMap, which is guarded by a
non-nil firewall.

Building those rules is the most expensive part of a sync on a peer that
routes many network resources. On a 15k-peer deployment a debug bundle
showed getPeerNetworkResourceFirewallRules accounting for 62% of the
allocations of Calculate, and Calculate for effectively all of the
allocations of handleSync, which was taking 3.2s on average and holding
the engine lock for the duration.

Let the caller ask Calculate to leave the rules out. The client passes
its existing DisableFirewall setting; the management server keeps the
default and still produces them.

RoutesFirewallRulesIsEmpty is set from the resulting empty list, so a
receiver that would otherwise infer legacy management from an empty rule
set does not misread the skip.

* [client,management] Cover the skip flag through the envelope

Review feedback on #7624.

The components test compared only the length of the peer firewall rules, so
a change to their content would have passed while the message claimed they
came out unchanged. Compare the slices.

The skip path was also only exercised by setting the field directly on the
components, which bypasses the envelope conversion where
RoutesFirewallRulesIsEmpty is derived. That bit is what keeps the client from
reading skipped rules as a legacy management server, so it gets a test that
goes through EnvelopeToNetworkMap with the flag set.

* [management] Give the router a peer ACL so the rule comparison bites

Review feedback on #7624.

peer-router-1 appears in no peer ACL in the shared fixture, so its
FirewallRules came out empty and the equality assertion compared two empty
slices — it would have passed even if the peer rules were dropped entirely.

Add a policy covering the router and require the baseline to be non-empty
before comparing.
2026-09-23 11:50:44 +02:00
Pascal Fischer 771d81b72a [management] Add proxy credentials limiter on management (#7569) 2026-09-21 15:55:56 +02:00
pascal d3c5b6718a Merge branch 'main' into poc/certificate-posture 2026-09-21 11:22:36 +02:00
Bethuel Mmbaga 7d8f4fa31c [management] Handle empty trusted peer (#7589) 2026-09-18 18:21:34 +03:00
pascal a2e66a7d64 Merge branch 'main' into poc/certificate-posture
# Conflicts:
#	go.mod
#	go.sum
2026-09-17 17:52:19 +02:00
Bethuel Mmbaga f8c3e565f3 [management] Read X-Real-IP when extracting the peer connection IP (#7561) 2026-09-17 12:45:03 +03:00
pascal 25078c4af0 Merge branch 'main' into poc/certificate-posture 2026-09-16 23:31:07 +02:00
Maycon Santos 7ce6a63dcb [management] Validate the proxy cluster an agent network bootstraps onto (#7402)
The agent network gateway service is private: agents reach it over the WireGuard tunnel, authorised by peer identity, with the cluster as its only target. Only a reverse proxy cluster with private capabilities can serve that, reported per cluster as the `private` capability — the same `supports_private` flag the dashboard gates NetBird-only services on.

A bootstrap could pin an account to a cluster without private capabilities, leaving an immutable dead gateway. Both bootstrap paths now validate the picked cluster: one the account can see must have a connected proxy reporting the capability. Shared and account-owned clusters qualify alike. Known-ness comes from proxy rows, not heartbeat freshness, so a cluster without the capability stays refused while merely offline. A hostname no proxy has declared stays pinnable (address-first). Identity is compared case-insensitively over the account's cluster list.
2026-09-15 08:42:21 +02:00
Maycon Santos ea294e1d46 [management] Refuse to pin an agent network gateway onto another account's host (#7519)
An agent network bootstrap stores its cluster as proxy_address, which selects the proxy that serves the endpoint. An account-scoped proxy only receives its own account's mappings, so a pin onto a host another account's proxy declares can never be served, and the endpoint is immutable — a dead gateway until the account deletes its settings. Nothing refused that pin; the domain unique index only arbitrates between endpoints.

Both bootstrap paths now refuse, before the insert, a host that another account's proxy declares, a host another account has labeled pins beneath (self-addressed path), or a hostname that is another account's endpoint (labeled path).

Shared clusters are unaffected: shared proxies are never foreign, and labeled pins under one cluster are never asked about, so any number of accounts still pin beneath eu.proxy.netbird.io. Registration is deliberately unchanged — refusing a proxy for another account's pin would let a pin lock a tenant out after the reaper drops its rows.
2026-09-14 22:20:25 +02:00
pascal d89e9c86eb Merge remote-tracking branch 'origin/main' into poc/certificate-posture 2026-09-14 15:49:55 +02:00
Maycon Santos b789ffbb9f [management] Expire unvalidated custom domain registrations (#7497)
Prevent unvalidated registrations from reserving domain names indefinitely.

Give pending registrations a 48-hour validation window and clean up expired entries at startup and every 60 minutes. Emit CustomDomainValidationExpired for each deletion and preserve registrations referenced by services.

Reject validation after expiry and prevent concurrent validation from recreating deleted registrations. Normalize domain names with the shared parser before registration.

Migrate existing pending registrations to receive a fresh 48-hour validation window.
2026-09-11 17:57:58 +02:00
pascal 6c910ede4a Merge branch 'main' into poc/certificate-posture 2026-09-11 17:13:05 +02:00
dmitri-netbird f422c41654 [management] extract peer update logic and wrap it in tests (#7338)
* extract peer update loop into a dedicated struct and wrap it in tests

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* make linter happy

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

---------

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
2026-09-11 17:07:26 +02:00
Bethuel Mmbaga 58114f98fb [management] Only trust forwarded-IP headers from configured trusted peers (#7454) 2026-09-11 15:41:52 +03:00
Pascal Fischer 1047df5fa2 [management] pass tls config for combined server (#7499) 2026-09-11 13:50:11 +02:00
Maycon Santos 21b4a83cea [management] Refuse services on unvalidated custom domains (#7341)
Require validated custom domains when creating or updating reverse proxy services. 
Propagate validation errors during updates and return HTTP 409 for duplicate domain claims.

Add regression tests for domain validation, ownership, and service creation and updates.
2026-09-10 11:57:14 +02:00
pascal 8c5cf86117 Merge remote-tracking branch 'origin/main' into poc/certificate-posture 2026-09-04 19:37:22 +02:00
dmitri-netbird 00003814f3 [management] update network_router_test to verify empty and nil peer_groups (#7425)
* update network_router_test to verify empty and nil peer_groups

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* fix an issue with deserialization of nil json array in user.go

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* order zones by id

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

---------

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
2026-09-04 15:29:51 +02:00
Pascal Fischer 0bdfa4277e [management] blocking sync requests for user peers sharing the same wireguard key (#7427) 2026-09-04 13:10:51 +02:00
Bethuel Mmbaga 13ab50b901 [management] Add SetNX and GetDel cache store operations (#7084) 2026-09-04 11:03:28 +03:00
dmitri-netbird c455a4ac31 [management] disallow weird ip addresses for direct upstream hosts (#7400)
* disallow weird ip addresses for direct upstream hosts

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* handle bracketed ipv6 addresses

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* extend the check to subnet service targets

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* fix spelling

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* reject ipv6 addresses with zones

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* catch host:port hostnames in services with subnet targets

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* make linter happy

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

---------

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
2026-09-04 10:00:35 +02:00
Maycon Santos 8dc4272519 [management] Serve networks with peer-based routers from the SQLite network map (#7418)
The SQLite network-map query expanded a router's groups with from network_routers, json_each(peer_groups). That comma is an inner join, so a router row survives only when json_each returns at least one row. A router targeting an individual peer carries no groups — the write path stores NULL for a nil slice and '[]' for an empty one — and json_each yields nothing for either, so the join erased the router before it could be keyed by its peer. Postgres reads the same rows through a correlated subquery and was never affected.

The fix expands the groups with a left join, so the router survives with a NULL group_peers.peer_id and the existing scan loop keys it by router.Peer. Group routers still fan out one row per member.
2026-09-04 08:14:02 +02:00
Viktor Liu c2b5d211d9 [management] Enforce reverse proxy group access before minting and when honouring a session cookie (#7240) 2026-09-04 07:15:20 +02:00
Maycon Santos 6aaeed744e [management] Check a provider's url and credential before saving it (#7301)
A bad upstream or key saved cleanly and surfaced minutes later as a failed
request or an empty model picker, with nothing pointing back at the record.

CreateProvider now spends the credential once against the vendor's model
listing. UpdateProvider does the same when the upstream, the key, the catalog
provider or the skip-TLS flag changed — only then, so renames and price edits
neither wait on a vendor nor fail because one is down. Both run before the store
write, so a rejected rotation leaves the working key where it was.

What cannot be checked still saves: no listing endpoint, no derivable Bedrock
control-plane host, a private upstream, a record skipping TLS verification.
Everything else blocks, outages included — 5xx, 429 and timeouts leave the
record unverified just as a refusal does. Refusals return 422 and carry no
status code or echoed URL.

Discovery now reads as a partial edit, so a retyped URL can be listed against
without also rotating the credential. Entries with their own listing host
(Bedrock) get their configured upstream resolved separately, since a successful
listing said nothing about it.
2026-09-02 21:46:47 +02:00
Maycon Santos 26e5495e5d [management,proxy] Serve guardrail allowlists of declared model ids (#7389)
After #7221, guardrail allowlists built from a path-style provider's declared
model ids (Bedrock, Vertex) stopped working: the raw region/version form was
compared against the parser's canonical id, so the agent config advertised an
empty model list and requests for the allowlisted model were refused.

Make every allowlist compare provider-aware, keyed on the destination
provider's catalog id: the agent config, the policy gate, and the synthesized
proxy allowlists match an entry on both its verbatim and canonical form —
Bedrock's strip only under bedrock_api, Vertex's only under vertex_ai_api,
verbatim everywhere else, so a plain provider's suffixed entries never widen.
The router's claim compare learns the Vertex @version strip.

New e2e, realstore, and unit tests reproduce both regressions and pin the fix.
2026-09-02 18:57:02 +02:00
Pascal Fischer 8a5e940c84 [management] remove old math rand lib (#6836) 2026-09-02 17:52:51 +02:00
Pascal Fischer e3d6c3d0eb [management] fix private services calc on new db path (#7383) 2026-09-01 20:20:30 +02:00
Maycon Santos ebc259e30b [management,client] Gate remote jobs behind an admin opt-in with MDM support (#7153)
This introduces a disabled-by-default allow-remote-jobs setting that
controls whether the management server may run jobs (such as debug
bundles) on a peer. The flag propagates end to end: through client
configuration, the daemon SetConfig and Login requests, authentication,
and system info, up to management, where it is stored on the peer and
exposed on the peers API as remote_jobs_allowed. The client refuses any
management-requested job unless the peer has opted in. Because enabling
remote jobs crosses the user-to-root boundary, turning it on requires
privilege, mirroring the SSH-server gate. Administrators can enforce the
setting through MDM policy on both macOS and Windows, and MDM can also
override the debug-bundle upload URL. The change ships policy
documentation and generated profile templates, and adds configuration,
conflict, and enforcement tests covering the opt-in, privilege, and MDM
paths.
2026-09-01 17:53:41 +02:00
Maycon Santos 3027130f0f [management] Add Agent Network access roles and self-service endpoints (#7221)
Delegating Agent Network today means handing out full account admin, and
regular users cannot see their own usage or how to connect a local tool.

Add two roles on top of the existing agent_network permission
submodules. agent_network_admin owns the whole area (providers,
policies, guardrails, budgets, usage, logs, settings) with read-only
users, groups, peers, and account info needed to build policies, and
nothing else in the account. usage_viewer is the regular User baseline
plus read on the aggregated usage and cost overview: no provider
configuration, no policies, no request-level logs, which can contain
captured prompts. billing_admin gets a proper permission-map entry with
the User baseline so role resolution stops failing with role-not-found;
its plan and invoice permissions stay enforced cloud-side.

Add the self-service endpoints behind the "My Agent Network" view,
available to every authenticated user because both answers are scoped
strictly to the caller. GET /api/agent-network/me/setup returns the
account endpoint plus the providers and models the caller's own groups
authorize, computed with the same rules the proxy enforces: policy
filtering as in policy selection, model allowlist union intersected
with declared models, orphan and disabled providers omitted. Not set up
and no access are deliberately indistinguishable, and the response
carries display metadata only. GET /api/agent-network/me/consumption
returns the caller's own user-dimension counters.
2026-09-01 14:35:38 +02:00
Daneyon Hansen 7a9582db16 [management,proxy] Add agentgateway integration (#7274)
* [management] Add agentgateway provider catalog entry

Allow Agent Network providers to target an operator-supplied agentgateway proxy while stamping trusted NetBird identity headers.

Signed-off-by: Daneyon Hansen <daneyon.hansen@solo.io>

* [proxy] Allow trusted Agent Network identity headers

Permit only the built-in identity injector to replace the two reserved agentgateway attribution headers while keeping them blocked for every other middleware.

Signed-off-by: Daneyon Hansen <daneyon.hansen@solo.io>

* [management,proxy] Add multi-vendor gateway routing

Let one Agent Network route declare multiple parser surfaces while preserving the existing singular vendor wire field.

Signed-off-by: Daneyon Hansen <daneyon.hansen@solo.io>

* [management] Update router test for model policies

Signed-off-by: Daneyon Hansen <daneyon.hansen@solo.io>

* [proxy] Cover reserved header policy

Signed-off-by: Daneyon Hansen <daneyon.hansen@solo.io>

* [management] Add agentgateway model discovery

Use agentgateway's OpenAI-compatible models endpoint and omit wildcard patterns until NetBird can authorize and price them consistently.

Signed-off-by: Daneyon Hansen <daneyon.hansen@solo.io>

---------

Signed-off-by: Daneyon Hansen <daneyon.hansen@solo.io>
2026-09-01 13:03:16 +02:00
pascal 84d83fa05e implement certificate posture check 2026-08-31 13:47:55 +02:00
Pascal Fischer 353251d886 [management] fix posture check evaluation for direct peers in policy definition (#7348) 2026-08-28 16:46:42 +02:00
Pascal Fischer 611a9291cd [management] fix posture check flip evaluation for affected peers calc (#7347) 2026-08-28 15:39:48 +02:00
Pascal FischerandDmitri Dolguikh e06c17cf59 [management] network map from nmap data type (#6919)
Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
Co-authored-by: Dmitri Dolguikh <dmitri.external@netbird.io>
2026-08-27 11:28:05 +02:00
Viktor Liu 51095cb986 [client, management] Support per-peer lazy connection state and default proxy peers to lazy (#6762)
* Support per-peer lazy connection state and default proxy peers to lazy

* Classify forward targets from incoming config in lazy exclusion

* Set IsUserspaceBind mock so lazy manager starts in engine test

* Skip lazy exclude reconciliation when the set is unchanged

* Keep cached lazy flag when a sync carries no peer config
2026-08-26 09:33:51 +02:00