Commit Graph

1268 Commits

Author SHA1 Message Date
riccardom
d806f25b33 CI fixes
- Add docstrings to `mdm_integration`
- refactor for cognitive complexity
- mod tidy
2026-06-09 12:15:49 +02:00
riccardom
b09a07023a Tests MDM config reload via ticker 2026-06-08 18:06:02 +02:00
riccardom
61b5b6b1a1 Introduces support for darwing plist loading 2026-06-08 18:06:02 +02:00
riccardom
83430c39e4 [UI] Fix for "hide" not working when propagating to parent with children 2026-06-08 18:06:02 +02:00
riccardom
47d438976e Pins feat profile retrieval to notif event 2026-06-08 18:06:02 +02:00
riccardom
3bef0fec5b [UI] Removes --disable-advanced-settings 2026-06-08 18:06:02 +02:00
riccardom
be3fe6edd6 [PROTO] Removes --disable-advanced-settings 2026-06-08 18:06:02 +02:00
riccardom
04998f6a1c Removes --disable-advanced-settings
It was a typo in our meetings. the actual thing is --disable-update-settings
2026-06-08 18:06:02 +02:00
riccardom
e55b64f2f7 Fixup: MDM wins. always 2026-06-08 18:06:02 +02:00
riccardom
d6780522ea [UI] Aligns to "enable/disable once on change only" 2026-06-08 18:06:02 +02:00
riccardom
01a4c245a7 Enforces disable networks 2026-06-08 18:06:02 +02:00
riccardom
6355a24deb [UI] Fixup for profile enable/disable toggle
We need to align the initial state to evaluate the delta in case.

The initial state has to be "true" since the profile starts visible.
Then we receive MDM and transition the cache bool value to the actual
MDM imposed state
2026-06-08 18:06:02 +02:00
riccardom
3d4240b979 [UI] Fixup for preshared key 2026-06-08 18:06:02 +02:00
riccardom
2be7ef4929 [UI] Fixup advanced Settings 2026-06-08 18:06:02 +02:00
riccardom
6509174400 Evaluate disable-update-settings errors only when there's an actual override 2026-06-08 18:06:02 +02:00
riccardom
b200f47e6d Adds Gate Login as well when --disable-update-settings=true is given to service
This commit tries to settle things with an old PR-4237 which had relaxed
the case where the SetConfig returned an `Unavailable` code error.

Under this circumnstance the PR allowed the upFunc to just emit a warning and
progress further with the login gRPC. Since the login call is consuming
the --management-url coming from the `up` command, it might be possible
to abuse the "Unavailable" code to inject a management URL that is different
from the configured one even though the --disable-update-settings is set
to true (?)
2026-06-08 18:06:02 +02:00
riccardom
1b477d39a8 Adds support for disabling of Profiles and UpdateSettings feature flags 2026-06-08 18:06:02 +02:00
riccardom
7570c5c911 Toggle gray in/out for Advanced Settings 2026-06-08 18:06:02 +02:00
riccardom
bf8c8b0ea3 Apply MDM locks 2026-06-08 18:06:02 +02:00
riccardom
362efb832c Fixup presharedkey 2026-06-08 18:06:02 +02:00
riccardom
1543d43a20 Advanced Settings locking 2026-06-08 18:06:02 +02:00
riccardom
cee17ebc36 Lock toggle Settngs 2026-06-08 18:06:02 +02:00
riccardom
aab1a3e150 UI behavior conflicts relaxation
UI sends full config snapshot with all values. It doesn't
make sense to block it if the values are aligned with the
values constrained by the MDM policy. It's just simplier
to allow values that are compliant. (this goes for the CLI
as well at this point)
2026-06-08 18:06:02 +02:00
riccardom
1100cea6a4 Add events to resync UI to actual config
This also provide fixup for UI no aligning to changed config when coming from cli up with config flags.
2026-06-08 18:06:02 +02:00
riccardom
029e69279c Publishes event for UI to sync upon MDM changes 2026-06-08 18:06:02 +02:00
riccardom
c759aed3a3 Adds also up/start after cancel 2026-06-08 18:06:02 +02:00
riccardom
c36bf3a171 Adds MDM 1m diff checker & reloader 2026-06-08 18:06:02 +02:00
riccardom
08966adf30 Adds debug config cobra command
This can be useful for troubleshooting and checking config
now that its resolution is not trivial

defaults > config > env cars > CLI/UI > MDM
2026-06-08 18:06:02 +02:00
riccardom
0db22debaf Prefix every log with MDM 2026-06-08 14:37:10 +02:00
riccardom
0594f76655 Adds some log 2026-06-08 14:37:10 +02:00
riccardom
5271df5962 Align split tunnel code 2026-06-08 14:37:10 +02:00
riccardom
2d7949adfe Cleanup setupKey to align to linear 2026-06-08 14:37:10 +02:00
riccardom
451fa5e142 Adds missing WGPort config 2026-06-08 14:37:10 +02:00
riccardom
22edfdd52b Implements windows loading of MDM policy 2026-06-08 14:37:10 +02:00
riccardom
df9e216370 MDM Name scoping for clarity 2026-06-08 14:37:10 +02:00
riccardom
293a93910e gRPC MDM changes 2026-06-08 14:37:10 +02:00
riccardom
66e807cc05 Adds initial 101 MDM policy business logic testing 2026-06-08 14:37:10 +02:00
riccardom
5255f5111b Add ManagedFields to returned config over GetConfig 2026-06-08 14:37:10 +02:00
riccardom
978a392453 Return error if trying to modify any config that is gated by MDM 2026-06-08 14:37:10 +02:00
riccardom
78fe7fc510 Helpers business logic 2026-06-08 14:37:10 +02:00
riccardom
3ad961c9a7 Unit tests 2026-06-08 14:37:10 +02:00
riccardom
607cb56515 Applies MDM override 2026-06-08 14:37:10 +02:00
riccardom
ce0750b73a Initial scaffolding 2026-06-08 14:37:10 +02:00
Maycon Santos
60d2fa08b0 [client] Mask sensitive data in debug bundle creation (#6364)
* [client] Mask sensitive data in debug bundle creation

* Avoid nil reference in turn and use masked constant
2026-06-08 13:17:04 +02:00
Theodor Midtlien
512899d82d [client] Prevent corruption from competing log rotation and improve debug bundle (#6214)
* Adds heuristic to detect an edge case on Linux where a system has configured logrotate as a separate service to rotate log files which would mangle our client log files. If we detect logrotate being configured for netbird, we disable our rotation.

* Adds new env var to disable log rotation: NB_LOG_DISABLE_ROTATION

* Adds compressed and plain logrotate files to debug bundle.

* Replaces lumberjack with timberjack (maintained fork with bug fixes and extra features).

* Clarifies which daemon version is running in the bundle stats.

* Change logging for client service status to console
2026-06-04 17:36:45 +02:00
Theodor Midtlien
5993ec6e43 [client] Allow wireguard port to be zero in UI and show port in status command (#6158)
* Allow wireguard port to be set to 0 in UI

* Add wireguard port to cmd status

* Correct protoc version
2026-06-04 15:04:11 +02:00
Zoltan Papp
3e61ccb162 [client] Persist sync response via pluggable store (disk on iOS) (#6331)
* Persist sync response via pluggable store (disk on iOS)

The latest Management sync response (which carries the network map) was
kept in memory for debug bundle generation. On memory-constrained
platforms like iOS the network map can be large enough to matter.

Introduce a syncstore package with a Store interface and two backends:
a memory backend (the previous behavior) and a disk backend that
serializes the response to a file in the state directory. The backend
is selected per-platform at build time: disk on iOS, memory elsewhere.

The disk store clears any leftover file on construction so a fresh
store never reads stale data from an earlier run (e.g. another
profile's network map).

In the engine, drop the separate persistSyncResponse bool: the store is
only instantiated while persistence is enabled, and its presence is
what marks persistence as active. The store is also cleared on engine
close so the file does not linger on disk.

* syncstore: silence nilnil linter on "nothing stored" returns

Get returns (nil, nil) to signal that nothing is stored, which is part
of the Store contract and preserves the original behaviour. Annotate
both backends with //nolint:nilnil so golangci-lint does not flag it.

* syncstore: hold syncRespMux for the whole store Set/Get

Both handleSync and GetLatestSyncResponse snapshotted e.syncStore under
the read lock and then released it before calling Set/Get. That allowed
SetSyncResponsePersistence(false) or engine close to clear the store
mid-call. In particular a concurrent Clear()+nil followed by a late
Set could re-create the file that was just removed, defeating the
leak/lingering protection.

Hold syncRespMux for the duration of the store operation in both spots
so the store cannot be cleared while a Set/Get is in flight.

* syncstore: avoid StateDir "." when state path is empty

On mobile the state path may be empty (the engine tolerates a missing
state file). filepath.Dir("") returns ".", which would make a
disk-backed syncstore write into the working directory instead of
letting NewDiskStore fall back to os.TempDir().

Only set engineConfig.StateDir when path is non-empty.
2026-06-03 14:18:50 +02:00
Viktor Liu
a48c20d8d8 [client] Gate DNS forwarder on BlockInbound (#6257) 2026-06-03 11:33:29 +02:00
Riccardo Manfrin
2b57a7d43b [client, management, misc] expose VCS revision in dev build version output (#6263)
* Refactor to use a common checker for development version

* Adds commit sha to development version for cobra command only

Leave dashboard unaffected

* Adjust for "v0.31.1-dev" test case

which must be considered pre-release

* Drop synthetic "dev"/"0.50.0-dev" firewall feature-gate fixtures

These test cases encoded the loose strings.Contains(v, "dev")
semantics inherited from peerSupportedFirewallFeatures, but
NetbirdVersion() never produces those values — only the literal
"development" (and now "development-<sha>[-dirty]") ever flows
through the wire. The agent owns the semantics of an ephemeral
development build, so the tests should exercise the strings we
actually emit.

Replaced with development, development-<sha> and
development-<sha>-dirty cases that match the HasPrefix("development")
predicate introduced upstream.

* Remove unexistent tests on wire format

The sha / dirty flag are added only when the CLI asks the version.
Account versions is unaffacted and can only strictly match "development"

* Adds tests for IsDevelopmentVersion
2026-06-03 08:56:50 +02:00
Maycon Santos
fa1e241aea [management, client, proxy] Follow-up fixes for private reverse-proxy services (#6268)
* fix(proxy): gate tunnel-peer fast-path on inbound listener marker

forwardWithTunnelPeer previously accepted any RFC1918 / ULA / CGNAT
source IP, so a public client whose address happened to fall in those
ranges could bypass the configured operator auth scheme by colliding
with a known tunnel IP. The fast-path is now gated on
TunnelLookupFromContext(r.Context()) being present — that context value
is attached only by the per-account inbound (overlay) listener, so the
host-facing listener never enters this branch.

Tests updated to reflect the new requirement: requests that don't
carry the inbound marker now fall through to the regular auth flow.

* fix(proxy): harden inbound listener resource + startup-ctx handling

Three correctness fixes on the per-account inbound path, with tests:

- Close the logrus ErrorLog PipeWriter on tearDown. WriterLevel hands
  back an *io.PipeWriter backed by a pipe + scanner goroutine that the
  caller owns; the two writers per account (https + plain) were never
  closed, leaking the pipe and goroutine on every teardown.
- Run the post-Start hooks on context.Background(). runClientStartup
  is launched in a goroutine from AddPeer and was inheriting the
  caller's request-scoped ctx, so a cancelled request could abort the
  inbound bring-up or fail the management status notification. The
  tail is split into notifyClientReady so the contract is testable.

Tests cover the PipeWriter close behaviour and assert the readyHandler
+ NotifyStatus calls receive a non-cancelled background context.

* feat(proxy): short-circuit peer-own-target loops with 421

When a peer that hosts the target of a private service dials its own
service URL the request was being looped through the proxy and back
over WireGuard to the same peer — twice the WG round-trip for no
benefit, with no signal to the caller that something was wrong.

Add isSelfTargetLoop to ReverseProxy.ServeHTTP: when the request
arrived on the per-account overlay listener (IsOverlayOrigin) and the
source tunnel IP matches the target host, refuse the request with 421
Misdirected Request and a body pointing the operator at the backend
directly.

The gate is scoped to overlay origin so requests on the public
listener that happen to share a source IP with the target host are
forwarded normally.

* fix(management): private-service validation + tunnel-IP lookup semantics

- Require an explicit port for L4 cluster targets. validateL4Target
  exempted TargetTypeCluster from the port check, but buildPathMappings
  serializes every L4 target via net.JoinHostPort(host, port) — port=0
  shipped a ":0" upstream. Cluster targets use the same Host/Port
  fields, so the same requirement applies.
- GetPeerByIP returns NotFound on a tunnel-IP miss instead of mapping
  every error to Internal. The proxy's ValidateTunnelPeer probes IPs
  that legitimately aren't in the roster; the miss is expected and now
  distinguishable from a real store failure.
- Thread ctx into getClusterCapability's gorm query so a cancelled
  request doesn't keep the store busy.

Tests updated for the L4-cluster port requirement and the GetPeerByIP
NotFound path.

* fix(client): include offlinePeers in PeerStateByIP lookup

ReplaceOfflinePeers moves peers into d.offlinePeers but PeerStateByIP
only scanned d.peers. Callers (the local DNS filter via
localPeerConnectivity, embed.Client.IdentityForIP used by the
proxy's tunnel-peer validator) were treating known-but-offline peers
as unknown, which:

- causes the DNS filter to keep returning records pointing at peers
  that have no live tunnel, AND
- makes the proxy's local-roster check deny a request from such a
  peer rather than letting the cached management RPC carry the
  authorisation decision.

Search both slices in PeerStateByIP. Adds a unit test for the IPv4
and IPv6 offline-match paths.

* fix(rest): reject empty Delete path params in reverse-proxy clients

ReverseProxyClustersAPI.Delete and ReverseProxyTokensAPI.Delete passed
the path parameter into url.PathEscape without an empty check.
PathEscape("") returns "" which collapses the request onto the
collection endpoint ("/api/reverse-proxies/clusters/" /
"/api/reverse-proxies/proxy-tokens/"), so a caller bug delete with no
id reached a routable URL with surprising semantics (typically 405).

Short-circuit with a typed error before the request is built. Tests
mount a handler on the collection path that fails the test if hit, so
the regression is impossible to reintroduce silently.

* chore(api,ci,docs,test): private-service schema, proto-check, fixups

Non-functional cleanups and contract/CI hardening around the
private-service work:

API schema (openapi.yml):
- Require a non-empty access_groups and mode=http when private=true,
  on both Service and ServiceRequest, mirroring
  validatePrivateRequirements. mode stays optional-but-constrained
  (empty defaults to http server-side), matching runtime.

CI (proto-version-check.yml):
- Cover renamed .pb.go files (read base via previous_filename).
- Match protoc-gen-go-grpc version headers (optional "- " prefix and
  -gen-go-grpc suffix) so grpc-generated files are in scope.

Docs / comments:
- Reword Config field docs to say defaults are applied at Server.Start
  (initDefaults), not New.
- Rename the obsolete --private-inbound flag to --private across
  comments and the proto doc.

Pre-existing test fixups surfaced by review:
- Repair the integration-tagged validate_session_test.go (SignToken
  signature growth + new Manager interface methods).
- Fix the CI-skip boolean precedence so Windows isn't skipped
  unconditionally.
- Guard the router.HTTPListener type assertion with comma-ok.

* fix(proxy): background ctx for already-started AddPeer notification

The earlier ctx fix covered the async runClientStartup path but missed
the synchronous branch: when a service is added to an already-started
client, AddPeer called NotifyStatus with the caller's request-scoped
ctx. A cancelled request/stream could drop the connected notification
to management. Use context.Background() here too, matching
notifyClientReady.

Extends TestNetBird_AddPeer_ExistingStartedClient_NotifiesStatus to
pass a pre-cancelled caller ctx and assert the notification still ran
on a non-cancelled context.

* use the cmd context for roundtripper
2026-06-02 13:40:09 +02:00