mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-13 02:09:08 +02:00
agent-network-settings-etag
3213
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6c9267c7d3 |
[management] State the sqlite precision caveat without the review history
A comment explaining why the truncation guard sits on the type recorded how the gap came to be noticed rather than the constraint itself, which AGENTS.md rules out. The technical point is the one worth keeping: sqlite preserves nanoseconds, so a sqlite-only suite cannot observe the truncation. The neighbouring back-compatibility comment gets the same treatment — "clients written before this existed" leans on the reader knowing what "this" was, and naming conditional requests outright says it plainly. |
||
|
|
29eff3b207 |
[management] Derive the settings ETag at a precision the store preserves
The validator a bootstrap returns was permanently unusable on PostgreSQL. POST derives it from the in-memory row, whose CreatedAt carries nanoseconds, while every later comparison derives it from a row read back out of the store — and PostgreSQL truncates timestamps to microseconds, MySQL DATETIME to whole seconds without an fsp. The two never agreed again, so the documented "conditional PUT without an intervening GET" answered 412 forever. Hashing CreatedAt at whole-second precision fixes it at the root: seconds is the floor every supported engine round-trips, so the validator no longer depends on which store is behind it. The cost is that a delete and re-bootstrap inside the same second, onto the same endpoint with the same toggles, derives the same validator — which needs a self-addressed endpoint reclaimed within one second, since a labeled bootstrap draws a fresh label. The whole suite ran green against this bug, because every test uses the sqlite test store and sqlite preserves nanoseconds. The regression guard is therefore on the type rather than through a store: it asserts the validator is unchanged by truncation at each engine's precision, so it holds without running the suite against all three. Also from review: the concurrency test recorded the winning writer's value in a slice left zero for the loser, and zero is a retention the API documents as "keep indefinitely" — so the assertion could be satisfied without matching the writer that won. It now uses a sentinel and asserts equality. The stale-delete e2e assertion accepted any error, which a server error or a state-guard refusal would have satisfied; it now requires a precondition failure. And the 412 message both conditional writes return is a single constant, since on delete the message is the only thing separating staleness from the state guards. |
||
|
|
35137326f7 |
[management] Expose the ETag response header to browser clients
ETag is not a CORS-safelisted response header, so JavaScript cannot read it unless the server names it in Access-Control-Expose-Headers. Without that the API hands a browser client a validator it has no way to see, and conditional requests are available to the CLI, the REST client and Terraform but silently not to the dashboard — the one client where a lost update is a person's work disappearing rather than a plan reverting. The library offers no way to extend cors.AllowAll(), so the policy is spelled out verbatim with ExposedHeaders added. That is a wider blast radius than the one field deserves, which is why the tests assert the rest of the policy too: any origin, no credentials, the full method set. If-Match needs no grant of its own, since AllowedHeaders is already "*" — the preflight test guards against a later narrowing that would leave writes readable but not conditional. Collapsing this back to cors.AllowAll() is the obvious tidy-up and would silently undo it, which is what the tests are there to catch. |
||
|
|
5085a2f96b |
[management] Surface the settings ETag through the REST client
The Terraform provider consumes this client, and it is the client that has the read-modify-write problem conditional requests solve, so the server-side work is inert until the validator reaches it. The new methods are additive and the existing four delegate to them unchanged. A breaking signature change would buy a tidier surface at the cost of every current caller, with no deprecation window, and the plain methods remain the right default for callers that do not care. CreateSettingsWithETag exists because the bootstrap emits a validator too, and discarding it would force a client to read again before its first conditional write — the round trip the header is there to avoid. IsPreconditionFailed joins IsNotFound because a refused precondition is otherwise an opaque APIError. Telling "someone else changed this, read again and retry" apart from a genuine failure is the decision a conditional client has to make, and it should not have to compare status codes by hand. Validators arrive unquoted and go back out quoted, so no caller handles the wire syntax. The e2e coverage drives all of it through the typed client against a real server, playing out the case that motivates the feature: a client plans an update, an operator turns PII redaction on in between, and the client's write is refused instead of silently turning it back off. |
||
|
|
30c2010c09 |
[management] Document the Agent Network settings ETag in the API spec
Declares what the handlers already do: the settings reads and writes return an ETag, and PUT and DELETE honour an If-Match against it. The 412 that PUT can now return is documented alongside, and DELETE's existing 412 grows a note that a precondition failure shares the status with its two state guards and is told apart by the message rather than the code. Response headers generate nothing under a models-only codegen config, so those entries are for human and third-party consumers. The If-Match parameters do generate — hence the two Params structs in types.gen.go, which nothing calls yet but which the REST client can take up. |
||
|
|
9b06290240 |
[management] Honour If-Match on Agent Network settings writes
With the validator derived and the helper in place, wire both together: reads and writes hand back an ETag, and PUT and DELETE honour an If-Match against it. A client that plans against one read and writes the whole object back now gets refused instead of silently reverting whatever changed in between. The comparison happens inside the write's own transaction, under the row lock the fetch already takes, rather than in the handler. Comparing before the transaction merely narrows the race: two writers read the same row, both find their precondition satisfied, and both then write. That is the lost update this is meant to prevent, and the concurrency test added here fails against that shape while passing against this one. It also runs ahead of each operation's other guards. A caller stale enough to be holding an old validator is stale in its identity echo too, so answering "you are working from an old read" is more useful than "the endpoint is immutable"; on delete it matters more still, because the provider guard raises the same 412 and would send a stale client hunting for providers it may not know exist. If-Match stays optional. The dashboard is human-driven, where last-write-wins is acceptable and a surprise 412 is worse than a benign overwrite; Terraform is machine-driven and can opt in. Making it mandatory would cost a dashboard change and should use 428 when that day comes. GET emits a validator for the pre-bootstrap defaults as well: they are a representation like any other, and validating them means an If-Match taken before bootstrap cannot match the row that appeared since. |
||
|
|
829156f53d |
[management] Add ETag and If-Match helpers for HTTP handlers
The read-modify-write pattern conditional requests guard is not specific to one resource — every Terraform-managed resource has the same shape, so the second consumer of this is a matter of when, not if. Deciding the entity-tag quoting, the "*" form, the list form and the weak-validator rule once here means an adopting resource inherits all of them instead of re-deriving each. Only the derivation stays on the type, since only the type knows which of its fields the representation covers. IfMatch returns a nil-safe precondition rather than a slice of tags. If-Match is defined in terms of the strong comparison function, so a weak validator can never satisfy it — and a header carrying nothing but weak validators then parses to an empty list, which the obvious call-site check reads as "no precondition given" and lets the write through unguarded. That is precisely the failure this is meant to prevent, and it is reachable by a proxy that weakens an ETag in transit. An absent header is unconditional; a header that was sent but carries nothing usable refuses. Weak validators are dropped rather than unwrapped, for the same reason: unwrapping one into a strong tag would quietly grant a match that the client's own header said it could not have. No handler uses this yet. |
||
|
|
4f6caa1110 |
[management] Derive an ETag for Agent Network settings
A client that reads the Agent Network settings, computes a change and writes the whole object back will silently revert anything that changed in between — Terraform's read-modify-write is exactly this shape. The consequence worth guarding is RedactPii: an operator enabling PII redaction in the dashboard can have it turned back off by an apply that was planned before their change, with no error and no drift warning on that run. Conditional requests need a validator. Derive one instead of storing it: a timestamp cannot separate two writes within a clock tick, and a revision column costs a migration plus the bump-discipline that goes with it. A hash of the representation needs neither and is correct by construction. The hash covers an explicit field tuple rather than the marshalled API types, whose field ordering is not a contract. AccountID stays out — it identifies the resource, not the representation — and so does UpdatedAt, so that a write changing nothing observable cannot invalidate a precondition someone else is holding. The identity fields and CreatedAt are in: a validator over only the mutable toggles would survive a delete and fresh bootstrap onto the same toggle values, and an If-Match held across that gap would then authorize a write against what is really a different resource. This only derives the validator. Nothing emits or honours it yet. |
||
|
|
27b2d3f351 |
[management] Add a proxy-connect authorizer seam (#7136)
At proxy connect time, the declared cluster address is validated for shape and checked for availability (`IsClusterAddressAvailable`), and from then on the declaration is what routes the cluster's mappings to the connection. Deployments that embed management through the integrations seam may need a policy on that claim — deciding which credential is allowed to declare which address. This adds an optional `ProxyConnectAuthorizer` hook on `ProxyServiceServer`, following the pattern of the existing `Set*` seams (`SetServiceManager`, `SetAgentNetworkSynthesizer`, `SetAgentNetworkLimitsService`, `SetProxyController`): - A nil-able interface field plus `SetProxyConnectAuthorizer`, guarded by the existing mutex. - One call at the end of `validateProxyConnect`, so both `GetMappingUpdate` and `SyncMappings` are covered by a single site. - **Nothing installs it by default** — with the hook unset (always, in this repo), behavior is byte-for-byte unchanged, which the tests pin. Design details: - The authorizer runs **last** — after input validation and the availability check — and **outside** the account-scoped branch, so management-wide tokens and token-less connects are also presented to it rather than bypassing policy. - The authorizer receives the presented `*types.ProxyAccessToken` (nil when none), the proxy ID, and the declared address. Everything it needs is already in the request/context; no proto or schema change. - A plain error from the authorizer surfaces as `PermissionDenied`, keeping an authorization rejection distinguishable from the `AlreadyExists` used for address conflicts in proxy logs. A status error passes through unchanged so implementations can pick their own code. |
||
|
|
ebfdf7d7b8 |
[management] Rework Agent Network endpoint identity and settings bootstrap (#7085)
Store the per-account gateway endpoint as {domain, proxy_address} with a
global unique index on the full hostname; dedicated = (domain ==
proxy_address). Bootstrap becomes an explicit POST carrying exactly one
of proxy_address (server allocates an adjective-noun label beneath it)
or endpoint (claimed verbatim, address-first); provider create loses its
bootstrap side effect. PUT is a full replace with every field required —
the immutable identity fields must be echoed unchanged and a mismatch is
rejected with 422. A guarded DELETE releases the endpoint: refused with
412 while providers exist or a proxy is actively serving the endpoint
hostname (matched case-insensitively); re-creating bootstraps fresh. A
self-addressed pin excludes its address from the account's cluster allow
list, and the live mapping update path now addresses the serving proxy
from the synthesized service. Existing rows are migrated on all three
store engines.
|
||
|
|
e8671a811d | [client, relay] Migrate relay QUIC tracer to qlog and bump quic-go to 0.59.1 (#7124) | ||
|
|
1ca26d8faa |
[client] Bump the netbirdio/wails fork to drop the native WebView2 (#7128)
## Describe your changes The native_webview2loader build tag embedded Microsoft's WebView2Loader.dll via //go:embed. Those DLLs are gitignored in the fork and go mod vendor resolves embed patterns regardless of build constraints, so vendoring the module failed on the missing files when packaging for openSUSE. The fork now removes that branch along with the go-winloader dependency, which drops out of the module graph here. The default GoWebView2Loader path is unaffected. ## Issue ticket number and link <!-- Required for anything that changes behavior. Link the issue (or the validated discussion it came from) that the NetBird team already agreed on. See https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second --> ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] I ran and tested this change locally — I did not rely on CI to find out whether it works - [ ] This PR has a single purpose (not a fix + refactor + feature in one) - [ ] This change is a trivial fix, **OR** it links an issue the NetBird team agreed on beforehand. Changes to the public API, gRPC protocols, functionality behavior, CLI / service flags, or new features always need that agreement first. See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated the application framework revision. * Removed an unused dependency. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f9abe2727f | [client] Do not misroute WireGuard packets to the STUN handler (#7059) | ||
|
|
664a3d026c | [client] Fix credentials for the gtk3 package uploads (#7125) | ||
|
|
d2c961f67c |
[client] Declare the xdg-utils dependency for the netbird-ui packages (#7126)
## Describe your changes The UI shells out to xdg-open to launch the external browser for the SSO verification page, which the embedded webview cannot open inline, and to reveal the debug bundle in the file manager. client/ui/build/linux/nfpm/nfpm.yaml lists xdg-utils for every package format, but the released packages are built from the goreleaser configs, where it was missing: the GTK and WebKitGTK dependencies carried over and xdg-utils did not. Add it to all four nfpm dependency lists. ## Issue ticket number and link <!-- Required for anything that changes behavior. Link the issue (or the validated discussion it came from) that the NetBird team already agreed on. See https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second --> ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] I ran and tested this change locally — I did not rely on CI to find out whether it works - [ ] This PR has a single purpose (not a fix + refactor + feature in one) - [ ] This change is a trivial fix, **OR** it links an issue the NetBird team agreed on beforehand. Changes to the public API, gRPC protocols, functionality behavior, CLI / service flags, or new features always need that agreement first. See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Added required desktop integration support to Debian and RPM packages. * Ensured GTK3 packages include the same runtime support for opening links and files through the system. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5584f8ef0a | [client] Add strict anonymization level and MAC anonymization to debug bundles (#7102) | ||
|
|
f65f7b347e |
[management] Deny reverse proxy access to pending and blocked users (#7105)
A user in the Pending Approval state could complete SSO and reach any SSO-protected reverse proxy service distributed to a group they belong to, including the All Users group. The reverse proxy authorization path checked the session token signature, that the user exists, that the user's account matches the service's account, and group membership — never the user's account status. The REST API (`permissions/manager.go`) and peer registration both gate on that state, but the proxy gRPC service does not go through the permissions manager, so neither gate applied. A pending user is persisted as blocked and pending approval, so blocked users reached those services the same way. `ValidateSession` now denies on account status, reporting `pending_approval` or `user_blocked` so the proxy access log and the denied page carry the cause rather than a generic refusal. `GenerateSessionToken` refuses to mint a token for such a user at all, so the browser never receives a session cookie and the OIDC callback can tell the user why instead of showing "Service configuration error". `ValidateUserGroupAccess` and `ValidateTunnelPeer` close the same gap; for the tunnel path this covers a user blocked after their peer was registered, since peer group membership alone kept mesh-origin access open. A single helper produces both the denied reason for the RPC responses and the sentinel error for the error-returning callers, so the four entry points cannot drift apart. A user the store cannot resolve is denied rather than passed through. One thing deliberately left out: session cookies are validated locally by the proxy against the service public key with no management round-trip, so a cookie issued before a user is blocked stays valid until it expires (24h by default). That is a revocation-propagation problem rather than this authorization gap, and every option for it (per-request validation with a cache, short-lived tokens with refresh, push-based revocation) changes the proxy hot path or the proxy/management protocol. Worth its own ticket.v0.76.3 |
||
|
|
179e8f6e13 | [infrastructure] add grafana dashboard for licensed management (#7095) | ||
|
|
2ee21d2b5c | [management] Affected peers for user updates (#7099) | ||
|
|
eb619fc7e3 |
[client] disambiguate the connection_type metric tag (#7043)
## Describe your changes `recordConnectionMetrics` mapped only `conntype.Relay` to `relay` and let a `default` branch record everything else as `ice`. That silently included `ICETurn` — an ICE connection through a TURN server, which [`conn.isRelayed`](https://github.com/netbirdio/netbird/blob/main/client/internal/peer/conn.go#L788-L795) itself counts as relayed — and `None`, the transient state set when the relay drops ([conn.go:632](https://github.com/netbirdio/netbird/blob/main/client/internal/peer/conn.go#L632)) or the peer state is reset ([conn.go:757](https://github.com/netbirdio/netbird/blob/main/client/internal/peer/conn.go#L757)). Both were reported as direct peer-to-peer, so the `ice` share overstated direct connections on every platform. The mapping now lists every priority explicitly and emits `ice_p2p`, `ice_turn`, `relay` or `unknown`. The new values deliberately do not reuse `ice` to avoid ambuguity. ## Issue ticket number and link No public issue. Found while reviewing the first production sample of client metrics: 38% of iOS connection events were tagged `ice` on a platform that forces relay by default, which traced back to the `default` branch at [client/internal/peer/conn.go#L963-L968](https://github.com/netbirdio/netbird/blob/main/client/internal/peer/conn.go#L963-L968). ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [x] Created tests that fail without the change (if possible) > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) Internal metrics documentation only, in `client/internal/metrics/infra/README.md`: the four `connection_type` values with their derivation, and a note that pre-fix `ice` samples are not comparable with `ice_p2p`. No public API, CLI or configuration change, so no netbirdio/docs PR. ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: N/A <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit * **New Features** * Connection metrics now distinguish direct peer-to-peer, TURN-assisted, relay, and unknown connection types. * Metrics include clearer connection and peer identification details. * **Documentation** * Updated connection timing metric values, traffic semantics, priority behavior, and historical data guidance. * **Bug Fixes** * Unset or unrecognized connection priorities are no longer incorrectly classified as peer-to-peer. * Unknown-transport metrics are skipped to prevent misleading connection data. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
8632a0d215 |
[infrastructure] Detect community GHCR images during enterprise migration (#7101)
The migration wizard detected the community deployment only by the Docker Hub image prefix (netbirdio/netbird-server), so deployments installed from the ghcr.io mirror failed with "Could not find a service running netbirdio/netbird-server*". This broadens the server and dashboard detection to also accept ghcr.io/netbirdio/... images. The regexes are anchored at the tag/digest separator so Enterprise images (netbird-server-cloud, dashboard-cloud) are still rejected, an already-migrated deployment must not be detected as a community one. Error messages updated to mention both forms. |
||
|
|
f63fd21e0c |
[client] peer: re-arm the WireGuard watcher after a lazy wake (#7091)
## Describe your changes The Conn struct is reused across lazy-connection deactivate/activate. Close cancels the WireGuard watcher (via wgWatcherCancel, and ctxCancel also tears down its context) but left conn.wgWatcher pointing at the stopped instance. enableWgWatcherIfNeeded skips while conn.wgWatcher is non-nil, so the next Open never started a fresh watcher: once a lazy connection had idled and woken, the peer ran with no watcher at all — no WireGuard handshake-timeout detection and none of the escalation that depends on it. Clear conn.wgWatcher and conn.wgWatcherCancel in Close so the next Open re-arms a fresh watcher. ## Issue ticket number and link <!-- Required for anything that changes behavior. Link the issue (or the validated discussion it came from) that the NetBird team already agreed on. See https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second --> ## Stack <!-- branch-stack --> ### Checklist - [X] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] I ran and tested this change locally — I did not rely on CI to find out whether it works - [ ] This PR has a single purpose (not a fix + refactor + feature in one) - [ ] This change is a trivial fix, **OR** it links an issue the NetBird team agreed on beforehand. Changes to the public API, gRPC protocols, functionality behavior, CLI / service flags, or new features always need that agreement first. See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [X] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved connection cleanup by fully releasing WireGuard watcher resources when a connection closes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
524b8b9718 | [management] prewarm a posture check cache on network map generation (#7093) | ||
|
|
2ce6323602 | [client] Update the wails fork reference to the integration branch head (#7087) v0.76.2 | ||
|
|
9a05a1c698 | [client] Reword the firewalld package comment (#7081) | ||
|
|
5dd914782a | [client] Stop the macOS UI on pkg upgrade (#7079) | ||
|
|
8c19b7a30a |
[infrastructure] Generate a session cookie encryption key on fresh self-hosted installs (#7056)
The community and enterprise bootstrap scripts now generate a dedicated `server.auth.sessionCookieEncryptionKey` from 32 random bytes for fresh installations. The key is Base64-encoded, persisted independently from the datastore encryption key, and reused from the generated configuration after restarts. The community script now also creates `config.yaml` with mode `0600` before writing it, matching the existing enterprise behavior. The server already supports the session cookie encryption key, but the bootstrap scripts left it unset. This adds defense-in-depth for newly generated deployments while preserving the existing server-side nonce validation. Because this only changes newly generated configuration, existing installations and sessions are unchanged. A focused regression test checks key presence, decoded length, separation from the datastore key, YAML placement, and file mode for both scripts. It is included in the infrastructure workflow. |
||
|
|
1e2a7aa571 |
[client] Add a UI setting to stay connected after quitting (#7078)
Quitting the GUI from the tray always sent a Down RPC, dropping the VPN connection with it. Some users want the connection to survive the UI. |
||
|
|
98f8a24108 |
[client] Ship a legacy GTK3 UI package for distros without WebKitGTK 6.0 (#7040)
Ship a legacy GTK3 UI package for distros without WebKitGTK 6.0 |
||
|
|
b2ae98f0fd | [misc] Expand agent guidelines with security, type-safety and lifecycle conventions (#7076) | ||
|
|
40ac64bb4e | [misc] Move enterprise setup to Traefik and harden migration (#7042) | ||
|
|
19a6cedfff |
[relay] randomize the relay reconnect backoff (#7067)
## Describe your changes The relay client's reconnect backoff was constructed without a `RandomizationFactor` ([guard.go:156-165](https://github.com/netbirdio/netbird/blob/main/shared/relay/client/guard.go#L156-L165)), so it kept the zero value: every client that lost the same relay server retried on the identical 2/4/8/16/32/60, potentially in waves. It was the only exponential backoff in the codebase without a randomization factor. Use `backoff.DefaultRandomizationFactor`, as random factor. ## Issue ticket number and link No public issue. Found while reviewing the relay reconnect path for the client-metrics review: 22k relay reconnection events in 24h, and the shared transport's own retry schedule was identical across all clients. ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [x] Created tests that fail without the change (if possible) > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) Internal retry-timing change with no user-visible surface: no CLI flag, configuration option or API field is added or altered, and the mean reconnect delay is unchanged. ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: N/A <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved reconnect timing to distribute repeated connection attempts more evenly and reduce synchronized retry spikes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
6526fc2bec |
[management] Prevent deleting groups referenced by reverse proxy services (#7062)
## Describe your changes A group could be deleted while a reverse proxy service still referenced it, silently breaking the service's access control: private services list groups in `access_groups` as the peer allowlist, and SSO bearer auth distributes tokens to `distribution_groups`. Group deletion now runs through the same linkage validation as routes, policies, and agent network policies: deleting a group that backs a private service allowlist or an enabled bearer-auth distribution list fails with a `GroupLinkError` naming the service domain. Disabled bearer configs and stale `access_groups` on non-private services are inert and do not block deletion. Tests cover both linked cases in single and bulk deletion, and pin the non-blocking cases. The test account seeds decoy services ahead of the linked ones so the check is proven to scan the full service list. |
||
|
|
2afa69b622 |
[management] prevent dangling group refs in agent-network ACLs. (#7060)
Block deleting a group referenced as a source group by an agent network policy, and drop unresolvable groups from synthesised private-service ACLs. A deleted group survived in agent_network_policies.source_groups and was carried into the injected in-memory policy, where network-map assembly resolved it to a nil group and panicked on every proxy peer sync. |
||
|
|
2a61eac047 |
[client] Fix Linux tray right-click opening the main window (#7039)
## Describe your changes On SNI hosts that report icon clicks via Activate (KDE Plasma, Waybar), Wails fired the left-click handler on every dbusmenu 'opened' event, so a right click opened the tray menu and immediately raised the main window, which stole focus and closed the menu. Host Left click Right click KDE Plasma, Waybar main window (Activate) menu (host-rendered) GNOME Shell + AppIndicator menu only menu only Minimal WMs via XEmbed host main window (Activate) XEmbed GTK popup Point the wails/v3 replace at the netbirdio fork (v3.0.0-beta.3 plus the fix): once the host has sent Activate/SecondaryActivate, a menu open no longer fires the click handler, while AppIndicator-only hosts that signal clicks solely via 'opened' keep the old behavior. ## Issue ticket number and link <!-- Required for anything that changes behavior. Link the issue (or the validated discussion it came from) that the NetBird team already agreed on. See https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second --> ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] I ran and tested this change locally — I did not rely on CI to find out whether it works - [ ] This PR has a single purpose (not a fix + refactor + feature in one) - [ ] This change is a trivial fix, **OR** it links an issue the NetBird team agreed on beforehand. Changes to the public API, gRPC protocols, functionality behavior, CLI / service flags, or new features always need that agreement first. See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Chores * Updated underlying application components to support compatibility and ongoing maintenance. * Clarified Linux tray interaction documentation, including platform-specific left- and right-click behavior and menu activation. * No user-facing features, workflow changes, or visual updates are included. * Existing Linux tray behavior remains unchanged. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f2d13b884a |
[client] Fix session expired relogin (#7055)
## Describe your changes After the SSO session expires, the daemon tears the engine down permanently (management returns `PermissionDenied` → `runCancel()` → the retry loop exits for good). The "Session expired" dialog's Login button still drove the extend-session flow, which requires a live engine: the user completed the full browser SSO + 2FA round trip only to get `Failed to extend the session — engine is not initialised`, with no way out other than quitting and relaunching the client. Reproduce: 1. Log in on a desktop client with session expiration enabled (e.g. 16h TTL). 2. Let the session expire (e.g. leave the machine asleep overnight). 3. Wake it, click **Login** on the "Session expired" dialog, complete SSO + 2FA. 4. The error dialog appears and every retry fails the same way. Changes: - The expired branch of the session-expiration dialog now emits `trigger-login`, driving the full `Login → SSO → Up` sequence that rebuilds the client, instead of the extend flow (an expired session can no longer be extended). - `RequestExtendAuthSession` fails fast when the engine is already gone, so the browser/2FA round trip is not wasted on a doomed extend. - The expired tray row navigated the main window to `/#/login`, a route that does not exist and fell through to the main page without starting a login; it now emits `trigger-login` as well. ## Issue ticket number and link <!-- Required for anything that changes behavior. Link the issue (or the validated discussion it came from) that the NetBird team already agreed on. See https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second --> ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] I ran and tested this change locally — I did not rely on CI to find out whether it works - [ ] This PR has a single purpose (not a fix + refactor + feature in one) - [ ] This change is a trivial fix, **OR** it links an issue the NetBird team agreed on beforehand. Changes to the public API, gRPC protocols, functionality behavior, CLI / service flags, or new features always need that agreement first. See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved session extension handling when the client engine is unavailable by prompting users to log in again. - Updated expired-session behavior to trigger the standard login flow, providing a more consistent sign-in experience. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
564595d283 |
[client, android] Fix profile account path test on Windows (#7057)
## Describe your changes The expected paths were hardcoded with Unix separators while profileAccountPathFor builds the result with filepath.Join, so the comparison failed on Windows. Derive the expectations with filepath.FromSlash to keep the test platform-independent. ## Issue ticket number and link <!-- Required for anything that changes behavior. Link the issue (or the validated discussion it came from) that the NetBird team already agreed on. See https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second --> ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] I ran and tested this change locally — I did not rely on CI to find out whether it works - [ ] This PR has a single purpose (not a fix + refactor + feature in one) - [ ] This change is a trivial fix, **OR** it links an issue the NetBird team agreed on beforehand. Changes to the public API, gRPC protocols, functionality behavior, CLI / service flags, or new features always need that agreement first. See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Updated profile account path test expectations to use platform-appropriate path separators, improving test reliability across operating systems. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
78c1c2fc32 |
[client] Probe the daemon login with IsLoginRequired (#7052)
## Describe your changes
Probe the daemon login with IsLoginRequired
The Login probe attemptLogin(ctx, "", "") on an unregistered peer ends
in registerPeer with no setup key and no JWT, which fails locally with
InvalidArgument before reaching Management. Since #6983 classified that
as StatusLoginFailed and returned early, every setup-key enrolment and
every expired-session SSO re-login aborted before using its credentials,
breaking all netbird-cloud e2e runs from commit
|
||
|
|
bc7a15ab71 |
[management] Align agent-network API contracts for API clients (#7026)
## Describe your changes Work on the Terraform provider (terraform-provider-netbird #177–#183) surfaced places where the agent-network API broke its own contracts or deviated from the conventions the rest of the management API follows, forcing client-side workarounds. Settings reads now follow the settings-endpoint convention: GET always answers with a JSON object. Before bootstrap it returns the defaults with an empty cluster/subdomain/endpoint (previously 200 with a JSON `null` body, while the spec said 404). The settings PUT can bootstrap the account by carrying a `cluster` — previously the row could only come into existence through the first provider create, and a settings-first setup was impossible; a differing cluster on a bootstrapped account is rejected instead of silently ignored. PUT remains full-state. The provider PUT schema promised omit-preserves semantics for several operator-editable fields that the handler never delivered (it builds the row from the request, like every other update handler). The schema wording now matches the shipped full-state behavior; only the api_key (secret) and session keys stay preserved by the manager. Identity headers are always present in provider responses so an explicitly cleared value round-trips as an empty string. The Go REST client gains the full agent-network surface (catalog, providers, policies, guardrails, budget rules, settings), including a shim translating the legacy 200+`null` settings body from older servers into an `IsNotFound` error. Note for reviewers: the dashboard special-cased the `null` settings body; it needs a small follow-up for the new defaults response (in progress). |
||
|
|
530021aec6 |
[client] Update wails to v3.0.0-beta.3 (#7038)
## Describe your changes Update wails to v3.0.0-beta.3 ## Issue ticket number and link <!-- Required for anything that changes behavior. Link the issue (or the validated discussion it came from) that the NetBird team already agreed on. See https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second --> ## Stack <!-- branch-stack --> ### Checklist - [ ] Is it a bug fix - [ ] Is a typo/documentation fix - [x] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] I ran and tested this change locally — I did not rely on CI to find out whether it works - [ ] This PR has a single purpose (not a fix + refactor + feature in one) - [ ] This change is a trivial fix, **OR** it links an issue the NetBird team agreed on beforehand. Changes to the public API, gRPC protocols, functionality behavior, CLI / service flags, or new features always need that agreement first. See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated the application framework dependency to a newer beta release for improved compatibility and stability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1bedb4e59d |
[client, android] Reuse the profile's account for Android SSO logins (#6988)
The Android binding never recorded which account a profile belongs to, so every interactive login and every session extend went to the IdP with no login_hint. With nothing to go on the IdP picks an account itself, which on a session extend means re-authenticating an account the profile is already signed in with. Store the email the PKCE flow already parses out of the ID token, and pass it back as the hint on later flows. An empty hint stays meaningful: a fresh profile, or one that was logged out, deliberately leaves the choice to the IdP, which is how a profile changes accounts. Logout clears the stored email for that reason — while it is on disk it would steer the next login straight back into the account just logged out of. The email is keyed off the profile's config path rather than the active profile: Auth.login runs in a goroutine, so the active profile can change under a flow already in flight. It lands in <profile>.account.json, not the <profile>.state.json desktop uses for the same data — there the email and the engine's state manager sit in different directories, but on Android both resolve under files/, and the state manager rewrites the whole file from its own keys. ## Describe your changes ## Issue ticket number and link ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] This change does **not** modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — **OR** I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added account email and active-status details to Android profile information. * Improved SSO sign-in and session renewal by restoring the previously used account as a login hint. * Added Android-specific profile email persistence with automatic cleanup on logout. * **Bug Fixes** * Profile email persistence failures now generate warnings without blocking login or logout. * Improved handling of missing or unreadable account data and repeated logout cleanup. * **Tests** * Added coverage for account-file naming, email persistence, and logout behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
7546e7751c |
[client, android] Reuse the persisted configuration when enrolling (#7022)
## Describe your changes
NewAuth builds a fresh in-memory configuration on every call, which
means a new WireGuard key each time. The peer registers under that key
and the key is written out, so any peer registered by an earlier call is
orphaned on the server — a client that enrols twice leaves two entries
and owns neither.
It also breaks the enrol-then-run sequence. `RunWithoutLogin` reloads
the configuration from disk through `UpdateOrCreateConfig`, so the
identity that registered is not necessarily the identity that runs, and
the management stream rejects it:
```
failed to login to Management Service: rpc error: code = PermissionDenied
desc = no peer auth method provided, please use a setup key or interactive SSO login
```
followed by a panic in `ConnectClient.run`.
### How it was found
Embedding the Android client in an application that enrols with a setup
key and then runs. Eight orphaned peers accumulated on a self-hosted
management server before the cause was clear, because every restart
registered a new one.
### The change
`NewAuth` passes `ConfigPath` and uses `UpdateOrCreateConfig`, so an
existing configuration is reused and one is only created when absent. A
caller wanting a fresh identity can delete the file — which is what
"forget this account" already does.
### Test
`TestNewAuth_ReusesPersistedIdentity` fails on the current code:
```
--- FAIL: TestNewAuth_ReusesPersistedIdentity (0.00s)
login_test.go:33: private key changed between calls: a second enrolment would orphan the peer registered by the first
```
and passes with the fix. `TestNewAuth_CreatesConfigWhenAbsent` covers
the first-enrolment path being unchanged. Both run in `client/android`
on Linux.
Per CONTRIBUTING, opening directly as a bug fix rather than raising an
issue first.
## Issue ticket number and link
[NET-1465](https://linear.app/netbird/issue/NET-1465/agent-network-rest-api-settings-defaults-bootstrap-via-put-provider)
## Stack
<!-- branch-stack -->
### Checklist
- [x] Is it a bug fix
- [ ] Is a typo/documentation fix
- [ ] Is a feature enhancement
- [ ] It is a refactor
- [ ] Created tests that fail without the change (if possible)
- [ ] I ran and tested this change locally — I did not rely on CI to
find out whether it works
- [ ] This PR has a single purpose (not a fix + refactor + feature in
one)
- [ ] This change is a trivial fix, **OR** it links an issue the NetBird
team agreed on beforehand. Changes to the public API, gRPC protocols,
functionality behavior, CLI / service flags, or new features always need
that agreement first. See
[CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second).
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] Documentation is **not needed** for this change (explain why): the
API reference is generated from the OpenAPI spec, which this PR updates
in-repo.
### Docs PR URL (required if "docs added" is checked)
Paste the PR link from https://github.com/netbirdio/docs here:
https://github.com/netbirdio/docs/pull/__
Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>
|
||
|
|
075b319fb3 |
[client, android] Pull fresh TUN settings on Android rebuild (#6991)
## Describe your changes Pull fresh TUN settings on Android rebuild instead of push The Android TUN rebuild consumed state pushed through notifications and a Java-side snapshot, and both sources were unreliable. The DNS search-domain notifier fired OnNetworkChanged with an empty string, which the rebuild handler treated as the new route list, so any search domain change rebuilt the TUN with zero routes and cut all tunnel traffic. The rebuild also reused the search domains cached at the last establish, so search domain updates never reached the TUN at runtime. Make the notification a pure trigger and let the Java side pull a fresh snapshot instead. Expose GetTunSettings on the Android SDK client: it returns the current TUN route ranges, derived on demand by the route manager from the client routes, the exit-node selection and the fake IP blocks, together with the DNS search domains. The route notifier keeps only its last-announced baseline to suppress triggers for unchanged syncs; the TUN route state is owned by the route manager. SearchDomains now locks the DNS server mutex since the pull arrives from a Java thread. Requires the matching android-client change that switches recreateTUN to the pull API. ## Issue ticket number and link ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] This change does **not** modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — **OR** I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added access to current TUN route ranges and DNS search domains. - TUN settings are returned in a mobile-friendly format for easier integration. - **Improvements** - Route changes are detected and synchronized more reliably. - Current routing information now reflects active routes, including supported fake-IP ranges. - Simplified network initialization for more consistent startup behavior. - **API Changes** - Removed the obsolete network-map retrieval method from the management client interface. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b82a42c855 |
[misc] Add android and ios tags to PR title check (#7037)
## Describe your changes Extend CI tag list with Android and iOS ## Issue ticket number and link <!-- Required for anything that changes behavior. Link the issue (or the validated discussion it came from) that the NetBird team already agreed on. See https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second --> ## Stack <!-- branch-stack --> ### Checklist - [ ] Is it a bug fix - [ ] Is a typo/documentation fix - [x] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] I ran and tested this change locally — I did not rely on CI to find out whether it works - [ ] This PR has a single purpose (not a fix + refactor + feature in one) - [ ] This change is a trivial fix, **OR** it links an issue the NetBird team agreed on beforehand. Changes to the public API, gRPC protocols, functionality behavior, CLI / service flags, or new features always need that agreement first. See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#ticket-first-pr-second). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated pull request title validation to accept `android` and `ios` tags. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e90be36cd5 | [client] Don't ask for an SSO login when the login never reached management (#6983) | ||
|
|
d29bc23bb7 |
[client] launch macOS GUI as the logged-in user after install/update (#6962)
## Describe your changes In unattended installs/updates there may be no logged-in user, so there's no context to start the GUI (nor anyone to see it). The bug is the GUI being started in the wrong user context / inheriting the wrong `$HOME` (OS mechanics aside). Today the GUI is started by a per-user LaunchAgent, i.e. on behalf of the user who logs in — no login ⇒ no GUI. The patch aligns to this: it launches the GUI on behalf of the logged-in console user if one exists, otherwise it delegates the launch to the per-user LaunchAgent at next login. Additionally it logs when default UI settings are applied. Note (small caveat): the LaunchAgent auto-starts the GUI at login only once it's been registered — which happens on the first GUI launch in the user's context. On an MDM/unattended fresh install done with no user logged in (where the user has never run the GUI before), they may need to start it manually once; it self-registers from then on. ## Issue ticket number and link No public issue — reported internally (community report on Slack: macOS advanced-view + onboarding reset on every update, esp. via MDM/Munki). Buggy line on main: https://github.com/netbirdio/netbird/blob/dd2bdc0de3aa14dd14dc90611c69c420bb3d3eb2/release_files/darwin_pkg/postinstall#L33 ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) Internal macOS installer / GUI-launch behavior. No public API, CLI, or configuration change: the fix only changes the user context the desktop GUI is launched in after a pkg install/update. ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: N/A <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/netbirdio/codesmith/netbird/pr/6962"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img alt="View with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787923973&installation_model_id=427504&pr_number=6962&repository=netbirdio%2Fnetbird&return_to=https%3A%2F%2Fgithub.com%2Fnetbirdio%2Fnetbird%2Fpull%2F6962&signature=5226bbc7986fc60eb8b4ab77e98ac17a6534864763fc6407048bcbbb1550394b"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a> <sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you need. Autofix is disabled.</sup> <!-- codesmith:autofix:disabled --> <!-- /codesmith:footer --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved macOS installation and updater UI launching to occur only when an active, valid GUI console session is detected. - Prevented UI launches during unattended/system, root, or login-window-related installs. - Ensured the app is launched in the correct console-user context, and skips cleanly when username/UID resolution fails. - **Improvements** - Added clearer informational logging when the UI preferences file is not found and default preferences are used. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ee1389d736 | [client] Keep the UI running when the notification service fails to start (#6959) | ||
|
|
6f42636514 |
[client] Android - Serialize Android tunnel reconfiguration callbacks (#6990)
## Describe your changes The Android route notifier and the DNS search-domain notifier both delivered OnNetworkChanged from a fire-and-forget goroutine per update. Two updates in quick succession could reach the Java side reordered: the TUN rebuild handler applies them in arrival order and compares against the last applied parameters, so a stale route set delivered last won as the final TUN state. This is the same reordering hazard fixed for iOS in #6454. Wrap the Android network change listener into the shared tunnelnotifier FIFO introduced in #6870, the same way RunOniOS does, and deliver both notifiers synchronously into it. Enqueueing is non-blocking, a single delivery goroutine preserves order, and calls into Java never overlap. Also stop hasRouteDiff from sorting the notifier's shared route slices in place; compare sorted copies instead. ## Issue ticket number and link ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] This change does **not** modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — **OR** I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ |
||
|
|
28197e6504 |
[client] Android - Create the Android fake IP manager lazily on DNS flag enable (#6989)
The fake IP manager was only created at route manager construction, from the DNS feature flag fetched by the initial GetNetworkMap call. When the flag flipped to true mid-session, UpdateRoutes set useNewDNSRoute but never created the manager, so domain routes added after the flip got a DNS interceptor with a nil fake IP manager. internalDnatFw only checked for a firewall and GOOS, so the interceptor took the DNAT path and called GetFakeIP/AllocateFakeIP on the nil *fakeip.Manager. These methods lock m.mu first, which is a nil pointer dereference: the first DNS answer for such a route panicked and crashed the VPN service. The fake IP blocks (240.0.0.0/8 and its v6 pair) also never reached the TUN, since only the constructor registered them. Create the manager and its TUN routes from UpdateRoutes when the flag turns on, notify so the fake IP blocks get into the TUN without a client route change, and treat a nil manager as no internal DNAT. This is groundwork for removing the initial GetNetworkMap fetch, after which every startup goes through the flag-off-to-on transition. |
||
|
|
2f721ec0d5 |
[client] Keep the account email backing the SSO login hint correct (#6986)
## Describe your changes Three fixes to how the desktop GUI keeps the account email that backs the SSO `login_hint`. Each is independent and reviewable on its own. **1. Store the email after a GUI SSO login** The daemon returns the authenticated user's email from `WaitSSOLogin` but cannot persist it: it runs as root while the per-profile state file is user-owned. The CLI's `handleSSOLogin` writes it after its own `WaitSSOLogin`; the GUI path read the value and dropped it. The profile was therefore left with no email, so `Profiles.List` showed no account for it, and later logins and session extends went out with no `login_hint` — leaving the IdP to pick an account instead of reusing the one the profile belongs to. Mirror the CLI and store it, next to the `Logout` path that already clears the same file for the same reason. **2. File the email against the profile the login ran for** `SetActiveProfileState` resolves the target itself, so it writes to whichever profile is active when it is called. A GUI SSO login spans seconds of user interaction in the browser, and the tray stays clickable throughout: switching profiles in that window left the email filed under the profile that happened to be active when the flow returned. The wrong profile then advertised an account it does not own, and offered it as the `login_hint` next time. Adds `SetProfileState(id, state)`, the write-side counterpart of the existing `GetProfileState(id)`, and keeps `SetActiveProfileState` as a wrapper for callers with no particular profile in mind. `Login` now reports the profile it resolved so the frontend can hand it back with the SSO wait, which closes the window. **3. Delete the email when a profile is removed** Removing a profile left its state file behind: the daemon deletes what it owns, but the email file is user-owned and out of reach for a root daemon — the same split that already puts the `Logout` cleanup on the UI side. Beyond the stray file, legacy profiles are keyed by name rather than by a generated ID, so recreating a profile under a removed one's name inherited its email — shown as the account in the profile list and sent as the `login_hint` on the next login. ## Issue ticket number and link ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] This change does **not** modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — **OR** I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - SSO login details can now be saved to the specific profile selected during sign-in. - Profile state can be managed independently for different profiles. - **Bug Fixes** - Removing a profile now also cleans up its associated saved state. - Cleanup issues no longer prevent successful profile removal and are handled gracefully. - Existing active-profile behavior remains unchanged. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |