Merge branch 'main' into ipc-authz-profile-ownership
@@ -4,8 +4,30 @@ This repository contains assets required to build the [documentation website for
|
||||
|
||||
We're glad that you want to contribute!
|
||||
|
||||
- [Reporting bugs and requesting features](#reporting-bugs-and-requesting-features)
|
||||
- [Contributing to the docs](#contributing-to-the-docs)
|
||||
|
||||
## Reporting bugs and requesting features
|
||||
|
||||
NetBird uses a discussion-first workflow across all its repositories. Bug reports and
|
||||
feature requests for the docs start in
|
||||
[netbird Discussions](https://github.com/netbirdio/netbird/discussions), not as issues here.
|
||||
|
||||
| What you want to do | Where to go |
|
||||
| --- | --- |
|
||||
| Report a bug, regression, or unexpected behavior | [Issue Triage](https://github.com/netbirdio/netbird/discussions/new?category=issue-triage) |
|
||||
| Request a feature or share an idea | [Ideas & Feature Requests](https://github.com/netbirdio/netbird/discussions/new?category=ideas-feature-requests) |
|
||||
| Ask about setup, configuration, or self-hosting | [Q&A / Support](https://github.com/netbirdio/netbird/discussions/new?category=q-a-support) |
|
||||
| Report a security vulnerability | [Security policy](https://github.com/netbirdio/netbird/security/policy), never a public thread |
|
||||
|
||||
Our team triages each discussion. Validated reports become issues in this repository,
|
||||
linked back to the discussion. See
|
||||
[How to use Discussions, Issues, and Pull Requests](https://github.com/netbirdio/netbird/discussions/6075)
|
||||
for the full workflow.
|
||||
|
||||
Small fixes like typos, broken links, or documentation corrections can go straight to a
|
||||
[pull request](#contributing-to-the-docs).
|
||||
|
||||
### Requirements
|
||||
* node 16
|
||||
* npm 8+
|
||||
|
||||
@@ -54,7 +54,7 @@ function rehypeInsertLastUpdated() {
|
||||
type: 'element',
|
||||
tagName: 'p',
|
||||
properties: {
|
||||
className: ['not-prose', 'text-sm', 'text-slate-400', 'dark:text-zinc-500', 'mt-0', 'mb-8', 'ml-2.5'],
|
||||
className: ['not-prose', 'text-sm', 'text-slate-400', 'dark:text-zinc-500', 'mt-0', 'mb-8', 'md:ml-2.5'],
|
||||
},
|
||||
children: [
|
||||
{ type: 'text', value: 'Updated ' },
|
||||
@@ -165,11 +165,30 @@ function rehypeAddMDXExports(getExports) {
|
||||
}
|
||||
}
|
||||
|
||||
// Components that render their own `h2` from `title` and `id` props. The
|
||||
// heading is not in the MDX tree, so it is added to the table of contents here.
|
||||
const sectionComponents = ['TroubleshootingStart', 'TroubleshootingTiles', 'StillStuck']
|
||||
|
||||
function jsxStringAttribute(node, name) {
|
||||
let attribute = node.attributes?.find((a) => a.name === name)
|
||||
return typeof attribute?.value === 'string' ? attribute.value : undefined
|
||||
}
|
||||
|
||||
function getSections(node) {
|
||||
let sections = []
|
||||
|
||||
for (let child of node.children ?? []) {
|
||||
if (child.type === 'element' && ['h2', 'h3', 'h4', 'h5', 'h6'].includes(child.tagName)) {
|
||||
if (child.type === 'mdxJsxFlowElement' && sectionComponents.includes(child.name)) {
|
||||
let title = jsxStringAttribute(child, 'title')
|
||||
let id = jsxStringAttribute(child, 'id')
|
||||
if (title && id) {
|
||||
sections.push(`{
|
||||
title: ${JSON.stringify(title)},
|
||||
id: ${JSON.stringify(id)},
|
||||
tagName: "h2"
|
||||
}`)
|
||||
}
|
||||
} else if (child.type === 'element' && ['h2', 'h3', 'h4', 'h5', 'h6'].includes(child.tagName)) {
|
||||
sections.push(`{
|
||||
title: ${JSON.stringify(toString(child))},
|
||||
id: ${JSON.stringify(child.properties.id)},
|
||||
|
||||
@@ -27,7 +27,7 @@ const nextConfig = {
|
||||
return [
|
||||
{
|
||||
source: '/slack-url',
|
||||
destination: 'https://join.slack.com/t/netbirdio/shared_invite/zt-43j76787p-otf3d0nMcJHAQYx46H3jsg',
|
||||
destination: 'https://join.slack.com/t/netbirdio/shared_invite/zt-4bkyz66cn-Bs~A8lAgIPii5bDl6QUXyA',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
@@ -910,7 +910,12 @@ const nextConfig = {
|
||||
},
|
||||
{
|
||||
source: '/selfhosted/activity-mysql-store',
|
||||
destination: '/selfhosted/maintenance/activity-mysql-store',
|
||||
destination: '/selfhosted/maintenance/activity-postgres-store',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/selfhosted/maintenance/activity-mysql-store',
|
||||
destination: '/selfhosted/maintenance/activity-postgres-store',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
<!--
|
||||
NetBird MDM preferences (macOS) — bare plist for MDM platforms that
|
||||
accept a managed-preferences plist tied to a bundle identifier
|
||||
(e.g. JumpCloud "Mac Application Custom Settings", Mosyle "Custom
|
||||
Settings", Jamf "Application & Custom Settings" → External
|
||||
Application).
|
||||
(e.g. Jamf Pro "Application & Custom Settings" → External
|
||||
Applications).
|
||||
|
||||
Bundle identifier (preference domain): io.netbird.client
|
||||
|
||||
@@ -18,7 +17,8 @@
|
||||
loader reads on every 1-minute MDM reload tick.
|
||||
|
||||
For MDM platforms that expect a full Configuration Profile instead
|
||||
of a bare plist (Custom Configuration Profile / .mobileconfig upload),
|
||||
of a bare plist (a .mobileconfig upload, such as JumpCloud's "MDM
|
||||
Custom Configuration Profile" policy),
|
||||
use docs/netbird-macos.mobileconfig — same keys, additional Payload*
|
||||
envelope.
|
||||
|
||||
@@ -85,6 +85,15 @@
|
||||
<false/>
|
||||
-->
|
||||
|
||||
<!-- ===== Lazy connections =====
|
||||
true forces lazy connections on, false forces them off, and an
|
||||
absent key defers to the Management setting. NB_LAZY_CONN
|
||||
takes precedence when both are configured. -->
|
||||
<!--
|
||||
<key>lazyConnection</key>
|
||||
<true/>
|
||||
-->
|
||||
|
||||
<!-- ===== WireGuard UDP port =====
|
||||
Range 1-65535. Omit to keep the daemon default. -->
|
||||
<!--
|
||||
@@ -92,6 +101,33 @@
|
||||
<integer>51820</integer>
|
||||
-->
|
||||
|
||||
<!-- ===== Remote jobs =====
|
||||
allowRemoteJobs : allow management-requested remote jobs
|
||||
(e.g. debug bundles) on this peer. Off by
|
||||
default. Present at any value locks the
|
||||
client toggle, so false pins it off.
|
||||
debugBundleUploadURL: override the upload service used for
|
||||
debug bundles produced by remote jobs,
|
||||
taking precedence over the value
|
||||
requested by Management. Must be an
|
||||
https URL with a host. -->
|
||||
<!--
|
||||
<key>allowRemoteJobs</key>
|
||||
<false/>
|
||||
|
||||
<key>debugBundleUploadURL</key>
|
||||
<string>https://uploads.example.com</string>
|
||||
-->
|
||||
|
||||
<!-- ===== Local Prometheus metrics endpoint ===== -->
|
||||
<!--
|
||||
<key>enableLocalMetrics</key>
|
||||
<true/>
|
||||
|
||||
<key>localMetricsAddress</key>
|
||||
<string>127.0.0.1:9191</string>
|
||||
-->
|
||||
|
||||
<!-- ===== UI / lockdown kill switches =====
|
||||
disableUpdateSettings : block every config change from UI and CLI
|
||||
on this device (Settings view stays
|
||||
@@ -99,7 +135,10 @@
|
||||
disableProfiles : hide the profile menu, reject profile CRUD.
|
||||
disableNetworks : hide the Networks / Exit Node menus,
|
||||
reject the related RPCs.
|
||||
disableMetricsCollection: opt out of anonymous usage telemetry. -->
|
||||
disableAdvancedView : hide the advanced section of the UI.
|
||||
UI-only: nothing is rejected on its
|
||||
account.
|
||||
disableMetricsCollection: reserved; recognized but no effect yet. -->
|
||||
<!--
|
||||
<key>disableUpdateSettings</key>
|
||||
<true/>
|
||||
@@ -110,6 +149,9 @@
|
||||
<key>disableNetworks</key>
|
||||
<true/>
|
||||
|
||||
<key>disableAdvancedView</key>
|
||||
<true/>
|
||||
|
||||
<key>disableMetricsCollection</key>
|
||||
<false/>
|
||||
-->
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<!--
|
||||
NetBird Managed App Configuration (iOS / iPadOS / tvOS).
|
||||
|
||||
Bundle identifier of the managed app:
|
||||
io.netbird.app (iOS, iPadOS)
|
||||
io.netbird.app.tv (tvOS)
|
||||
|
||||
Delivery: your MDM sends this dictionary alongside the managed app
|
||||
(Intune "App configuration policies" → Enter XML data; Jamf Pro
|
||||
"Mobile Device Apps → App Configuration"; the equivalent App
|
||||
Configuration field on Kandji, Mosyle, Workspace ONE, JumpCloud).
|
||||
iOS hands it to the app, which reads it on every policy load.
|
||||
|
||||
The app must be MANAGED by the MDM that sends this configuration.
|
||||
It need not have been installed by it: the MDM can take over a copy
|
||||
the user installed from the App Store (the user must accept on an
|
||||
unsupervised device). Until then the app receives no configuration
|
||||
and NetBird runs with no policy at all.
|
||||
|
||||
Editing this file:
|
||||
- Delete or comment out every key you do NOT want to enforce. An
|
||||
absent key means "no enforcement" for that field.
|
||||
- Some MDM consoles want only the inner <dict>; paste from
|
||||
<dict> to </dict> and drop the plist envelope in that case.
|
||||
- Validate locally with:
|
||||
plutil -lint netbird-ios-appconfig.plist
|
||||
|
||||
When a change takes effect: iOS hands this configuration to the
|
||||
NetBird app, and the app passes it to the network extension that
|
||||
runs the tunnel whenever the app is running. With the app in the
|
||||
foreground a change reaches the tunnel within about 30 seconds;
|
||||
while the app is closed or backgrounded iOS suspends it, so a
|
||||
policy pushed (or withdrawn) in that window applies the next time
|
||||
the app is opened. A tunnel brought up by VPN On Demand or the
|
||||
widget runs on the last policy the app saw.
|
||||
|
||||
tvOS: only the interface is enforced. The hand-off needs a shared
|
||||
app group, which does not work between the tvOS app and its
|
||||
extension, so the tvOS engine runs without the policy.
|
||||
|
||||
Keys that do not apply to the platform are parsed and ignored here,
|
||||
so one payload can be shared with desktop devices: allowServerSSH,
|
||||
disableServerRoutes, disableAutostart, disableMetricsCollection,
|
||||
enableLocalMetrics, localMetricsAddress, splitTunnelMode,
|
||||
splitTunnelApps.
|
||||
|
||||
See the iOS section of docs/src/pages/client/mdm-integration.mdx.
|
||||
-->
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
|
||||
<!-- ===== Identity / auth ===== -->
|
||||
<key>managementURL</key>
|
||||
<string>https://api.netbird.io:443</string>
|
||||
|
||||
<!--
|
||||
Pre-shared key: secret. Remove the entry entirely when not
|
||||
used; do NOT leave an empty <string></string>, which the
|
||||
client treats as a deliberate empty-PSK enforcement.
|
||||
-->
|
||||
<!--
|
||||
<key>preSharedKey</key>
|
||||
<string>REPLACE_ME</string>
|
||||
-->
|
||||
|
||||
<!-- ===== Interface lockdown =====
|
||||
disableUpdateSettings : read-only mode — nothing is hidden,
|
||||
every configuration control is locked.
|
||||
disableProfiles : hide the Profiles section (no create,
|
||||
switch or remove).
|
||||
disableNetworks : remove the Resources tab.
|
||||
disableAdvancedView : hide Advanced (on tvOS, the whole
|
||||
section). -->
|
||||
<!--
|
||||
<key>disableUpdateSettings</key>
|
||||
<true/>
|
||||
|
||||
<key>disableProfiles</key>
|
||||
<true/>
|
||||
|
||||
<key>disableNetworks</key>
|
||||
<true/>
|
||||
|
||||
<key>disableAdvancedView</key>
|
||||
<true/>
|
||||
-->
|
||||
|
||||
<!-- ===== Connection controls =====
|
||||
Each of these locks its own control in the app. -->
|
||||
<!--
|
||||
<key>disableAutoConnect</key>
|
||||
<false/>
|
||||
|
||||
<key>disableClientRoutes</key>
|
||||
<false/>
|
||||
|
||||
<key>rosenpassEnabled</key>
|
||||
<true/>
|
||||
|
||||
<key>rosenpassPermissive</key>
|
||||
<false/>
|
||||
-->
|
||||
|
||||
<!-- ===== Remote jobs =====
|
||||
allowRemoteJobs present at any value locks the client toggle,
|
||||
so pushing false pins remote jobs off. debugBundleUploadURL
|
||||
overrides where a bundle produced by a remote job is sent. -->
|
||||
<!--
|
||||
<key>allowRemoteJobs</key>
|
||||
<false/>
|
||||
|
||||
<key>debugBundleUploadURL</key>
|
||||
<string>https://uploads.example.com</string>
|
||||
-->
|
||||
|
||||
<!-- ===== Applied by the tunnel engine =====
|
||||
No control to lock in the app - the engine applies these to
|
||||
the connection itself. -->
|
||||
<!--
|
||||
<key>blockInbound</key>
|
||||
<false/>
|
||||
|
||||
<key>wireguardPort</key>
|
||||
<integer>51820</integer>
|
||||
|
||||
<key>lazyConnection</key>
|
||||
<true/>
|
||||
-->
|
||||
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -16,11 +16,22 @@
|
||||
(confirm against the signed pkg before fleet roll-out)
|
||||
|
||||
Distribution:
|
||||
- sign with `productsign --sign "Developer ID Installer: ..." ...`
|
||||
before fleet roll-out (Apple-Configurator-2 won't install an
|
||||
unsigned profile on Sonoma+ without user override).
|
||||
- For local dev install: `sudo profiles install -path netbird-macos.mobileconfig`.
|
||||
- For MDM (Jamf/Kandji/Mosyle/Intune): upload as a Custom Profile.
|
||||
- delivered through an MDM, the profile is signed by the MDM
|
||||
channel itself; nothing to sign by hand,
|
||||
- handed out any other way (Apple Configurator, double-click), it
|
||||
installs unsigned and asks the user to confirm on recent macOS.
|
||||
To avoid that, sign it as a CMS message with an identity the
|
||||
target Macs trust:
|
||||
security cms -S -N "Your Signing Identity" \
|
||||
-i netbird-macos.mobileconfig -o signed.mobileconfig
|
||||
Configuration profiles are not signed with productsign, which
|
||||
handles installer packages.
|
||||
- For a local test: double-click the file, then install it from
|
||||
System Settings > General > Device Management. macOS 11 and later
|
||||
cannot install configuration profiles with the `profiles` command.
|
||||
- For MDM (Kandji/Mosyle/Intune/Workspace ONE/JumpCloud): upload as
|
||||
a custom profile. Jamf Pro: prefer io.netbird.client.plist in
|
||||
Application & Custom Settings, or sign this profile before upload.
|
||||
|
||||
Editing:
|
||||
- Replace UUID placeholders below with fresh UUIDs (`uuidgen` on
|
||||
@@ -121,6 +132,15 @@
|
||||
<false/>
|
||||
-->
|
||||
|
||||
<!-- ===== Lazy connections (bool) =====
|
||||
true forces them on, false forces them off, an
|
||||
absent key defers to the Management setting.
|
||||
NB_LAZY_CONN wins over both. -->
|
||||
<!--
|
||||
<key>lazyConnection</key>
|
||||
<true/>
|
||||
-->
|
||||
|
||||
<!-- ===== WireGuard UDP port (int) =====
|
||||
Range 1-65535. Omit to keep the default. -->
|
||||
<!--
|
||||
@@ -128,6 +148,27 @@
|
||||
<integer>51820</integer>
|
||||
-->
|
||||
|
||||
<!-- ===== Remote jobs =====
|
||||
allowRemoteJobs is off by default; present at any
|
||||
value it locks the client toggle, so false pins
|
||||
remote jobs off. debugBundleUploadURL overrides the
|
||||
upload service for bundles produced by remote jobs
|
||||
and must be an https URL with a host. -->
|
||||
<!--
|
||||
<key>allowRemoteJobs</key>
|
||||
<false/>
|
||||
<key>debugBundleUploadURL</key>
|
||||
<string>https://uploads.example.com</string>
|
||||
-->
|
||||
|
||||
<!-- ===== Local Prometheus metrics endpoint ===== -->
|
||||
<!--
|
||||
<key>enableLocalMetrics</key>
|
||||
<true/>
|
||||
<key>localMetricsAddress</key>
|
||||
<string>127.0.0.1:9191</string>
|
||||
-->
|
||||
|
||||
<!-- ===== Split tunnel (Android-only at the daemon level)
|
||||
Pushed harmlessly on macOS for fleets with mixed
|
||||
desktop+mobile devices; the macOS daemon ignores it. -->
|
||||
@@ -138,7 +179,9 @@
|
||||
<string>com.acme.app1,com.acme.app2</string>
|
||||
-->
|
||||
|
||||
<!-- ===== UI / kill switches (bool) ===== -->
|
||||
<!-- ===== UI / kill switches (bool) =====
|
||||
disableAdvancedView is UI-only: it hides the
|
||||
advanced section and rejects nothing. -->
|
||||
<!--
|
||||
<key>disableUpdateSettings</key>
|
||||
<true/>
|
||||
@@ -146,6 +189,8 @@
|
||||
<true/>
|
||||
<key>disableNetworks</key>
|
||||
<true/>
|
||||
<key>disableAdvancedView</key>
|
||||
<true/>
|
||||
<key>disableMetricsCollection</key>
|
||||
<false/>
|
||||
-->
|
||||
|
||||
@@ -53,9 +53,9 @@ set -euo pipefail
|
||||
# docs/netbird.admx + .adml (Windows ADMX schema)
|
||||
#
|
||||
NULL='__UNSET__'
|
||||
managementURL='https://api.netbird.io:443'
|
||||
managementURL="$NULL"
|
||||
preSharedKey="$NULL" # secret; redacted in log
|
||||
allowServerSSH='true'
|
||||
allowServerSSH="$NULL"
|
||||
blockInbound="$NULL"
|
||||
disableAutoConnect="$NULL"
|
||||
disableAutostart="$NULL"
|
||||
@@ -65,9 +65,15 @@ disableMetricsCollection="$NULL"
|
||||
disableUpdateSettings="$NULL"
|
||||
disableProfiles="$NULL"
|
||||
disableNetworks="$NULL"
|
||||
disableAdvancedView="$NULL" # UI-only: hides the advanced section, rejects nothing
|
||||
rosenpassEnabled="$NULL"
|
||||
rosenpassPermissive="$NULL"
|
||||
wireguardPort='51820'
|
||||
lazyConnection="$NULL" # "true"/"false"; absent defers to the Management setting
|
||||
wireguardPort="$NULL"
|
||||
allowRemoteJobs="$NULL" # present at any value locks the client toggle
|
||||
debugBundleUploadURL="$NULL" # https URL with a host
|
||||
enableLocalMetrics="$NULL" # local Prometheus endpoint; unrelated to disableMetricsCollection
|
||||
localMetricsAddress="$NULL" # default 127.0.0.1:9191
|
||||
splitTunnelMode="$NULL" # "allow" or "disallow", Android-only at the daemon level
|
||||
splitTunnelApps="$NULL" # comma-separated app IDs, Android-only
|
||||
##############################################################################
|
||||
@@ -175,9 +181,15 @@ main() {
|
||||
is_set "$disableUpdateSettings" && emit_bool disableUpdateSettings "$disableUpdateSettings"
|
||||
is_set "$disableProfiles" && emit_bool disableProfiles "$disableProfiles"
|
||||
is_set "$disableNetworks" && emit_bool disableNetworks "$disableNetworks"
|
||||
is_set "$disableAdvancedView" && emit_bool disableAdvancedView "$disableAdvancedView"
|
||||
is_set "$rosenpassEnabled" && emit_bool rosenpassEnabled "$rosenpassEnabled"
|
||||
is_set "$rosenpassPermissive" && emit_bool rosenpassPermissive "$rosenpassPermissive"
|
||||
is_set "$lazyConnection" && emit_bool lazyConnection "$lazyConnection"
|
||||
is_set "$wireguardPort" && emit_int wireguardPort "$wireguardPort"
|
||||
is_set "$allowRemoteJobs" && emit_bool allowRemoteJobs "$allowRemoteJobs"
|
||||
is_set "$debugBundleUploadURL" && emit_string debugBundleUploadURL "$debugBundleUploadURL"
|
||||
is_set "$enableLocalMetrics" && emit_bool enableLocalMetrics "$enableLocalMetrics"
|
||||
is_set "$localMetricsAddress" && emit_string localMetricsAddress "$localMetricsAddress"
|
||||
is_set "$splitTunnelMode" && emit_split_tunnel_mode "$splitTunnelMode"
|
||||
is_set "$splitTunnelApps" && emit_string splitTunnelApps "$splitTunnelApps"
|
||||
|
||||
|
||||
@@ -64,7 +64,25 @@
|
||||
<string id="DisableNetworks_Help">When enabled, the client UI/CLI cannot list, select or deselect NetBird networks (the corresponding daemon RPCs return Unavailable). Equivalent to --disable-networks.</string>
|
||||
|
||||
<string id="DisableMetricsCollection_Name">Disable metrics collection</string>
|
||||
<string id="DisableMetricsCollection_Help">When enabled, the client does not collect or report local usage metrics.</string>
|
||||
<string id="DisableMetricsCollection_Help">Reserved for a future client telemetry feature. The client recognizes this setting but it has no effect in current releases.</string>
|
||||
|
||||
<string id="DisableAdvancedView_Name">Disable advanced view</string>
|
||||
<string id="DisableAdvancedView_Help">When enabled, the client hides the advanced section of its interface. This policy only changes what the interface offers: it is not part of the daemon configuration and never causes a configuration change to be rejected. Setting it to Disabled explicitly re-enables the section.</string>
|
||||
|
||||
<string id="LazyConnection_Name">Lazy connections</string>
|
||||
<string id="LazyConnection_Help">Local override for lazy connections. Enabled forces lazy connections on, Disabled forces them off, and leaving the policy Not Configured defers to the Management setting. The NB_LAZY_CONN environment variable takes precedence over this policy.</string>
|
||||
|
||||
<string id="AllowRemoteJobs_Name">Allow remote jobs</string>
|
||||
<string id="AllowRemoteJobs_Help">Allows management-requested remote jobs, such as debug bundle requests, to run on this peer. Off by default; equivalent to --allow-remote-jobs. Configuring this policy at either value locks the corresponding client toggle, so Disabled pins remote jobs off.</string>
|
||||
|
||||
<string id="DebugBundleUploadURL_Name">Debug bundle upload URL</string>
|
||||
<string id="DebugBundleUploadURL_Help">Overrides the upload service used for debug bundles produced by remote jobs, taking precedence over the value requested by Management. Must be an https URL including a host.</string>
|
||||
|
||||
<string id="EnableLocalMetrics_Name">Enable local metrics endpoint</string>
|
||||
<string id="EnableLocalMetrics_Help">Exposes the client's local Prometheus /metrics endpoint. Unrelated to "Disable metrics collection".</string>
|
||||
|
||||
<string id="LocalMetricsAddress_Name">Local metrics listen address</string>
|
||||
<string id="LocalMetricsAddress_Help">Listen address of the local Prometheus /metrics endpoint. Defaults to 127.0.0.1:9191 when the endpoint is enabled and this policy is Not Configured.</string>
|
||||
|
||||
</stringTable>
|
||||
<presentationTable>
|
||||
@@ -93,6 +111,19 @@
|
||||
</textBox>
|
||||
</presentation>
|
||||
|
||||
<presentation id="DebugBundleUploadURL_Pres">
|
||||
<textBox refId="DebugBundleUploadURL_Text">
|
||||
<label>Debug bundle upload URL:</label>
|
||||
</textBox>
|
||||
</presentation>
|
||||
|
||||
<presentation id="LocalMetricsAddress_Pres">
|
||||
<textBox refId="LocalMetricsAddress_Text">
|
||||
<label>Listen address:</label>
|
||||
<defaultValue>127.0.0.1:9191</defaultValue>
|
||||
</textBox>
|
||||
</presentation>
|
||||
|
||||
</presentationTable>
|
||||
</resources>
|
||||
</policyDefinitionResources>
|
||||
|
||||
@@ -231,5 +231,79 @@
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="DisableAdvancedView"
|
||||
class="Machine"
|
||||
displayName="$(string.DisableAdvancedView_Name)"
|
||||
explainText="$(string.DisableAdvancedView_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="DisableAdvancedView">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="LazyConnection"
|
||||
class="Machine"
|
||||
displayName="$(string.LazyConnection_Name)"
|
||||
explainText="$(string.LazyConnection_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="LazyConnection">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="AllowRemoteJobs"
|
||||
class="Machine"
|
||||
displayName="$(string.AllowRemoteJobs_Name)"
|
||||
explainText="$(string.AllowRemoteJobs_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="AllowRemoteJobs">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="DebugBundleUploadURL"
|
||||
class="Machine"
|
||||
displayName="$(string.DebugBundleUploadURL_Name)"
|
||||
explainText="$(string.DebugBundleUploadURL_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
presentation="$(presentation.DebugBundleUploadURL_Pres)">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<elements>
|
||||
<text id="DebugBundleUploadURL_Text" valueName="DebugBundleUploadURL" required="true" />
|
||||
</elements>
|
||||
</policy>
|
||||
|
||||
<policy name="EnableLocalMetrics"
|
||||
class="Machine"
|
||||
displayName="$(string.EnableLocalMetrics_Name)"
|
||||
explainText="$(string.EnableLocalMetrics_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="EnableLocalMetrics">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="LocalMetricsAddress"
|
||||
class="Machine"
|
||||
displayName="$(string.LocalMetricsAddress_Name)"
|
||||
explainText="$(string.LocalMetricsAddress_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
presentation="$(presentation.LocalMetricsAddress_Pres)">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<elements>
|
||||
<text id="LocalMetricsAddress_Text" valueName="LocalMetricsAddress" required="true" />
|
||||
</elements>
|
||||
</policy>
|
||||
|
||||
</policies>
|
||||
</policyDefinitions>
|
||||
|
||||
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 294 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 476 KiB |
|
Before Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 39 KiB |
@@ -15,7 +15,17 @@ export function HeroPattern() {
|
||||
[7, 3],
|
||||
[10, 6],
|
||||
]}
|
||||
className="absolute inset-x-0 inset-y-[-50%] h-[200%] w-full skew-y-[-18deg] fill-black/40 stroke-black/50 mix-blend-overlay dark:fill-white/2.5 dark:stroke-white/5"
|
||||
className="absolute
|
||||
inset-x-0
|
||||
inset-y-[-50%]
|
||||
h-[200%]
|
||||
w-full
|
||||
skew-y-[-18deg]
|
||||
supports-[(-moz-appearance:none)]:skew-y-0
|
||||
fill-black/40
|
||||
stroke-black/50
|
||||
mix-blend-overlay
|
||||
dark:fill-white/2.5 dark:stroke-white/5"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute inset-x-0 top-0 h-[25rem] dark:[mask-image:linear-gradient(white,transparent)] pointer-events-none z-10">
|
||||
|
||||
@@ -188,7 +188,7 @@ export function Layout({ children, title, tableOfContents }) {
|
||||
<AnnouncementBanner />
|
||||
<HeroPattern/>
|
||||
<div
|
||||
className="relative mx-auto flex max-w-8xl sm:px-2 lg:px-8 xl:px-12 lg:ml-72 xl:ml-80"
|
||||
className="relative mx-auto flex max-w-8xl md:px-2 lg:px-8 xl:px-12 lg:ml-72 xl:ml-80"
|
||||
style={{ paddingTop: bannerHeight }}
|
||||
>
|
||||
<header
|
||||
@@ -206,9 +206,9 @@ export function Layout({ children, title, tableOfContents }) {
|
||||
</div>
|
||||
<Header />
|
||||
</header>
|
||||
<div className="min-w-0 max-w-2xl flex-auto px-4 py-16 lg:max-w-none lg:pl-8 lg:pr-0 xl:px-5">
|
||||
<main className="py-16">
|
||||
<Prose as="article">{children}</Prose>
|
||||
<div className="min-w-0 flex-auto px-4 py-16 md:max-w-2xl lg:max-w-none lg:pl-8 lg:pr-0 xl:px-5">
|
||||
<main className="py-8 lg:py-16">
|
||||
<Prose as="article" className="max-md:max-w-none">{children}</Prose>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Header } from '@/components/Header'
|
||||
import { NavigationAPI } from '@/components/NavigationAPI'
|
||||
import {NavigationDocs} from "@/components/NavigationDocs";
|
||||
import {useRouter} from "next/router";
|
||||
import { useAnnouncements } from '@/components/announcement-banner/AnnouncementBannerProvider'
|
||||
|
||||
function MenuIcon(props) {
|
||||
return (
|
||||
@@ -52,6 +53,7 @@ export const useMobileNavigationStore = create((set) => ({
|
||||
export function MobileNavigation() {
|
||||
let isInsideMobileNavigation = useIsInsideMobileNavigation()
|
||||
let { isOpen, toggle, close } = useMobileNavigationStore()
|
||||
let { bannerHeight } = useAnnouncements()
|
||||
let ToggleIcon = isOpen ? XIcon : MenuIcon
|
||||
|
||||
let router = useRouter()
|
||||
@@ -78,7 +80,10 @@ export function MobileNavigation() {
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-sm dark:bg-[#181A1D]/40" />
|
||||
<div
|
||||
className="fixed inset-0 bg-zinc-400/20 backdrop-blur-sm dark:bg-[#181A1D]/40"
|
||||
style={{ top: `calc(${bannerHeight}px + 4rem)` }}
|
||||
/>
|
||||
</Transition.Child>
|
||||
|
||||
<Dialog.Panel>
|
||||
@@ -105,7 +110,8 @@ export function MobileNavigation() {
|
||||
>
|
||||
<motion.div
|
||||
layoutScroll
|
||||
className="fixed bottom-0 left-0 top-14 w-full overflow-y-auto bg-white/70 dark:bg-[#181A1D]/95 backdrop-blur-lg px-4 pb-4 pt-6 shadow-lg shadow-zinc-900/10 ring-1 ring-zinc-900/7.5 dark:ring-neutral-500/10 min-[416px]:max-w-sm sm:px-6 sm:pb-10"
|
||||
className="fixed bottom-0 left-0 w-full overflow-y-auto bg-white/70 dark:bg-[#181A1D]/95 backdrop-blur-lg px-4 pb-4 pt-6 shadow-lg shadow-zinc-900/10 ring-1 ring-zinc-900/7.5 dark:ring-neutral-500/10 min-[416px]:max-w-sm sm:px-6 sm:pb-10"
|
||||
style={{ top: `calc(${bannerHeight}px + 4rem)` }}
|
||||
>
|
||||
{router.route.startsWith("/ipa") ? <NavigationAPI tableOfContents={[]} /> :
|
||||
<NavigationDocs />}
|
||||
|
||||
@@ -55,7 +55,6 @@ export const apiNavigation = [
|
||||
{
|
||||
title: 'Cloud Resources',
|
||||
links: [
|
||||
{ title: 'Ingress Ports', href: '/api/resources/ingress-ports' },
|
||||
{
|
||||
title: 'IDP (Azure API)',
|
||||
href: '/api/resources/idp-azure-integrations',
|
||||
|
||||
@@ -69,6 +69,7 @@ export const docsNavigation = [
|
||||
{ title: 'Proxmox VE', href: '/get-started/install/proxmox-ve' },
|
||||
{ title: 'Synology', href: '/get-started/install/synology' },
|
||||
{ title: 'TrueNAS', href: '/get-started/install/truenas' },
|
||||
{ title: 'Unraid', href: '/get-started/install/unraid' },
|
||||
{ title: 'pfSense', href: '/get-started/install/pfsense' },
|
||||
{ title: 'OPNsense', href: '/get-started/install/opnsense' },
|
||||
{ title: 'OpenWrt', href: '/get-started/install/openwrt' },
|
||||
@@ -110,6 +111,22 @@ export const docsNavigation = [
|
||||
title: 'MDM Deployment',
|
||||
isOpen: false,
|
||||
links: [
|
||||
{
|
||||
title: 'MDM Integration',
|
||||
href: '/client/mdm-integration',
|
||||
},
|
||||
{
|
||||
title: 'Enforce Settings on Windows',
|
||||
href: '/manage/peers/mdm-deployment/windows-mdm-policy',
|
||||
},
|
||||
{
|
||||
title: 'Enforce Settings on macOS',
|
||||
href: '/manage/peers/mdm-deployment/macos-mdm-policy',
|
||||
},
|
||||
{
|
||||
title: 'Enforce Settings on iOS',
|
||||
href: '/manage/peers/mdm-deployment/ios-mdm-policy',
|
||||
},
|
||||
{
|
||||
title: 'Deploy with Group Policy (GPO)',
|
||||
href: '/manage/peers/mdm-deployment/windows-gpo-deployment',
|
||||
@@ -172,6 +189,10 @@ export const docsNavigation = [
|
||||
title: 'Disable route when in the office',
|
||||
href: '/manage/access-control/posture-checks/connecting-from-the-office',
|
||||
},
|
||||
{
|
||||
title: 'Requiring a device certificate',
|
||||
href: '/manage/access-control/posture-checks/device-certificates',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -262,6 +283,10 @@ export const docsNavigation = [
|
||||
title: 'Bring Your Own Proxy',
|
||||
href: '/manage/reverse-proxy/bring-your-own-proxy',
|
||||
},
|
||||
{
|
||||
title: 'OpenShift',
|
||||
href: '/manage/reverse-proxy/openshift',
|
||||
},
|
||||
{
|
||||
title: 'Custom Domains',
|
||||
href: '/manage/reverse-proxy/custom-domains',
|
||||
@@ -369,6 +394,10 @@ export const docsNavigation = [
|
||||
// { title: 'JumpCloud', href: '/manage/team/single-sign-on/jumpcloud' },
|
||||
// ]
|
||||
},
|
||||
{
|
||||
title: 'Sign-in Domains',
|
||||
href: '/manage/team/sign-in-domains',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -517,6 +546,7 @@ export const docsNavigation = [
|
||||
{ title: 'Quickstart', href: '/agent-network/quickstart' },
|
||||
{ title: 'Providers', href: '/agent-network/providers' },
|
||||
{ title: 'Clusters', href: '/agent-network/clusters' },
|
||||
{ title: 'Control Center', href: '/agent-network/control-center' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -598,6 +628,10 @@ export const docsNavigation = [
|
||||
title: 'High Availability',
|
||||
href: '/selfhosted/maintenance/scaling/high-availability',
|
||||
},
|
||||
{
|
||||
title: 'External Relays (Licensed)',
|
||||
href: '/selfhosted/enterprise/external-relays',
|
||||
},
|
||||
{
|
||||
title: 'Grafana Dashboard',
|
||||
href: '/selfhosted/enterprise/grafana-dashboard',
|
||||
@@ -867,6 +901,10 @@ export const docsNavigation = [
|
||||
title: 'External IdP to Embedded IdP',
|
||||
href: '/selfhosted/migration/external-to-embedded-idp',
|
||||
},
|
||||
{
|
||||
title: 'MySQL to PostgreSQL',
|
||||
href: '/selfhosted/migration/mysql-to-postgresql',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -875,6 +913,7 @@ export const docsNavigation = [
|
||||
title: 'CLIENT',
|
||||
links: [
|
||||
{ title: 'Desktop App', href: '/client/desktop-app' },
|
||||
{ title: 'nblink', href: '/client/nblink' },
|
||||
{ title: 'Profiles', href: '/client/profiles' },
|
||||
{ title: 'Profile Ownership', href: '/client/profile-ownership' },
|
||||
{ title: 'gRPC Daemon Socket', href: '/client/grpc-socket' },
|
||||
@@ -1026,6 +1065,16 @@ export const docsNavigation = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Deployment',
|
||||
isOpen: false,
|
||||
links: [
|
||||
{
|
||||
title: 'Roll Out with MDM',
|
||||
href: '/use-cases/deployment/mdm-rollout',
|
||||
},
|
||||
],
|
||||
},
|
||||
{ title: 'Homelab', href: '/use-cases/homelab' },
|
||||
],
|
||||
},
|
||||
@@ -1094,6 +1143,10 @@ export const docsNavigation = [
|
||||
title: 'Pending approval',
|
||||
href: '/help/troubleshooting-account-access',
|
||||
},
|
||||
{
|
||||
title: 'User in another account',
|
||||
href: '/help/troubleshooting-user-in-another-account',
|
||||
},
|
||||
{ title: 'IdP & SSO setup', href: '/manage/team/single-sign-on' },
|
||||
{ title: 'User provisioning', href: '/manage/team/idp-sync' },
|
||||
{
|
||||
|
||||
@@ -1,48 +1,105 @@
|
||||
import { Fragment } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { Button } from '@/components/Button'
|
||||
|
||||
/**
|
||||
* "Still stuck?" call-to-action banner for the bottom of the Troubleshooting
|
||||
* hub. Renders a short prompt and a row of action buttons.
|
||||
* hub. Renders a centered title, a row of action buttons with an optional note
|
||||
* under each, and an optional row of secondary links.
|
||||
*
|
||||
* @param {string} [title='Still stuck?']
|
||||
* @param {string} description
|
||||
* @param {Array<{label: string, href: string, primary?: boolean}>} actions
|
||||
* @param {string} [id] - Optional id for the title anchor (listed in "On this page")
|
||||
* @param {string} [description]
|
||||
* @param {Array<{label: string, href: string, primary?: boolean, note?: string}>} actions
|
||||
* @param {string} [separator] - Optional word rendered between buttons (e.g. "or"),
|
||||
* so two equally-weighted actions don't read as a recommended-vs-secondary pair.
|
||||
* @param {string} [linksLabel] - Lead-in text, on its own line above the secondary links
|
||||
* @param {Array<{label: string, href: string}>} [links] - Secondary text links
|
||||
*/
|
||||
export function StillStuck({
|
||||
title = 'Still stuck?',
|
||||
id,
|
||||
description,
|
||||
actions = [],
|
||||
separator,
|
||||
linksLabel,
|
||||
links = [],
|
||||
}) {
|
||||
// `not-prose` on the card would also drop the prose layout (left margin and
|
||||
// max width), so the outer div keeps it and matches TroubleshootingTiles.
|
||||
return (
|
||||
<div className="not-prose my-12 rounded-2xl bg-zinc-50 p-6 ring-1 ring-inset ring-zinc-900/7.5 dark:bg-white/2.5 dark:ring-white/10">
|
||||
<h2 className="text-xl font-semibold text-zinc-900 dark:text-white">
|
||||
{title}
|
||||
</h2>
|
||||
{description && (
|
||||
<p className="mt-1 max-w-2xl text-sm text-zinc-600 dark:text-zinc-400">
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
<div className="mt-5 flex flex-wrap items-center gap-3">
|
||||
{actions.map((action, i) => (
|
||||
<Fragment key={action.href}>
|
||||
{i > 0 && separator && (
|
||||
<span className="text-sm text-zinc-500 dark:text-zinc-400">
|
||||
{separator}
|
||||
</span>
|
||||
)}
|
||||
<Button
|
||||
href={action.href}
|
||||
variant={action.primary ? 'primary' : 'secondary'}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
</Fragment>
|
||||
))}
|
||||
<div className="my-12 xl:max-w-none">
|
||||
<div className="not-prose rounded-2xl bg-zinc-50 px-4 py-6 text-center ring-1 ring-inset ring-zinc-900/7.5 dark:bg-white/2.5 dark:ring-white/10">
|
||||
<h2
|
||||
id={id}
|
||||
className="scroll-mt-24 text-xl font-semibold text-zinc-900 dark:text-white"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
{description && (
|
||||
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
{/* Stacked in reading order on phones. From `sm` up, a two-row grid puts
|
||||
each note under its button and keeps the separator level with the
|
||||
buttons, not the notes. */}
|
||||
<div
|
||||
className="mt-5 flex flex-col items-center gap-2 sm:grid sm:items-start sm:justify-center sm:gap-x-4 sm:[grid-template-columns:var(--cols)]"
|
||||
style={{
|
||||
'--cols': actions.map(() => 'minmax(0, 16rem)').join(' auto '),
|
||||
}}
|
||||
>
|
||||
{actions.map((action, i) => (
|
||||
<Fragment key={action.href}>
|
||||
{i > 0 && separator && (
|
||||
<span
|
||||
className="text-sm text-zinc-500 sm:row-start-1 sm:self-center sm:[grid-column:var(--col)] dark:text-zinc-400"
|
||||
style={{ '--col': 2 * i }}
|
||||
>
|
||||
{separator}
|
||||
</span>
|
||||
)}
|
||||
<div
|
||||
className="sm:row-start-1 sm:[grid-column:var(--col)]"
|
||||
style={{ '--col': 2 * i + 1 }}
|
||||
>
|
||||
<Button
|
||||
href={action.href}
|
||||
variant={action.primary ? 'primary' : 'secondary'}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
</div>
|
||||
{action.note && (
|
||||
<p
|
||||
className="text-xs text-zinc-500 sm:row-start-2 sm:[grid-column:var(--col)] dark:text-zinc-400"
|
||||
style={{ '--col': 2 * i + 1 }}
|
||||
>
|
||||
{action.note}
|
||||
</p>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
{links.length > 0 && (
|
||||
<div className="mt-5 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
{linksLabel && <p>{linksLabel}</p>}
|
||||
<p className={linksLabel ? 'mt-1' : undefined}>
|
||||
{links.map((link, i) => (
|
||||
<Fragment key={link.href}>
|
||||
{i > 0 && <span aria-hidden="true"> · </span>}
|
||||
<Link
|
||||
href={link.href}
|
||||
className="font-medium text-zinc-900 underline decoration-[#F28C28]/40 underline-offset-2 hover:decoration-[#F28C28] dark:text-white"
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
</Fragment>
|
||||
))}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import clsx from 'clsx'
|
||||
import { useLayoutEffect, useRef } from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { useAnnouncements } from '@/components/announcement-banner/AnnouncementBannerProvider'
|
||||
@@ -36,19 +36,34 @@ function CloseIcon(props) {
|
||||
|
||||
function AnnouncementItem({ announcement, onClose }) {
|
||||
const announcementLink = useCustomQueryURL(announcement.link || '')
|
||||
const bannerRef = useRef(null)
|
||||
const { setBannerHeight } = useAnnouncements()
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const banner = bannerRef.current
|
||||
// Text wrapping and viewport changes can make the banner taller on phones.
|
||||
const updateHeight = () => setBannerHeight(banner.getBoundingClientRect().height)
|
||||
updateHeight()
|
||||
const observer = new ResizeObserver(updateHeight)
|
||||
observer.observe(banner)
|
||||
|
||||
return () => {
|
||||
observer.disconnect()
|
||||
setBannerHeight(0)
|
||||
}
|
||||
}, [setBannerHeight])
|
||||
|
||||
return (
|
||||
<div
|
||||
id="announcement-banner"
|
||||
className={clsx(
|
||||
'sticky top-0 z-50 flex w-full items-center justify-center border-b border-zinc-800 bg-netbird/95 px-4 py-1.5 text-[11px] font-medium text-black shadow-sm backdrop-blur'
|
||||
)}
|
||||
ref={bannerRef}
|
||||
className="fixed inset-x-0 top-0 z-50 flex w-full items-center justify-center border-b border-zinc-800 bg-netbird/95 px-4 py-1.5 text-[11px] font-medium text-black shadow-sm backdrop-blur"
|
||||
>
|
||||
<div className="flex flex-col items-start gap-1 pr-8 leading-snug md:flex-row md:items-center">
|
||||
<div className="pr-8 leading-relaxed md:flex md:items-center md:gap-1 md:leading-snug">
|
||||
{announcement.tag ? (
|
||||
<div className="mr-2 inline rounded-md bg-black/70 px-2 py-1 text-[9px] font-semibold uppercase tracking-[0.18em] text-white">
|
||||
<span className="mr-2 inline-block whitespace-nowrap rounded-md bg-black/70 px-2 py-1 align-middle text-[9px] font-semibold uppercase tracking-[0.18em] text-white">
|
||||
{announcement.tag}
|
||||
</div>
|
||||
</span>
|
||||
) : null}
|
||||
<span className="mr-2 text-[12px] md:text-[13px]">
|
||||
{announcement.text}
|
||||
@@ -101,4 +116,4 @@ export function AnnouncementBanner() {
|
||||
onClose={closeAnnouncement}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ const ANNOUNCEMENTS_URL =
|
||||
'https://raw.githubusercontent.com/netbirdio/dashboard/main/announcements.json'
|
||||
const STORAGE_KEY = 'netbird-announcements'
|
||||
const CACHE_DURATION_MS = 30 * 60 * 1000
|
||||
const BANNER_HEIGHT = 33
|
||||
|
||||
const AnnouncementContext = createContext({
|
||||
bannerHeight: 0,
|
||||
setBannerHeight: () => {},
|
||||
announcements: undefined,
|
||||
closeAnnouncement: () => {},
|
||||
})
|
||||
@@ -83,6 +83,7 @@ const saveAnnouncements = (closedAnnouncements) => {
|
||||
|
||||
export function AnnouncementBannerProvider({ children }) {
|
||||
const [announcements, setAnnouncements] = useState(undefined)
|
||||
const [bannerHeight, setBannerHeight] = useState(0)
|
||||
const fetchingRef = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -108,12 +109,11 @@ export function AnnouncementBannerProvider({ children }) {
|
||||
[announcements]
|
||||
)
|
||||
|
||||
const bannerHeight = announcements?.some((a) => a.isOpen) ? BANNER_HEIGHT : 0
|
||||
|
||||
return (
|
||||
<AnnouncementContext.Provider
|
||||
value={{
|
||||
bannerHeight,
|
||||
setBannerHeight,
|
||||
announcements,
|
||||
closeAnnouncement,
|
||||
}}
|
||||
@@ -125,4 +125,4 @@ export function AnnouncementBannerProvider({ children }) {
|
||||
|
||||
export function useAnnouncements() {
|
||||
return useContext(AnnouncementContext)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,19 @@ export { Badge } from '@/components/Badge'
|
||||
export { YouTube }
|
||||
export { Video } from '@/components/Video'
|
||||
|
||||
export const table = function Table(props) {
|
||||
return (
|
||||
<div
|
||||
className="my-8 overflow-x-auto [&>table]:my-0"
|
||||
role="region"
|
||||
aria-label="Scrollable table"
|
||||
tabIndex={0}
|
||||
>
|
||||
<table {...props} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const h2 = function H2(props) {
|
||||
return <Heading level={2} {...props} />
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ After installation, NetBird client requires login that can be done through Ident
|
||||
* **Keeping the network map.** The Management service stores information about all the registered peers including WireGuard public key that was sent during the registration process.
|
||||
* **Managing private IP addresses.** Each peer receives a unique private IP with which it can be identified in the network.
|
||||
We use [Carrier Grade NAT](https://en.wikipedia.org/wiki/Carrier-grade_NAT) address space with an allocated address block <em>100.64.0.0/10</em>.
|
||||
Each account gets its own range inside this block, a randomly chosen /16 by default, and each new peer receives a randomly chosen free address from it.
|
||||
When a peer is deleted, its address is released immediately and can be assigned to a later peer; there is no holding period.
|
||||
* **Synchronizing network changes to peers.** The Management Service keeps a control channel open to each peer sending network updates.
|
||||
Whenever a new peer joins the network, all other peers that are authorized to connect to it receive an update.
|
||||
After that, they are able to establish a connection to the new peer.
|
||||
@@ -74,7 +76,7 @@ The client application requests a user to log in with an Identity Provider (IDP)
|
||||
* **Receiving network updates from the Management service.**
|
||||
Each peer receives initial configuration and a list of peers with corresponding public keys and IP addresses so that it can establish a point-to-point connection.
|
||||
* **Establishing point-to-point WireGuard connection.** To establish a connection with a remote peer, the Client first discovers the most suitable connection candidate, or simply address (IP:port) that other peers can use to connect to it.
|
||||
It then sends it to the remote peer via Signal. This message is encrypted with the peer's private key and a public key of the remote peer.
|
||||
It then sends it to the remote peer via Signal. The message body is encrypted end to end with a shared key that each peer derives from its own private key and the other peer's public key (NaCl `box`: Curve25519, XSalsa20, Poly1305). Signal sees the two peers' public keys, so it knows who is talking to whom, but never the contents. The same shared key also authenticates the message, so there is no separate signature step.
|
||||
The remote peer does the same and once the peers can reach each other, they establish an encrypted WireGuard tunnel.
|
||||
* **Applying access control policies.** The client application applies access control rules that are received from the Management service.
|
||||
The client uses a system's available firewall manager like `nftables` to apply the rules.
|
||||
|
||||
@@ -18,6 +18,10 @@ This section covers **network/perimeter firewall** requirements (e.g., Fortigate
|
||||
|
||||
The NetBird client doesn't require any inbound port to be open; it negotiates the connection with the support of the signal and relay services.
|
||||
|
||||
<Note>
|
||||
NetBird peers use WireGuard over UDP (default port `51820`), but you don't need to open that port inbound. Peers connect outbound and use hole punching. If a direct connection isn't possible, they fall back to the relay over UDP/443 (QUIC) or TCP/443 (WebSocket). An outbound rule for UDP port 51820 won't reliably help either: the port a peer sends to is whatever the NAT in front of the other peer assigned, and that is often not 51820. If your policy requires fixed ports, see [Static peer ports for site-to-site firewall rules](#static-peer-ports-for-site-to-site-firewall-rules).
|
||||
</Note>
|
||||
|
||||
### Outbound ports
|
||||
|
||||
NetBird usually won't need open ports, but sometimes you or your IT team needs to secure and verify all outbound traffic, and that may affect how NetBird clients connect to the [control plane](/about-netbird/how-netbird-works) and negotiate the peer-to-peer connections.
|
||||
|
||||
@@ -50,14 +50,14 @@ some additional features that are targeted at business customers and help with n
|
||||
|
||||
- **[Users and groups provisioning](/manage/team/idp-sync)** from your identity provider (IdP).
|
||||
- **[Traffic events logging](/manage/activity/traffic-events-logging)** of connections to internal resources for audit and analysis.
|
||||
- **[Event streaming](/manage/activity/event-streaming)** to 3rd party platforms and SIEM systems (also available in self-hosted deployments with an [enterprise license](/selfhosted/enterprise)).
|
||||
- **[Event streaming](/manage/activity/event-streaming)** to 3rd party platforms and SIEM systems.
|
||||
- **[Integrations with EDR](/manage/access-control/endpoint-detection-and-response)** like CrowdStrike and others.
|
||||
- **[Peer approval](/manage/peers/approve-peers)** to join the network.
|
||||
- **[User invites](/manage/team/add-users-to-your-network#direct-user-invites)**.
|
||||
- **[MSP functionality](/manage/for-partners/msp-portal)** for managing multiple tenant networks from a single account.
|
||||
|
||||
<Note>
|
||||
SCIM provisioning and some enterprise features require a [Commercial License](https://netbird.io/pricing#on-prem) for self-hosted deployments.
|
||||
Users and groups provisioning (including SCIM), traffic events logging, event streaming, EDR integrations, and peer approval are also available in self-hosted deployments with an [Enterprise Commercial License](/selfhosted/enterprise). The MSP functionality is available only in NetBird Cloud.
|
||||
</Note>
|
||||
|
||||
## Geo Distributed Relay Servers
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import {Note} from "@/components/mdx"
|
||||
|
||||
export const description =
|
||||
'See which groups can reach which AI providers in Control Center, edit providers and agent policies in place, and stage new Agent Network access with Draft Mode.'
|
||||
|
||||
# Agent Network in Control Center
|
||||
|
||||
Agent Network is also available in [Control Center](/manage/control-center) for a better view of who can reach which AI provider. Alongside your peers, groups, and access control policies, it draws Agent Network **providers** and **agent policies** on the same canvas, so you can follow a group to the AI providers it is allowed to reach, edit them in place, and build new access with [Draft Mode](/manage/control-center/draft-mode).
|
||||
|
||||
<Note>
|
||||
**Permissions:** Viewing and changing providers and agent policies in Control Center requires access to Control Center and the same Agent Network permissions as on the **Agent Network** pages. The `Agent Network Admin` role has no access to Control Center, so it manages providers and policies from the **Agent Network** pages only. Learn more about [user roles](/manage/team/user-roles).
|
||||
</Note>
|
||||
|
||||
One example carries through the page: giving the `Engineers` group access to the **Anthropic API** provider.
|
||||
|
||||
## How It Looks on the Canvas
|
||||
|
||||
Agent Network adds two node types to the graph, and they read left to right like an access control policy:
|
||||
|
||||
- **Agent policy:** a pill in the policy column, showing the policy name and a status dot (green when enabled, grey when disabled). It grants its source group access to one or more providers. See [Policies](/agent-network/policies).
|
||||
- **Provider:** a card in the destination column, showing the provider's logo, name, and upstream URL, like `https://api.anthropic.com`. See [Providers](/agent-network/providers).
|
||||
|
||||
So the path is **group → agent policy → provider**. Disabled policies and providers stay on the canvas, dimmed, so you can still see what they would grant.
|
||||
|
||||
Agent Network nodes show up in the **Peers**, **Users**, and **Groups** views:
|
||||
|
||||
- **Groups:** the providers the selected group can reach, through which agent policies.
|
||||
- **Peers:** the providers reachable through any of the selected peer's groups.
|
||||
- **Users:** the providers reachable through the groups of each of the user's peers.
|
||||
|
||||
The **Networks** view does not show Agent Network nodes, because providers are not network resources.
|
||||
|
||||
### Focus on a Policy or Provider
|
||||
|
||||
When the graph gets busy, use [Focus Mode](/manage/control-center#focus-mode) to isolate one path. Right-click an agent policy or provider and choose **Focus**, or press `F` and then select the node. Everything unrelated dims, leaving only the groups and providers connected to it, and a **Focusing on** pill at the top of the canvas shows what is in focus. Close the pill to return to the full view.
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/agent-network/control-center/agent-network-control-center-focus-policy.png"
|
||||
alt="Control Center in the Peer view, focused on an agent policy that connects to the Anthropic API provider, with unrelated nodes dimmed"
|
||||
className="imagewrapper-big"/>
|
||||
</p>
|
||||
|
||||
## Edit Providers and Agent Policies in Place
|
||||
|
||||
You do not have to leave Control Center to change Agent Network configuration. Click a node, or right-click it and choose **Edit**, to open the same editor you know from the Agent Network pages:
|
||||
|
||||
- **Provider:** the **Edit Provider** dialog, with the **Provider** tab (provider, upstream URL, API key, display name) and the **Models** tab.
|
||||
- **Agent policy:** the **Update Agent Policy** dialog, with the **Policy**, **Limits**, and **Guardrails** tabs.
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/agent-network/control-center/agent-network-control-center-edit-provider.png"
|
||||
alt="The Edit Provider dialog opened from Control Center, showing the Anthropic API provider, its upstream URL, a masked API key, and the display name"
|
||||
className="imagewrapper"/>
|
||||
</p>
|
||||
|
||||
In the live view, saving asks you to confirm, because the change applies to your account immediately. The right-click menu also offers:
|
||||
|
||||
- **Disable / Enable:** turns the provider or policy off or on right away.
|
||||
- **Delete:** removes it from your account after a confirmation.
|
||||
|
||||
## Build Access in Draft Mode
|
||||
|
||||
For a new provider, a new policy, or both at once, switch to **Draft** and stage the change before it goes live. Draft Mode works the same as for the rest of Control Center; see [Plan and Deploy Changes with Draft Mode](/manage/control-center/draft-mode) for the full workflow.
|
||||
|
||||
### Add Agent Network Nodes
|
||||
|
||||
Click **Add** in the toolbar (or press `C`) and open the **Agent Network** tab of the components panel:
|
||||
|
||||
- **Add new:** a **Provider** node ("Connect an AI provider or gateway") and an **Agent Policy** node ("Authorize groups to reach providers").
|
||||
- **Existing providers** and **Existing agent policies:** the ones already in your account, ready to drag onto the canvas. Each existing policy shows how many groups and providers it connects.
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/agent-network/control-center/agent-network-control-center-components-panel.png"
|
||||
alt="The Draft Mode components panel on the Agent Network tab, listing the Provider and Agent Policy nodes, existing providers, and existing agent policies"
|
||||
className="imagewrapper-big"/>
|
||||
</p>
|
||||
|
||||
You can also right-click the canvas and choose **New Agent Policy** or **New Agent Provider**.
|
||||
|
||||
- **Dropping a Provider node** opens the **Connect Provider** dialog. The provider card lands on the canvas once you save it, and its creation is tracked in the draft.
|
||||
- **Dropping an Agent Policy node** places a blank policy node. It becomes a tracked change once it has a source group and at least one provider.
|
||||
|
||||
For our example, drag the existing **Anthropic API** provider and the existing `Engineers` group onto the canvas. If the provider does not exist yet, drop a new **Provider** node instead and connect it in the dialog.
|
||||
|
||||
### Connect Groups to Policies and Providers
|
||||
|
||||
Draw connections between nodes to wire up access. Unlike access control policies, the drag direction does not matter here: a group is always the source and a provider is always the destination.
|
||||
|
||||
- **Group to provider:** opens the **Create Agent Policy** dialog prefilled with both, named after them (`Engineers → Anthropic API`). This is the quickest way to grant access.
|
||||
- **Group to agent policy:** sets the policy's source group.
|
||||
- **Agent policy to provider:** adds the provider to the policy. A policy can reach several providers.
|
||||
|
||||
For our example, connect `Engineers` to **Anthropic API**. Before saving the policy, use the **Limits** and **Guardrails** tabs to cap spend and restrict the policy to the models the team actually uses, so the first version you deploy is already scoped. See [Token & Budget Limits](/agent-network/policies/limits) and [Guardrails](/agent-network/policies/guardrails).
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/agent-network/control-center/agent-network-control-center-draft-connected.png"
|
||||
alt="A draft canvas where the new Engineers group connects through a new agent policy to the Anthropic API provider, with two pending changes on the Review & Deploy button"
|
||||
className="imagewrapper-big"/>
|
||||
</p>
|
||||
|
||||
### Review and Deploy
|
||||
|
||||
Click **Review & Deploy** to see every change with the request it sends, like `POST /agent-network/policies` for the new `Engineers → Anthropic API` policy. Provider API keys are masked in the preview.
|
||||
|
||||
On deploy, groups are created first, then providers, then agent policies, so a new policy can point at a group and provider created moments earlier in the same batch.
|
||||
|
||||
**Approve & Deploy** stays disabled while a policy is unfinished. The review flags it when:
|
||||
|
||||
- an agent policy has no source group or no provider,
|
||||
- an agent policy points at a provider or group that is marked for deletion, or
|
||||
- an agent policy points at a draft provider that was removed from the draft.
|
||||
|
||||
### Remove and Delete
|
||||
|
||||
As elsewhere in Draft Mode, **Remove** takes a node you created in the draft off the canvas, and **Delete** marks an existing provider or agent policy for deletion at deploy. Removing a draft provider also drops any new draft policy that pointed at it.
|
||||
|
||||
## Recap
|
||||
|
||||
- Control Center draws Agent Network as **group → agent policy → provider**, in the Peers, Users, and Groups views.
|
||||
- Click a provider or agent policy to edit it in place; live saves confirm first and apply immediately.
|
||||
- In Draft Mode, add providers and agent policies from the **Agent Network** tab, then connect `Engineers` to **Anthropic API** to create a scoped policy in one step.
|
||||
- Review shows the exact API requests, and the batch deploys groups, then providers, then policies.
|
||||
|
||||
## Related Docs
|
||||
|
||||
- [Control Center overview](/manage/control-center)
|
||||
- [Plan and Deploy Changes with Draft Mode](/manage/control-center/draft-mode)
|
||||
- [Agent Network Providers](/agent-network/providers)
|
||||
- [Agent Network Policies](/agent-network/policies)
|
||||
@@ -1,21 +1,98 @@
|
||||
import { Note } from '@/components/mdx'
|
||||
|
||||
export const description =
|
||||
'Get an LLM request routed through NetBird Agent Network end to end: set up the NetBird server with the proxy, connect a provider, create a policy, and make your first keyless call.'
|
||||
'Get an LLM request routed through NetBird Agent Network end to end, on NetBird Cloud or self-hosted: set up a private proxy, connect a provider, create a policy, and make your first keyless call.'
|
||||
|
||||
# NetBird Agent Network Quickstart
|
||||
|
||||
This guide takes you from a fresh server to a working, keyless LLM call through
|
||||
Agent Network.
|
||||
This guide takes you to a working, keyless LLM call through Agent Network, either on
|
||||
NetBird Cloud or on a server of your own.
|
||||
|
||||
<Note>
|
||||
NetBird Agent Network is open source and self-hosted. This guide sets up a minimal deployment with core functionality;
|
||||
you can enable the full platform later.
|
||||
|
||||
It is currently in beta, and the setup process installs release candidate versions of the platform.
|
||||
NetBird Agent Network is currently in beta. It is open source, and you can run it on NetBird Cloud or self-host it.
|
||||
</Note>
|
||||
|
||||
## Infrastructure Requirements
|
||||
## Choose Where to Run Agent Network
|
||||
|
||||
Agent Network needs two things: a NetBird account, and a **private proxy** that serves your
|
||||
agent network endpoint. The proxy is reachable only by devices connected to your NetBird
|
||||
network, so the endpoint never faces the public internet.
|
||||
|
||||
- **NetBird Cloud** is the fastest path. There is no server to install, and NetBird can run a
|
||||
dedicated private proxy for your account. Follow [Set Up on NetBird Cloud](#set-up-on-net-bird-cloud).
|
||||
- **Self-hosted** runs the whole platform, proxy included, on a Linux VM you control. Follow
|
||||
[Set Up Self-Hosted](#set-up-self-hosted).
|
||||
|
||||
With a managed proxy, your LLM requests pass through a proxy that NetBird operates. If those
|
||||
requests must stay on your own infrastructure, choose **Deploy a new proxy** during the Cloud
|
||||
setup instead, or self-host.
|
||||
|
||||
## Before You Begin
|
||||
|
||||
Whichever path you choose, you need:
|
||||
|
||||
- **A device for your agent** that can run the NetBird client, such as your laptop. Agent
|
||||
network endpoints are reachable only over NetBird, so the agent must run on a connected
|
||||
device. See [Get Started](/get-started) to install the client.
|
||||
- **Access to an LLM provider**: the API key for a hosted provider such as Anthropic or OpenAI,
|
||||
or the URL of an AI gateway or a self-hosted model. You enter it once in the dashboard, and it
|
||||
never goes on the agent's device. See [Providers](/agent-network/providers).
|
||||
- **An agent or client to test with**, such as Claude Code, Codex, the OpenAI SDK, or cURL.
|
||||
|
||||
## Set Up on NetBird Cloud
|
||||
|
||||
### Cloud Requirements
|
||||
|
||||
- **A NetBird Cloud account**, or an email address to create one. You create it during the
|
||||
setup below.
|
||||
- **The owner or admin role**, if you add Agent Network to an existing account. The guided setup
|
||||
only opens for those roles.
|
||||
- **Your own server**, only if you choose **Deploy a new proxy** instead of the managed proxy.
|
||||
See [Clusters](/agent-network/clusters).
|
||||
|
||||
### Activate Agent Network
|
||||
|
||||
1. [Activate Agent Network on NetBird Cloud](https://app.netbird.io/peers?signup_source=netbird.ai):
|
||||
sign up, or sign in to your existing account.
|
||||
- A **new account** opens in the focused Agent Network view, which hides the rest of the
|
||||
dashboard. You can turn it off later in **Settings**; see
|
||||
[Focused Dashboard View](/agent-network#focused-dashboard-view).
|
||||
- An **existing account** keeps its dashboard and gains the **Agent Network** menu.
|
||||
2. Follow the guided setup. It walks you through the same steps this guide describes:
|
||||
1. **Connect your device**: install the NetBird client and connect it. See
|
||||
[Add Your Device to the Network](#add-your-device-to-the-network).
|
||||
2. **Set up your proxy**: choose where your agent network endpoint is served from.
|
||||
- **Managed proxy**: NetBird deploys a dedicated, private proxy for your account and
|
||||
reserves your endpoint. This usually takes under a minute.
|
||||
- **Your own proxy**, to keep LLM requests on your own infrastructure: click **Or use your
|
||||
own proxy instead**, then select a private proxy you already run or **Deploy a new
|
||||
proxy**. For a new proxy, enter a domain and run the generated
|
||||
Docker command or cloud deployment on your server; it starts the proxy in private mode.
|
||||
The setup continues once the proxy connects and your endpoint is reserved. See
|
||||
[Clusters](/agent-network/clusters).
|
||||
3. **Connect a provider**: see [Connect a Provider](#connect-a-provider).
|
||||
4. **Create a policy** (existing accounts only): see [Create a Policy](#create-a-policy).
|
||||
5. **Configure your agent**: see [Configure Your Agent](#configure-your-agent), then send a
|
||||
first request.
|
||||
|
||||
On a new account, the setup also prepares the account for Agent Network's deny-by-default
|
||||
model: it creates a **Users** group with you in it, creates a starter policy that lets
|
||||
**Users** reach your first provider, and removes the permissive **Default** access control
|
||||
policy. An existing account keeps its groups and policies, so you create the Agent Network
|
||||
policy yourself.
|
||||
|
||||
Once your first request goes through, continue at [Verify in Usage & Logs](#verify-in-usage-and-logs).
|
||||
|
||||
## Set Up Self-Hosted
|
||||
|
||||
<Note>
|
||||
This setup installs a minimal deployment with core functionality, using release candidate versions of the
|
||||
platform. You can enable the full platform later.
|
||||
</Note>
|
||||
|
||||
### Self-Hosted Requirements
|
||||
|
||||
#### Infrastructure
|
||||
|
||||
- A Linux VM with at least **1 CPU** and **2 GB** of memory.
|
||||
- The VM must be publicly accessible on **TCP ports 80 and 443**, and **UDP port 3478**.
|
||||
@@ -26,7 +103,7 @@ Agent Network.
|
||||
The public domain is not used for the Agent Network itself. It is only used to establish network connectivity
|
||||
and manage the platform.
|
||||
|
||||
## Software Requirements
|
||||
#### Software
|
||||
|
||||
- Docker with the docker-compose plugin v2 or higher ([Docker installation guide](https://docs.docker.com/engine/install/))
|
||||
- [jq](https://jqlang.github.io/jq/): install with `sudo apt install jq` or `sudo yum install jq`
|
||||
@@ -40,8 +117,11 @@ Download and run the installation script:
|
||||
curl -fsSL https://pkgs.netbird.io/getting-started.sh | NETBIRD_AGENT_NETWORK=true bash
|
||||
```
|
||||
|
||||
The script deploys the NetBird server together with a private proxy, so your deployment
|
||||
already has the proxy Agent Network needs.
|
||||
|
||||
Once the script finishes, open `https://netbird.example.com`, create your admin
|
||||
account on the setup page, create an admin user, and log in.
|
||||
account on the setup page, create an admin user, and log in. Then continue with the steps below.
|
||||
|
||||
## Add Your Device to the Network
|
||||
|
||||
@@ -59,8 +139,9 @@ and protected by a peer-to-peer encrypted WireGuard tunnel.
|
||||
|
||||
You should now see your device in the peers list in the NetBird dashboard.
|
||||
|
||||
To access a provider via NetBird, you need to use an **agent network endpoint**, which is generated when you connect your
|
||||
first provider.
|
||||
To access a provider via NetBird, you need to use an **agent network endpoint**. On a
|
||||
self-hosted deployment it is generated when you connect your first provider. On NetBird
|
||||
Cloud it is reserved when you set up your proxy.
|
||||
|
||||
## Connect a Provider
|
||||
|
||||
@@ -83,7 +164,14 @@ See [Providers](/agent-network/providers) for more details.
|
||||
<img src="/docs-static/img/agent-network/quickstart/agent-network-add-provider.png" alt="agent network add provider" className="imagewrapper" />
|
||||
</p>
|
||||
|
||||
You should now see a newly-generated endpoint above the **Providers** table.
|
||||
<Note>
|
||||
If **Connect Provider** says you need a reverse proxy cluster with private capabilities, your account has no
|
||||
private proxy yet. The shared NetBird Cloud proxy cluster can't serve an agent network endpoint. Set up a proxy
|
||||
first, either through the guided setup or under **Agent Network > Configuration > Clusters**. See
|
||||
[Clusters](/agent-network/clusters).
|
||||
</Note>
|
||||
|
||||
You should now see your agent network endpoint above the **Providers** table.
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/agent-network/quickstart/agent-network-endpoint.png" alt="agent network endpoint" className="imagewrapper" />
|
||||
@@ -112,6 +200,9 @@ policy explicitly allows it. A policy connects a **Source Group** (your users or
|
||||
agent devices) to one or more **Providers**, and is where you attach optional token
|
||||
and budget limits and guardrails.
|
||||
|
||||
If you set up a new NetBird Cloud account through the guided setup, it already created a
|
||||
starter policy for the **Users** group. Skip to the test request below.
|
||||
|
||||
1. Go to **Team > Users** and add your user to a group, such as **Engineering**.
|
||||
2. Go to **Agent Network > Policies** and click **Add Policy**.
|
||||
3. Select the **Source Group** you want to authorize.
|
||||
@@ -141,4 +232,4 @@ recorded with the caller identity, model, tokens, and cost.
|
||||
<p>
|
||||
<img src="/docs-static/img/agent-network/quickstart/agent-network-usage-and-logs.png" alt="agent network usage & logs"
|
||||
className="imagewrapper-big" />
|
||||
</p>
|
||||
</p>
|
||||
|
||||
@@ -11,6 +11,14 @@ and the reason a request was allowed or denied.
|
||||
<img src="/docs-static/img/agent-network/usage-and-logs/agent-network-access-logs.png" alt="agent network access logs table" className="imagewrapper-big" />
|
||||
</p>
|
||||
|
||||
## Who can see what
|
||||
|
||||
`Owner`, `Admin`, `Auditor`, `Agent Network Admin`, and `Usage Viewer` see every caller's requests. Every other user sees only their own requests.
|
||||
|
||||
<Warning>
|
||||
When [prompt collection](/agent-network/usage-and-logs/log-collection) is enabled, access log entries include captured prompts and completions. Any user with one of the roles above can read other users' prompts. See [User Roles](/manage/team/user-roles#agent-network-permissions).
|
||||
</Warning>
|
||||
|
||||
## Columns
|
||||
|
||||
- **Time**
|
||||
|
||||
@@ -20,4 +20,8 @@ identity, cost attribution, and token usage.
|
||||
Token and cost **usage** is always collected, so dashboards and limits stay
|
||||
accurate even if access-log collection is turned off. The full **access log**
|
||||
(request detail and optional prompts) is retained according to your log
|
||||
collection settings.
|
||||
collection settings.
|
||||
|
||||
## Who Can See What
|
||||
|
||||
`Owner`, `Admin`, `Auditor`, `Agent Network Admin`, and `Usage Viewer` see usage and access logs for every caller. Every other user sees only their own. When prompt collection is enabled, access logs include captured prompts, so these roles can read other users' prompts. See [User Roles](/manage/team/user-roles#agent-network-permissions).
|
||||
|
||||
@@ -22,6 +22,10 @@ Filter the view by:
|
||||
|
||||
Filters apply to both the chart and the table.
|
||||
|
||||
## Who can see what
|
||||
|
||||
`Owner`, `Admin`, `Auditor`, `Agent Network Admin`, and `Usage Viewer` see usage for the whole account. Every other user sees only their own usage. See [User Roles](/manage/team/user-roles#agent-network-permissions).
|
||||
|
||||
## Tokens vs. Cost
|
||||
|
||||
Switch between input/output token totals and estimated USD spend. Cost is
|
||||
|
||||
@@ -116,9 +116,9 @@ The renewal flow follows one rule: re-authenticating never closes an active conn
|
||||
|
||||
## Localization
|
||||
|
||||
You can switch languages from the **Display Language** picker in **Settings → General**, and the change applies instantly. NetBird v0.75 ships with English, French, German, Hungarian, Italian, Japanese, Portuguese, Russian, Simplified Chinese, and Spanish.
|
||||
You can switch languages from the **Display Language** picker in **Settings → General**, and the change applies instantly. The desktop app is available in English, French, Galician, German, Hebrew, Hungarian, Italian, Japanese, Portuguese, Russian, Simplified Chinese, Spanish, and Ukrainian.
|
||||
|
||||
Translations are a continuing community effort. If your language is missing or a phrase reads incorrectly, please [open a pull request](https://github.com/netbirdio/netbird).
|
||||
Translations are a community effort on [Crowdin](https://crowdin.com/project/netbird). If your language is missing or a phrase reads incorrectly, join the project and translate there. Crowdin syncs finished translations into the NetBird repository, so please don't open pull requests that edit the translation files directly.
|
||||
|
||||
## Capturing a Debug Bundle
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@ sudo ip -6 rule del not from all fwmark 0x1bd00 lookup netbird priority 110
|
||||
| Variable | Platform | Description |
|
||||
|---|---|---|
|
||||
| `NB_DNS_FORWARDER_PORT` | All | Override the port the internal DNS forwarder binds to for handling DNS routes received from clients (default: `22054`). The forwarder is separate from the local resolver. |
|
||||
| `NB_SKIP_DNS_PROBE` | All | Skip the startup check that sends a test query to the local resolver to verify it is reachable. Set this if the probe causes delays (e.g. in air-gapped networks). |
|
||||
| `NB_DNS_LAZY_WARMUP_TIMEOUT` | All | Per-query budget for waking an idle peer when the local resolver returns one of its A or AAAA records (Go duration, default: `2s`). The value must be positive; invalid, zero, or negative values use the default. |
|
||||
| `NB_USE_LEGACY_DNS_RESOLUTION` | Windows | Available from client v0.78.0. Go back to only setting the NetBird address as the tunnel adapter's nameserver, leaving Windows free to query the other adapters' resolvers in parallel. By default, a peer with a primary nameserver also gets a Name Resolution Policy Table rule that sends every query to the NetBird resolver and nowhere else. Set this if a zone that only the local network resolves has to stay reachable while connected and cannot be declared as a match domain — it also brings back the query leak and the non-deterministic resolution the rule prevents. |
|
||||
| `NB_UNCLEAN_SHUTDOWN_RESOLV_FILE` | Linux, FreeBSD | Custom file path to store a backup of `/etc/resolv.conf` before the client modifies it. Used to restore the original on next startup if the client crashed without cleaning up (default: `<state-dir>/resolv.conf`, i.e. `/var/lib/netbird/resolv.conf` on Linux, `/var/db/netbird/resolv.conf` on FreeBSD). |
|
||||
|
||||
@@ -1,412 +1,341 @@
|
||||
import { Note, Warning } from "@/components/mdx";
|
||||
|
||||
export const description =
|
||||
"Enforce NetBird client configuration through your MDM channel (Intune, Jamf, Kandji, Mosyle, Workspace ONE, JumpCloud, Group Policy). Push management URL, kill switches, and other settings to a fleet of Windows and macOS devices.";
|
||||
"Enforce NetBird client settings on a fleet of Windows, macOS, and iOS devices through your MDM (Intune, Jamf, Kandji, Mosyle, Workspace ONE, JumpCloud, Group Policy): how a policy is applied and locked, and what every policy key does, grouped by the job it solves.";
|
||||
|
||||
# MDM Integration
|
||||
|
||||
NetBird's client honors policies pushed by your Mobile Device Management
|
||||
(MDM) channel, so an administrator can enforce configuration across a
|
||||
fleet of devices instead of touching each machine. On every supported
|
||||
platform the daemon reads from the **OS-native managed-configuration
|
||||
store** that your MDM already writes to. No extra NetBird software sits between
|
||||
you and the MDM provider; whatever you can push to that store (manually,
|
||||
via Group Policy, via a Configuration Profile, via your MDM console)
|
||||
becomes effective NetBird policy.
|
||||
Installing NetBird on a few hundred laptops is the easy half of a rollout. The hard half is everything that happens afterwards: a user points the client at a different server, creates a second profile, turns on an SSH server, or switches off a setting your security posture depends on. Fixing that one machine at a time does not scale, and asking users nicely is not a control.
|
||||
|
||||
This page covers Windows and macOS. iOS and Android support is on the
|
||||
roadmap.
|
||||
The NetBird client solves this by honoring **MDM policy**: settings your device management tool writes to the operating system's own managed-configuration store. The client reads that store, applies what it finds on top of every other setting, and locks those settings so nobody on the device can change them. NetBird adds no agent or connector of its own. If your MDM can write to the store, it can manage NetBird.
|
||||
|
||||
## At a glance
|
||||
This page is in four parts:
|
||||
|
||||
| Platform | Where NetBird reads policy | How an admin writes it |
|
||||
1. [What you can achieve](#what-you-can-achieve): the goals you can reach with a policy, and the setting for each.
|
||||
2. [Recommended policies](#recommended-policies): a starting policy for user devices, shared devices, and routing peers.
|
||||
3. [How the client applies a policy](#how-the-client-applies-a-policy): the mental model, and what a user sees on a managed device.
|
||||
4. [Policy keys reference](#policy-keys-reference), then [verifying enforcement](#verifying-enforcement) and [troubleshooting](#troubleshooting).
|
||||
|
||||
How to deliver a policy is specific to each operating system, so it has its own page:
|
||||
|
||||
| Platform | Where NetBird reads policy | How to deliver it |
|
||||
| --- | --- | --- |
|
||||
| Windows | `HKLM\Software\Policies\NetBird` (registry) | Group Policy (ADMX) · Intune ADMX ingestion or OMA-URI · `reg import` · MDM-vendor scripts |
|
||||
| macOS | `/Library/Managed Preferences/io.netbird.client.plist` | Configuration Profile (`.mobileconfig`) pushed by your MDM, targeting bundle id `io.netbird.client` |
|
||||
| Windows | Registry, `HKLM\Software\Policies\NetBird` | [Enforce settings on Windows](/manage/peers/mdm-deployment/windows-mdm-policy): Intune, Group Policy, `.reg` import, JumpCloud |
|
||||
| macOS | `/Library/Managed Preferences/io.netbird.client.plist` | [Enforce settings on macOS](/manage/peers/mdm-deployment/macos-mdm-policy): a configuration profile for bundle id `io.netbird.client` |
|
||||
| iOS and tvOS | Managed App Configuration for bundle id `io.netbird.app` (tvOS: `io.netbird.app.tv`) | [Enforce settings on iOS](/manage/peers/mdm-deployment/ios-mdm-policy): an app configuration sent with the managed app |
|
||||
|
||||
Both backends are the de-facto convention for desktop apps (the same
|
||||
shape Chrome, Edge, Firefox, Zoom, Tailscale, Citrix Workspace, and
|
||||
others use). Any MDM that supports the platform also supports NetBird —
|
||||
there is no NetBird-specific integration to build.
|
||||
Linux and Android have no MDM channel today. See [When not to use MDM policy](#when-not-to-use-mdm-policy).
|
||||
|
||||
## How enforcement works
|
||||
To plan a whole rollout, from installing the client to users signing in and a policy in place, follow [Roll Out NetBird Across Your Organization with MDM](/use-cases/deployment/mdm-rollout), which walks through it with Intune.
|
||||
|
||||
When NetBird starts, and every minute while it runs, the daemon:
|
||||
## What you can achieve
|
||||
|
||||
1. Reads the platform-native managed-configuration store.
|
||||
2. Merges the values **on top of every other configuration layer**
|
||||
(defaults → on-disk profile → environment variables → CLI/UI input →
|
||||
**MDM**). MDM always wins.
|
||||
3. Locks any field that came from the MDM source. Attempts to change
|
||||
that field from the GUI, the CLI (`netbird up --flag=...`, `netbird
|
||||
login --flag=...`) or via direct gRPC are rejected with a clear
|
||||
error listing the locked fields. The client UI greys these fields
|
||||
out and tags them with **(MDM)** so the user knows they cannot
|
||||
change them.
|
||||
4. If the MDM payload changes (admin pushes new values), the change
|
||||
takes effect within ~1 minute on the device — no client restart
|
||||
needed.
|
||||
Find what you want to do, set the key, and deliver it with your MDM (see the platform pages above). Each key links to its full entry in the [Policy keys reference](#policy-keys-reference).
|
||||
|
||||
Include only the keys you mean to pin. A key you set is locked, even when you set it to `false`; a key you leave out stays the user's to change.
|
||||
|
||||
### Set the server and startup
|
||||
|
||||
| You want to | Set | What to know |
|
||||
| --- | --- | --- |
|
||||
| Make every device use your management server | [`managementURL`](#managementURL) to your server's URL | Pins the server, not the account: users can still sign out and in to another account on that server. |
|
||||
| Make devices connect automatically at boot | [`disableAutoConnect`](#disableAutoConnect) to `false` | Users can still disconnect by hand. |
|
||||
| Make users connect by hand | [`disableAutoConnect`](#disableAutoConnect) to `true` | The client starts disconnected; the user clicks **Connect** or runs `netbird up`. |
|
||||
| Stop the NetBird app opening at login | [`disableAutostart`](#disableAutostart) to `true` | The background service still runs. A user can switch launch-at-login back on, but the app turns it off again the next time it starts. |
|
||||
|
||||
### Lock the client app
|
||||
|
||||
| You want to | Set | What users can still do |
|
||||
| --- | --- | --- |
|
||||
| Stop users changing NetBird's settings | [`disableUpdateSettings`](#disableUpdateSettings) to `true` | Connect, disconnect, and sign in. Every settings change, in the app or on the command line, is refused. |
|
||||
| Stop users adding a second profile, for example to reach a personal NetBird account | [`disableProfiles`](#disableProfiles) to `true` | Use their current profile as normal. They can still sign out and sign in to a different account; see the [`managementURL`](#managementURL) note. |
|
||||
| Make sure users keep the networks and exit node you assign | [`disableNetworks`](#disableNetworks) to `true` | Use the assigned networks and exit node. They cannot turn them off. To send all internet traffic through an exit node, also enable **Auto Apply** on it in the dashboard. |
|
||||
| Give non-technical users a simpler app | [`disableAdvancedView`](#disableAdvancedView) to `true` | Everything except the **Peers** and **Resources** lists. |
|
||||
| Lock the app down completely, for example on shared or kiosk devices | all four to `true` | Connect and disconnect. |
|
||||
|
||||
None of these settings stops a user from disconnecting NetBird. See [What a policy does not do](#what-a-policy-does-not-do).
|
||||
|
||||
### Control what the device exposes
|
||||
|
||||
| You want to | Set | What to know |
|
||||
| --- | --- | --- |
|
||||
| Stop other peers opening connections to a device | [`blockInbound`](#blockInbound) to `true` | Outbound connections still work. It also stops the device's SSH server and its routing. Never use it on a routing peer. |
|
||||
| Make sure no NetBird SSH server runs on a device | [`allowServerSSH`](#allowServerSSH) to `false` | Wins even when SSH is enabled for the peer in the dashboard. |
|
||||
| Allow the NetBird SSH server on a device | [`allowServerSSH`](#allowServerSSH) to `true` | SSH must also be enabled for the peer in the dashboard, and `blockInbound` must be off. |
|
||||
| Stop a laptop from carrying other peers' traffic | [`disableServerRoutes`](#disableServerRoutes) to `true` | Protects against a laptop wrongly assigned as a routing peer. Never use it on a real routing peer. |
|
||||
| Stop a device using routed networks and exit nodes | [`disableClientRoutes`](#disableClientRoutes) to `true` | Cuts the device off from every network resource and exit node, so it is rarely what you want on a laptop. |
|
||||
|
||||
### Encrypt the tunnel
|
||||
|
||||
| You want to | Set | What to know |
|
||||
| --- | --- | --- |
|
||||
| Add a shared secret to every connection | [`preSharedKey`](#preSharedKey) | Every peer, including routing peers and servers, needs the same key, or they cannot connect to each other. Local users can read the key. |
|
||||
| Add post-quantum protection | [`rosenpassEnabled`](#rosenpassEnabled) and [`rosenpassPermissive`](#rosenpassPermissive) to `true` | Permissive mode lets peers without Rosenpass still connect. Remove it once every peer runs Rosenpass. |
|
||||
|
||||
### Support and monitoring
|
||||
|
||||
| You want to | Set | What to know |
|
||||
| --- | --- | --- |
|
||||
| Let support collect debug bundles remotely | [`allowRemoteJobs`](#allowRemoteJobs) to `true`, and [`debugBundleUploadURL`](#debugBundleUploadURL) to your upload server | Bundles hold peer names, IP addresses, and logs; the upload URL keeps them on infrastructure you control. |
|
||||
| Make sure remote jobs never run on a device | [`allowRemoteJobs`](#allowRemoteJobs) to `false` | Remote jobs are off by default; pinning `false` stops anyone turning them on. |
|
||||
| Scrape connection state with Prometheus | [`enableLocalMetrics`](#enableLocalMetrics) to `true` | Listens on `127.0.0.1:9191` by default. Keep `localMetricsAddress` on loopback: the endpoint has no authentication. |
|
||||
|
||||
### Tune the connection
|
||||
|
||||
| You want to | Set | What to know |
|
||||
| --- | --- | --- |
|
||||
| Keep every connection open on a device | [`lazyConnection`](#lazyConnection) to `false` | Overrides the account setting for that device. `NB_LAZY_CONN` on the device beats this key. |
|
||||
| Force lazy connections on a device | [`lazyConnection`](#lazyConnection) to `true` | Same precedence as above. |
|
||||
| Use a fixed WireGuard port | [`wireguardPort`](#wireguardPort) | Only when another program needs the default port or your firewall rules expect a specific one. |
|
||||
|
||||
## Recommended policies
|
||||
|
||||
Start from the column that matches the device group, then add or remove keys for your own posture. Send each policy to one device group, and keep routing peers out of every group that gets `blockInbound` or `disableServerRoutes`.
|
||||
|
||||
| Key | User devices | Shared or kiosk devices | Routing peers and servers |
|
||||
| --- | --- | --- | --- |
|
||||
| `managementURL` | ✓ | ✓ | ✓ |
|
||||
| `disableUpdateSettings` | ✓ | ✓ | ✓ |
|
||||
| `disableProfiles` | ✓ | ✓ | ✓ |
|
||||
| `disableServerRoutes` | ✓ | ✓ | never |
|
||||
| `blockInbound` | optional | ✓ | never |
|
||||
| `allowRemoteJobs` and `debugBundleUploadURL` | optional | optional | optional |
|
||||
| `disableNetworks` | optional | ✓ | |
|
||||
| `disableAdvancedView` | optional | ✓ | |
|
||||
| `disableAutoConnect: false` (always connect at boot) | | ✓ | ✓ |
|
||||
|
||||
Why each column looks the way it does:
|
||||
|
||||
- **User devices**, such as laptops and desktops, consume resources and never serve them, so `disableServerRoutes` stops one that is wrongly assigned as a routing peer from carrying other peers' traffic. Add `blockInbound` if nothing ever needs to reach these devices. Leave it out if IT reaches them over [NetBird SSH](/manage/peers/ssh), and control inbound access with access policies instead. Add `disableNetworks` to stop users choosing networks and exit nodes (the ones the dashboard assigns keep working), and `disableAdvancedView` to hide the **Peers** and **Resources** lists.
|
||||
- **Shared or kiosk devices** should leave the user as little as possible to change: no network or exit node choice, no advanced view, and a connection at every boot.
|
||||
- **Routing peers and servers** must keep forwarding traffic and accepting connections, so they never get `blockInbound` or `disableServerRoutes`. This column only applies to Windows and macOS machines; Linux has no MDM channel, so configure Linux routing peers with [CLI flags](#when-not-to-use-mdm-policy).
|
||||
- **Remote jobs** are optional in every column. Turn on `allowRemoteJobs` wherever your support team collects [debug bundles](/manage/peers/remote-jobs) remotely, and always pin `debugBundleUploadURL` with it, so bundles go to an upload server you control. Routing peers are often the first place to look when a resource is unreachable or relayed.
|
||||
|
||||
Leave these keys out unless you have a specific reason:
|
||||
|
||||
- `preSharedKey`, `rosenpassEnabled`, `rosenpassPermissive`: each has to match on every peer, or devices lose their connections. See [Encryption keys](#encryption-keys).
|
||||
- `disableClientRoutes`: cuts a device off from every network resource and exit node.
|
||||
- `wireguardPort`: the default suits almost every fleet.
|
||||
- `lazyConnection`: leave lazy connections to the account setting, so one dashboard switch controls the whole fleet. Push `false` only for devices that must hold every connection open.
|
||||
- `enableLocalMetrics`, `localMetricsAddress`: only for devices you scrape with Prometheus.
|
||||
- `splitTunnelMode`, `splitTunnelApps`, `disableMetricsCollection`: no effect yet.
|
||||
|
||||
### The user device baseline
|
||||
|
||||
The three keys every column above shares make the usual starting policy, and the platform pages use it as their example. With a self-hosted management server:
|
||||
|
||||
| Key | Value | What it does |
|
||||
| --- | --- | --- |
|
||||
| `managementURL` | `https://netbird.example.com:443` | Every device talks to your server and nothing else |
|
||||
| `disableUpdateSettings` | `true` | Users can connect and disconnect, but cannot change settings |
|
||||
| `disableProfiles` | `true` | Users cannot add a second profile |
|
||||
|
||||
On NetBird Cloud, use `https://api.netbird.io:443`. Send the policy only to the device group that holds your user devices, so routing peers and servers stay out of it.
|
||||
|
||||
## How the client applies a policy
|
||||
|
||||
Think of a policy as **a set of pinned switches**. Every key in the payload pins one setting to the value you chose and takes it out of the user's reach. Every key you leave out stays an ordinary setting that the user, the CLI, or the dashboard controls as usual. **A key that is absent is not pinned, and a key that is present is pinned, whatever its value.**
|
||||
|
||||
<Note>
|
||||
MDM is **authoritative**. When a key is present in the MDM payload, the
|
||||
MDM value wins regardless of whether it is `true` or `false`. An admin
|
||||
pushing `disableNetworks=false` via MDM re-enables the feature even on a
|
||||
host that was installed with `--disable-networks`. The MDM payload is
|
||||
the source of truth as long as the policy is in place. MDM-supplied
|
||||
values are also never written back to the on-disk profile, so removing
|
||||
the policy at the MDM side takes effect on the next reload with no
|
||||
stale residue on the device.
|
||||
**Pushing `false` is not the same as leaving a key out.** `allowServerSSH: false` pins the SSH server off: no user, CLI flag, or dashboard setting can turn it on while the policy stands. Leaving `allowServerSSH` out of the payload leaves the SSH server to whoever controls the device. Only include the keys you mean to pin.
|
||||
</Note>
|
||||
|
||||
On Windows and macOS, the client daemon reads the policy at startup and again every minute. Each time:
|
||||
|
||||
1. **It applies the policy on top of every other configuration layer.** Defaults, the on-disk profile, environment variables, and CLI or app input all apply first; the policy is applied last and wins. A policy also overrides the service-install flags `--disable-update-settings`, `--disable-profiles`, and `--disable-networks`: pushing `disableNetworks: false` re-enables network selection even on a device installed with `--disable-networks`. One exception: the `NB_LAZY_CONN` environment variable takes precedence over the `lazyConnection` key.
|
||||
2. **It locks the pinned settings.** A request that tries to change a pinned setting to a different value, from the desktop app, the CLI (`netbird login --flag`, or `netbird up --flag` on a disconnected client) or the daemon API, is rejected as a whole with `fields managed by MDM cannot be modified: [<keys>]`. None of its other fields are applied either. A request that repeats the pinned value is accepted, because it changes nothing. These locks read the policy file on every request, so they apply as soon as the file changes; the `disableUpdateSettings`, `disableProfiles`, and `disableNetworks` gates apply at the next reload. Two keys only change what the app shows or does, and lock nothing: [`disableAutostart`](#disableAutostart) and [`disableAdvancedView`](#disableAdvancedView).
|
||||
3. **It never writes policy values to disk.** The on-disk profile keeps the device's own settings. When you remove a key from the policy, the setting falls back to that on-disk value on the next reload and becomes editable again, with nothing left behind.
|
||||
|
||||
When a reload finds that the policy has changed, the client restarts its connection to apply it. Expect a brief interruption on every policy change (typically a second or two), and plan larger changes outside working hours. The desktop app then shows a notification: **NetBird settings updated**, "Your NetBird configuration was updated by your IT policy."
|
||||
|
||||
On iOS the timing is different: the app passes the policy to the tunnel only while the app is running. See [Enforce settings on iOS](/manage/peers/mdm-deployment/ios-mdm-policy#when-a-policy-takes-effect).
|
||||
|
||||
### What a user sees
|
||||
|
||||
Since NetBird v0.75.0, the desktop app **hides** the controls for pinned settings rather than greying them out. With the user device baseline, the server address field is gone. Read-only mode removes the **Network**, **Security**, **SSH**, and **Advanced** settings tabs, and `disableProfiles` removes the **Profiles** tab. **General**, **Troubleshooting**, and **About** remain, and the **Connect** button still works.
|
||||
|
||||
On the CLI, changing a pinned setting fails with the error above. The [`disableUpdateSettings`](#disableUpdateSettings), [`disableProfiles`](#disableProfiles), and [`disableNetworks`](#disableNetworks) keys have errors of their own, listed under [Troubleshooting](#troubleshooting).
|
||||
|
||||
### What a policy does not do
|
||||
|
||||
A policy pins the client's configuration. It does not make the tunnel mandatory: a user can still disconnect with `netbird down` or the **Disconnect** button, no key blocks `netbird logout` of the active profile (which removes the peer from your account), and anyone with administrator rights on the device can stop the service, uninstall the client, or edit the registry or the managed-preferences file. NetBird has no always-on mode. Enforce that layer with the operating system: give users standard accounts, manage the NetBird service with your MDM, and write your [access policies](/manage/access-control) so that disconnecting from NetBird costs a user access to company resources rather than freeing them from restrictions.
|
||||
|
||||
## Policy keys reference
|
||||
|
||||
The same 21 keys apply on every platform. Names are camelCase in the
|
||||
managed-configuration payload; the Windows ADMX template renders the
|
||||
PascalCase variant in the Group Policy Editor — both are recognized.
|
||||
The client recognizes 24 keys. They are grouped below by the job they do, starting with the ones nearly every rollout needs. A key that has no meaning on a platform (for example `allowServerSSH` on iOS) is ignored there, so one payload is safe to send to every platform.
|
||||
|
||||
| Key | Type | Description |
|
||||
Key names are camelCase in plist and app-configuration payloads. On Windows, the registry value names and the Group Policy templates use PascalCase (`ManagementURL`, `BlockInbound`); both forms are recognized, and names are matched without regard to case. In the Group Policy editor, each policy is named after its key in plain words, such as **Block inbound** for `blockInbound`.
|
||||
|
||||
Value types:
|
||||
|
||||
- **boolean**: a native boolean (`<true/>` or `<false/>` in a plist), or one of the strings `true`/`false`, `1`/`0`, `yes`/`no`, `on`/`off`. An integer (`0` is false, anything else is true) also works on Windows (`REG_DWORD`) and iOS, but **not on macOS**: there, a boolean written as `<integer>` is listed as managed and locked, but never applied, so the setting is frozen at whatever the device had.
|
||||
- **string**: a string. An empty string counts as not set.
|
||||
- **integer**: an integer, or a string of decimal digits.
|
||||
|
||||
The registry type for each key on Windows is on [Enforce settings on Windows](/manage/peers/mdm-deployment/windows-mdm-policy#value-types).
|
||||
|
||||
### Server and startup keys
|
||||
|
||||
Nearly every rollout sets `managementURL`. The other two keys decide whether NetBird connects, and whether its app opens, on their own.
|
||||
|
||||
| Key | Type | What it does |
|
||||
| --- | --- | --- |
|
||||
| `managementURL` | string | Override the management server URL (e.g. `https://api.netbird.io:443` or a self-hosted URL). |
|
||||
| `preSharedKey` | string | WireGuard pre-shared key. Treated as secret and redacted in logs. |
|
||||
| `wireguardPort` | integer | UDP port the local WireGuard interface binds to. Range `1–65535`. |
|
||||
| `allowServerSSH` | boolean | Allow the embedded NetBird SSH server on this peer. |
|
||||
| `allowRemoteJobs` | boolean | Allow management-requested remote jobs (e.g. debug bundles) on this peer. Off by default; equivalent to `--allow-remote-jobs`. |
|
||||
| `debugBundleUploadURL` | string | Override the upload service used for debug bundles produced by remote jobs, taking precedence over the value requested by Management. Must be an `https` URL with a host. |
|
||||
| `disableAutoConnect` | boolean | Skip auto-connecting on startup; require an explicit `netbird up`. |
|
||||
| <span id="disableAutostart"></span>`disableAutostart` | boolean | Prevent the GUI from registering itself as an OS autostart entry on fresh installs, and — when enabled at any later point — remove an existing registration on the next GUI launch (Windows Registry `Run` key, macOS Login Item, Linux `.desktop`). Desktop GUIs only; no-op on iOS/Android. Once the admin lifts the policy, the setting stays off until the user re-enables it in Settings. |
|
||||
| `lazyConnection` | boolean | Local override for lazy connections. `true` forces lazy connections on, `false` forces them off, and an absent key defers to the Management setting. `NB_LAZY_CONN` takes precedence when both are configured. |
|
||||
| `rosenpassEnabled` | boolean | Turn on the post-quantum Rosenpass key exchange. |
|
||||
| `rosenpassPermissive` | boolean | Permissive mode for Rosenpass (interop with non-Rosenpass peers). |
|
||||
| `blockInbound` | boolean | Drop all inbound traffic except established/related — kill-switch style. |
|
||||
| `disableClientRoutes` | boolean | This peer does not route traffic to other peers. |
|
||||
| `disableServerRoutes` | boolean | This peer is not a router for others. |
|
||||
| `disableMetricsCollection` | boolean | Disable anonymous usage telemetry. |
|
||||
| `disableUpdateSettings` | boolean | Block every configuration change from UI or CLI on this device (read-only mode). |
|
||||
| `disableProfiles` | boolean | Hide the profile menu in the GUI and reject profile CRUD via CLI. |
|
||||
| `disableNetworks` | boolean | Hide the Networks / Exit Node menus in the GUI and reject the related RPCs. |
|
||||
| `enableLocalMetrics` | boolean | Expose the client's [local Prometheus `/metrics` endpoint](/client/local-metrics). |
|
||||
| `localMetricsAddress` | string | Listen address of the local `/metrics` endpoint (default `127.0.0.1:9191`). |
|
||||
| <span id="managementURL"></span>`managementURL` | string | The management server the client connects to, for example `https://api.netbird.io:443` for NetBird Cloud or `https://netbird.example.com:443` for a self-hosted server. Must start with `http://` or `https://`; a URL without a port gets `:443` (or `:80`) added. An invalid URL is logged and ignored, and the client keeps its previous server. |
|
||||
| <span id="disableAutoConnect"></span>`disableAutoConnect` | boolean | `true`: the client does not connect when its service starts, so the user has to click **Connect** or run `netbird up`. `false`: pins [Connect on Startup](/client/connect-on-startup) on, so the client always connects at boot. |
|
||||
| <span id="disableAutostart"></span>`disableAutostart` | boolean | `true`: the desktop app does not open itself at login. The app removes its login entry (the Windows `Run` registry key or the macOS Login Item) every time it starts, and never creates one on a fresh install. The background service is not affected. Desktop app only. |
|
||||
|
||||
- **`managementURL` pins the server, not the account.** NetBird Cloud serves every account from `https://api.netbird.io:443`, so on Cloud this key stops a user from pointing the client at another server, but not from signing in to another Cloud account. No key pins the account: even with `managementURL`, `disableProfiles`, and `disableUpdateSettings` all pinned, `netbird logout` followed by `netbird up --setup-key <key>` moves the device to another account on the same server. A device signed in to another account has no access to your resources, because your access policies do not apply to it.
|
||||
- **`disableAutostart` only acts when the app starts.** The **Launch NetBird UI at Login** switch in the app's settings stays usable. A user who turns it back on gets the app at their next login, and the app removes the entry again as soon as it opens. Pushing `false` does not turn launch-at-login on; it only stops the app from removing the entry.
|
||||
|
||||
### Client app keys
|
||||
|
||||
These keys decide how much of the app a user can touch. The user device baseline uses the first two.
|
||||
|
||||
| Key | Type | What it does |
|
||||
| --- | --- | --- |
|
||||
| <span id="disableUpdateSettings"></span>`disableUpdateSettings` | boolean | Read-only mode. Every request that would change a setting is rejected, from the app or the CLI (`netbird login --flag`, or `netbird up --flag` on a disconnected client). Connecting, disconnecting, and signing in again still work, because they change no settings. |
|
||||
| <span id="disableProfiles"></span>`disableProfiles` | boolean | Hides profile management in the app and rejects creating, renaming, removing, or switching [profiles](/client/profiles). The device stays on its current profile. |
|
||||
| <span id="disableNetworks"></span>`disableNetworks` | boolean | Stops the user changing which networks and exit node the device uses. Networks and exit nodes **keep working**: the device still reaches the resources and exit node the dashboard assigns to it. Only the choice is taken away. The app hides the exit node switcher and the **Resources** tab, and `netbird networks list`, `select`, and `deselect` are rejected. The device keeps whatever selection it had when the policy arrived. |
|
||||
| <span id="disableAdvancedView"></span>`disableAdvancedView` | boolean | Removes **Advanced View** from the desktop app's menu, along with its **Peers** and **Resources** tabs: the list of peers this device can see, with each connection's details, and the list of network resources. A user already in that view is switched back to the simple view. On iOS it hides the Advanced section. It only changes what the app shows: no setting is locked, and no request is rejected because of it. |
|
||||
| `allowGroups` | string | Let only the listed groups connect to the daemon socket. Overrides `--allow-group`. An empty value removes the restriction. On macOS, set one `gid:<id>` value. On Windows, set comma-separated `sid:<SID>` values. See [Restrict daemon access to a group](/client/profile-ownership#restrict-daemon-access-to-a-group). |
|
||||
| `splitTunnelMode` | string | `allow` or `disallow` — split-tunnel policy mode (Android only at the client level; harmless on desktop). |
|
||||
| `splitTunnelApps` | string | Comma-separated list of package names that the split-tunnel mode applies to (Android only). |
|
||||
|
||||
### Notes on a few keys
|
||||
- **`disableUpdateSettings` covers settings no key names.** The DNS, firewall, interface, and SSH sub-options have no key of their own, so the only way to stop users changing them is read-only mode. On a device with `disableUpdateSettings: true`, an administrator who needs to change one of those settings has to lift the policy first.
|
||||
- **`disableNetworks` plus an exit node pins the device to that exit node.** With an exit node that has **Auto Apply** enabled, the exit node is applied automatically and the user cannot deselect it. See [Enforcing the exit node on managed devices](/use-cases/remote-access/exit-nodes#enforcing-the-exit-node-on-managed-devices) for the full recipe, including why the policy has to reach the devices before users touch the exit node switch.
|
||||
|
||||
- `disableUpdateSettings` and `disableProfiles` overlap with the
|
||||
service-install CLI flags `--disable-update-settings` and
|
||||
`--disable-profiles`. Either source can disable the feature; the MDM
|
||||
value wins when present.
|
||||
- `disableUpdateSettings` keeps the Settings view in the GUI visible
|
||||
(so users can inspect current values) but rejects every attempt to
|
||||
save changes. Use it for read-only fleets.
|
||||
- `disableNetworks` combined with an exit node that has Auto Apply
|
||||
enabled pins a device to that exit node: the exit node applies
|
||||
automatically and the daemon rejects every attempt to deselect it.
|
||||
See [Enforcing the Exit Node on Managed Devices](/use-cases/remote-access/exit-nodes#enforcing-the-exit-node-on-managed-devices)
|
||||
for the full recipe, including why the policy must be deployed
|
||||
before users touch the exit node switch.
|
||||
- `localMetricsAddress` is applied as pushed. Users are held to a
|
||||
loopback address unless they are root, because the endpoint is
|
||||
unauthenticated, but a policy already comes from an administrator and
|
||||
is not subject to that check. A non-loopback address publishes peer
|
||||
names and connectivity state to anything that can reach the port.
|
||||
- `splitTunnelMode` and `splitTunnelApps` are wired into Android's
|
||||
`VpnService.Builder.addAllowedApplication()` flow; on Windows and
|
||||
macOS the daemon parses the keys but ignores them. They are safe to
|
||||
ship in a cross-platform payload.
|
||||
- `disableAutostart` only affects the desktop GUI's OS autostart entry
|
||||
(Windows Registry `Run` key, macOS Login Item, Linux `.desktop`).
|
||||
iOS and Android have no equivalent user-space autostart mechanism —
|
||||
the daemon runs as a system service — so the key is parsed and
|
||||
ignored there. Safe to ship in cross-platform payloads.
|
||||
- The `disableMetricsCollection` key is reserved for an upcoming
|
||||
metrics integration; the client recognizes it today but no metrics
|
||||
pipeline is shipped yet.
|
||||
### Device exposure keys
|
||||
|
||||
## Windows
|
||||
These keys decide what reaches the device, and what the device passes on. Each pins a client-side setting that also exists in the app and the CLI, and each one wins over what the dashboard configures: routes, routing-peer assignments, SSH, and access policies.
|
||||
|
||||
The NetBird daemon reads policies from
|
||||
`HKLM\Software\Policies\NetBird`. Anything that ends up under that
|
||||
registry key — through whichever delivery channel — becomes policy.
|
||||
The shapes are:
|
||||
|
||||
| Value name | Registry type | Example |
|
||||
| Key | Type | What it does |
|
||||
| --- | --- | --- |
|
||||
| `ManagementURL`, `PreSharedKey`, `SplitTunnelMode`, `SplitTunnelApps` | `REG_SZ` | `"https://api.netbird.io:443"` |
|
||||
| All `Disable*` flags, `AllowServerSSH`, `RosenpassEnabled`, `RosenpassPermissive` | `REG_DWORD` (0 / 1) | `0x00000001` |
|
||||
| `WireguardPort` | `REG_DWORD` | `0x0000ca6c` (51820 decimal) |
|
||||
| <span id="blockInbound"></span>`blockInbound` | boolean | `true`: drops every inbound connection from other peers, whatever your access policies allow. It also stops the SSH server and stops the device acting as a routing peer. Outbound connections from the device are not affected. See [Block Inbound Connections](/client/block-inbound-connections). |
|
||||
| <span id="allowServerSSH"></span>`allowServerSSH` | boolean | `false`: the [NetBird SSH server](/manage/peers/ssh) never runs on this device, even if SSH is enabled for the peer in the dashboard. `true`: lets it run, but only once SSH is also enabled for the peer in the dashboard and `blockInbound` is off. |
|
||||
| <span id="disableServerRoutes"></span>`disableServerRoutes` | boolean | `true`: the device never acts as a [routing peer](/manage/networks/how-routing-peers-work). It does not forward traffic to the networks, resources, or exit node it is assigned to serve. |
|
||||
| <span id="disableClientRoutes"></span>`disableClientRoutes` | boolean | `true`: the device ignores the routes it receives. It does not install routes to network resources reached through a routing peer, and it cannot use an exit node. It can still reach other peers directly. |
|
||||
|
||||
Choose one of the delivery channels below. All four converge on the
|
||||
same registry key.
|
||||
- **The two route keys are easy to mix up.** "Client" and "server" describe the device's role in a route, not the direction of the traffic. `disableClientRoutes` stops the device *using* routes, which is what an end-user laptop does. `disableServerRoutes` stops the device *providing* routes, which is what a routing peer does. On a laptop, `disableClientRoutes: true` cuts it off from every network resource and exit node, which is rarely what you want. `disableServerRoutes: true` is the safe choice for laptops, because it stops a laptop that has been wrongly assigned as a routing peer from carrying other peers' traffic.
|
||||
- **Do not send `blockInbound` or `disableServerRoutes` to routing peers.** A routing peer with either key set stops forwarding. If it is the only routing peer for a network, that network becomes unreachable for everyone. Scope these keys to a device group that excludes routing peers and servers.
|
||||
- **`blockInbound` is not a kill switch.** It blocks connections *to* the device, not traffic *from* it, and it does not keep the tunnel up. Use it for devices that only ever consume resources, such as most end-user laptops, and never for a device other peers need to reach.
|
||||
|
||||
### Group Policy (on-prem AD / local gpedit)
|
||||
### Encryption keys
|
||||
|
||||
For a full end-to-end walkthrough — domain Central Store, GPO creation, silent MSI install, and troubleshooting — see [Deploying NetBird with Group Policy (GPO)](/manage/peers/mdm-deployment/windows-gpo-deployment). The steps below cover the minimal local setup.
|
||||
These keys change how connections are encrypted. A pre-shared key, and Rosenpass outside permissive mode, have to match on both ends of a connection, so a key sent to only part of a fleet can cut that part off from the rest.
|
||||
|
||||
1. Copy the ADMX/ADML files into the system Policy Definitions store:
|
||||
- Place `netbird.admx` in `C:\Windows\PolicyDefinitions\`.
|
||||
- Place `netbird.adml` in `C:\Windows\PolicyDefinitions\en-US\`.
|
||||
2. Open `gpedit.msc` (or the AD Group Policy Management Editor).
|
||||
3. Navigate to **Computer Configuration → Administrative Templates →
|
||||
NetBird**.
|
||||
4. Edit any policy (e.g. **Management URL**), set it to **Enabled**
|
||||
with the desired value, and click **OK**.
|
||||
5. Run `gpupdate /force` on each target device (or wait for the
|
||||
periodic refresh).
|
||||
6. Verify with `reg query HKLM\Software\Policies\NetBird` — the values
|
||||
you set should appear there.
|
||||
| Key | Type | What it does |
|
||||
| --- | --- | --- |
|
||||
| <span id="preSharedKey"></span>`preSharedKey` | string | A WireGuard pre-shared key mixed into every connection this device makes. Only peers configured with the **same** key can connect to each other. Redacted in logs, and never returned by the client API. |
|
||||
| <span id="rosenpassEnabled"></span>`rosenpassEnabled` | boolean | Turns on [Rosenpass](/client/post-quantum-cryptography), a post-quantum key exchange that rotates a fresh pre-shared key every two minutes. Experimental. A device with Rosenpass on cannot connect to a peer without it unless `rosenpassPermissive` is also on. |
|
||||
| <span id="rosenpassPermissive"></span>`rosenpassPermissive` | boolean | With Rosenpass on, lets this device fall back to a standard WireGuard connection with a peer that does not run Rosenpass, and keeps using Rosenpass with the peers that do. |
|
||||
|
||||
Download the templates: <a href="/docs-static/files/netbird.admx" download>netbird.admx</a> / <a href="/docs-static/files/netbird.adml" download>netbird.adml</a>.
|
||||
- **A pre-shared key splits your network in two.** Devices that have the key and devices that do not cannot connect to each other. If you pushed a `preSharedKey` to your laptops only, they would lose every routing peer and server that does not carry the same key. Only use it when every peer, including routing peers and servers, gets the same key.
|
||||
- **The pre-shared key is not hidden from local users.** By default, standard users can read the NetBird policy key on Windows, and the macOS managed-preferences file is readable by every user. Treat the key as an extra layer of protection, never as a credential that only administrators know.
|
||||
- **To roll out Rosenpass, start in permissive mode.** Push `rosenpassEnabled: true` with `rosenpassPermissive: true`, so devices that do not have Rosenpass yet can still connect. Remove permissive mode once every peer runs Rosenpass.
|
||||
|
||||
### Microsoft Intune (ADMX ingestion)
|
||||
### Support and monitoring keys
|
||||
|
||||
Recommended for cloud-managed Windows fleets.
|
||||
These keys turn on remote diagnostics and local metrics. Both features expose information about the device, so the safe default is to leave them off and pin them on only where you need them.
|
||||
|
||||
1. In the Intune admin center, go to **Devices → Configuration → Import
|
||||
ADMX**, upload `netbird.admx` together with `netbird.adml`. Wait for
|
||||
the **Available** status.
|
||||
2. Create a new **Configuration Profile → Templates → Imported
|
||||
Administrative templates → NetBird**.
|
||||
3. Configure the policies you want to enforce.
|
||||
4. Assign the profile to your device group(s) and save.
|
||||
| Key | Type | What it does |
|
||||
| --- | --- | --- |
|
||||
| <span id="allowRemoteJobs"></span>`allowRemoteJobs` | boolean | `true`: lets the management server run [remote jobs](/manage/peers/remote-jobs) on this device, such as collecting a debug bundle. Off by default. `false`: pins remote jobs off, so a user cannot turn them on. |
|
||||
| <span id="debugBundleUploadURL"></span>`debugBundleUploadURL` | string | Where debug bundles produced by remote jobs are uploaded, overriding the location the management server asks for. Must be an `https` URL with a host; any other value is ignored. Treated as a secret and never logged, because the URL can carry credentials or a signed token. |
|
||||
| <span id="enableLocalMetrics"></span>`enableLocalMetrics` | boolean | Turns on the client's [local Prometheus `/metrics` endpoint](/client/local-metrics), which reports peer connection state, latency, and whether each connection is direct or relayed. |
|
||||
| <span id="localMetricsAddress"></span>`localMetricsAddress` | string | The address the `/metrics` endpoint listens on. Default `127.0.0.1:9191`. |
|
||||
|
||||
Devices pick up the policy on the next Intune sync (typically within
|
||||
8 hours, sooner if you trigger a manual sync from the device). The
|
||||
values end up in `HKLM\Software\Policies\NetBird`.
|
||||
- **Pin the upload URL wherever you allow remote jobs.** A debug bundle holds peer names, IP addresses, and logs. Setting `debugBundleUploadURL` to an upload server you run keeps bundles on infrastructure you control.
|
||||
- **Keep the metrics endpoint on loopback.** The endpoint has no authentication. A user without administrator rights can only bind it to a loopback address, but a policy value is applied exactly as written. A non-loopback `localMetricsAddress` publishes peer names and connection state to anything that can reach the port.
|
||||
|
||||
### Microsoft Intune (custom OMA-URI)
|
||||
### Connection tuning keys
|
||||
|
||||
If you cannot ingest the ADMX template, you can push individual values
|
||||
via OMA-URI under
|
||||
`./Device/Vendor/MSFT/Policy/ConfigOperations/ADMXInstall/...` or via
|
||||
the Registry CSP at
|
||||
`./Device/Vendor/MSFT/Registry/HKEY_LOCAL_MACHINE/Software/Policies/NetBird/<ValueName>`.
|
||||
ADMX ingestion is simpler and gives admins the same UI as on-prem GPO,
|
||||
so prefer that.
|
||||
Most fleets never need these keys.
|
||||
|
||||
### `.reg` import (single source of truth)
|
||||
| Key | Type | What it does |
|
||||
| --- | --- | --- |
|
||||
| <span id="lazyConnection"></span>`lazyConnection` | boolean | Overrides what the management server decides about [lazy connections](/manage/peers/lazy-connection) for this device. `true` forces them on and `false` forces them off, for every peer the device connects to. Leaving the key out follows the account's **Lazy connections** setting, which is on by default for accounts created by management v0.74.0 or later; older accounts keep the setting they had. The `NB_LAZY_CONN` environment variable, if set on the device, takes precedence over this key. |
|
||||
| <span id="wireguardPort"></span>`wireguardPort` | integer | The local UDP port the WireGuard interface listens on. Default `51820`. Values outside `1` to `65535` are ignored, and the client keeps its previous port. |
|
||||
|
||||
For fleets without an MDM, or as a quick-test path, you can carry the
|
||||
whole policy in a single `.reg` file:
|
||||
Set `wireguardPort` only when another program on the device needs the default port, or your firewall rules expect a specific one.
|
||||
|
||||
1. Configure the policy values on a reference machine (via `gpedit` or
|
||||
`reg add`).
|
||||
2. Export the key:
|
||||
```
|
||||
reg export "HKLM\Software\Policies\NetBird" netbird-policy.reg /y
|
||||
```
|
||||
3. Distribute the resulting file and apply with:
|
||||
```
|
||||
reg import netbird-policy.reg
|
||||
```
|
||||
### Keys with no effect yet
|
||||
|
||||
Download a sample: <a href="/docs-static/files/netbird-policy.reg" download>netbird-policy.reg</a>.
|
||||
The client recognizes these keys, so they do no harm in a payload, but none of them changes anything today.
|
||||
|
||||
### JumpCloud
|
||||
|
||||
NetBird ships a JumpCloud companion script: <a href="/docs-static/files/netbird-policy.reg.ps1" download>netbird-policy.reg.ps1</a>. To use it:
|
||||
|
||||
1. In the JumpCloud admin console, go to **Device Management →
|
||||
Commands → +**.
|
||||
2. Type: **Windows PowerShell**. Run as: **SYSTEM**.
|
||||
3. Paste `netbird-policy.reg.ps1` verbatim into the command body.
|
||||
4. In the same command, attach the `netbird-policy.reg` file you
|
||||
produced above. JumpCloud copies attached files into the command's
|
||||
working directory before invoking the script.
|
||||
5. Bind the command to the target system group and run it.
|
||||
|
||||
The script wipes the existing `HKLM\Software\Policies\NetBird` key
|
||||
before importing the `.reg`, so the `.reg` is the **single source of
|
||||
truth** for that device. To unset all policy, attach an empty (header-
|
||||
only) `.reg`; the daemon will pick up the absence on the next reload.
|
||||
|
||||
## macOS
|
||||
|
||||
The NetBird daemon reads policy from
|
||||
`/Library/Managed Preferences/io.netbird.client.plist`. macOS writes
|
||||
that file when an MDM provider pushes a Configuration Profile whose
|
||||
`com.apple.ManagedClient.preferences` payload targets the bundle id
|
||||
`io.netbird.client`.
|
||||
|
||||
<Warning>
|
||||
macOS wipes the contents of `/Library/Managed Preferences/` on every
|
||||
boot if the device is not MDM-enrolled. Manual `defaults write` works
|
||||
for a quick test but does not survive a reboot on an un-enrolled Mac.
|
||||
Use a real MDM channel for production rollouts.
|
||||
</Warning>
|
||||
|
||||
### Custom Configuration Profile (recommended)
|
||||
|
||||
This is the canonical macOS path and works with every MDM
|
||||
(Jamf, Kandji, Mosyle, Microsoft Intune for Mac, Workspace ONE,
|
||||
JumpCloud, Apple Configurator 2, etc.).
|
||||
|
||||
1. Start from the template <a href="/docs-static/files/netbird-macos.mobileconfig" download>netbird-macos.mobileconfig</a>. Open it in your editor (or in
|
||||
[iMazing Profile Editor](https://imazing.com/profile-editor) /
|
||||
[ProfileCreator](https://github.com/ProfileCreator/ProfileCreator)).
|
||||
2. Inside the `mcx_preference_settings` dictionary, set the keys you
|
||||
want to enforce. Keep the bundle id `io.netbird.client` as the
|
||||
preference domain.
|
||||
3. Replace the placeholder `PayloadUUID` values with freshly generated
|
||||
UUIDs (`uuidgen` on macOS) so each deployment has unique ids.
|
||||
4. (Optional, recommended for production) sign the profile with your
|
||||
organization's Developer ID Installer certificate using
|
||||
`productsign` — unsigned profiles on Sonoma/Sequoia/Tahoe require
|
||||
an extra user confirmation on install.
|
||||
5. Upload the resulting `.mobileconfig` to your MDM as a **Custom
|
||||
Configuration Profile** and scope it to the target device group.
|
||||
|
||||
Verify on a target device with:
|
||||
|
||||
```bash
|
||||
sudo defaults read "/Library/Managed Preferences/io.netbird.client"
|
||||
```
|
||||
|
||||
The output should match the keys you set in the profile.
|
||||
|
||||
### MDM-specific notes
|
||||
|
||||
- **Jamf Pro**: upload as **Computers → Configuration Profiles → New →
|
||||
Application & Custom Settings → External Applications → Upload File
|
||||
(Plist file)** for the preference domain `io.netbird.client`.
|
||||
- **Kandji**: use the **Custom Profile** assignment library item.
|
||||
- **Mosyle**: **Profiles → Add new profile → Custom Settings** with
|
||||
domain `io.netbird.client`.
|
||||
- **Microsoft Intune (for Mac)**: **Devices → Configuration → Create
|
||||
profile → macOS → Templates → Custom**, upload the `.mobileconfig`.
|
||||
- **Apple Configurator 2** (no MDM, ideal for testing on a tethered
|
||||
device): drag the `.mobileconfig` onto the device in Configurator and
|
||||
push.
|
||||
|
||||
### JumpCloud
|
||||
|
||||
JumpCloud supports two delivery channels for the NetBird policy on
|
||||
macOS. Pick whichever fits how your fleet is enrolled.
|
||||
|
||||
#### MDM Custom Configuration Profile (recommended for MDM-enrolled fleets)
|
||||
|
||||
If your Macs are MDM-enrolled with JumpCloud, push the policy as a
|
||||
managed-preferences plist:
|
||||
|
||||
1. In the JumpCloud admin console, open **Policy Management →
|
||||
Policies → +** and choose the **Mac** platform.
|
||||
2. Pick the **MDM Custom Configuration Profile** policy template.
|
||||
3. Upload <a href="/docs-static/files/io.netbird.client.plist" download>io.netbird.client.plist</a>
|
||||
as the plist payload. Edit the file before upload to enable just
|
||||
the keys you want to enforce — leave the rest commented out.
|
||||
4. Bind the policy to the target Device Group and save.
|
||||
|
||||
Notes:
|
||||
|
||||
- JumpCloud's **MDM Custom Configuration Profile** accepts a bare
|
||||
managed-preferences plist (the inner Apple managed-prefs dictionary)
|
||||
— **not** a full `.mobileconfig` envelope. Uploading
|
||||
`netbird-macos.mobileconfig` will be rejected. Use the bare
|
||||
`io.netbird.client.plist` for this code path; reserve
|
||||
`netbird-macos.mobileconfig` for other MDMs that expect the full
|
||||
Configuration Profile shape.
|
||||
- Keep the filename as `io.netbird.client.plist`. The Apple
|
||||
convention for managed-preferences plists is
|
||||
`<bundle.id>.plist` (this is how macOS materializes the file at
|
||||
`/Library/Managed Preferences/<bundle.id>.plist`), and JumpCloud's
|
||||
policy form does not currently expose a separate bundle-identifier
|
||||
field — keeping the canonical filename is the safest path. If your
|
||||
JumpCloud console version surfaces a bundle-id / preference-domain
|
||||
field elsewhere in the policy wizard, set it to `io.netbird.client`
|
||||
too.
|
||||
|
||||
JumpCloud wraps the plist into an Apple Configuration Profile and
|
||||
pushes it via the MDM channel. The OS materializes the file at
|
||||
`/Library/Managed Preferences/io.netbird.client.plist`, where the
|
||||
NetBird daemon picks it up within the next 1-minute reload tick.
|
||||
Removing the policy from JumpCloud removes the file on the next sync,
|
||||
which un-locks the corresponding fields on the client.
|
||||
|
||||
#### Shell Command (no MDM enrollment required)
|
||||
|
||||
If your fleet is JumpCloud-managed but not MDM-enrolled, NetBird ships
|
||||
a companion script: <a href="/docs-static/files/netbird-macos.sh" download>netbird-macos.sh</a>. It is the macOS
|
||||
counterpart of the Windows `.reg.ps1` script — same fleet, different
|
||||
backend:
|
||||
|
||||
1. Edit the `### POLICY VALUES ###` block at the top of the script;
|
||||
set the variables for the keys you want to enforce and leave the
|
||||
rest at `$NULL`.
|
||||
2. In the JumpCloud admin console, go to **Device Management →
|
||||
Commands → +**. Type: **Mac, Shell**. Run as: **root**.
|
||||
3. Paste the edited script verbatim into the command body.
|
||||
4. Bind to the target system group and run.
|
||||
|
||||
The script writes
|
||||
`/Library/Managed Preferences/io.netbird.client.plist`, sets ownership
|
||||
to `root:wheel` with mode `644`, and kicks the NetBird daemon so the
|
||||
change applies immediately. On MDM-enrolled devices the file survives
|
||||
reboots; on un-enrolled devices the file is wiped at the next reboot
|
||||
(macOS-imposed). Prefer the Custom Mac Application Settings policy
|
||||
above when the fleet is enrolled.
|
||||
| Key | Type | Status |
|
||||
| --- | --- | --- |
|
||||
| <span id="splitTunnelMode"></span>`splitTunnelMode` | string | `allow` or `disallow`. Intended for per-app split tunneling on Android, which has no MDM channel yet. Ignored on every other platform. |
|
||||
| <span id="splitTunnelApps"></span>`splitTunnelApps` | string | A comma-separated list of Android package names for `splitTunnelMode`. Same status. |
|
||||
| <span id="disableMetricsCollection"></span>`disableMetricsCollection` | boolean | Reserved for a future client telemetry feature. Unrelated to `enableLocalMetrics`. |
|
||||
|
||||
## Verifying enforcement
|
||||
|
||||
On any platform, the cleanest verification is the daemon's own debug
|
||||
dump:
|
||||
On Windows and macOS, ask the daemon what it is enforcing:
|
||||
|
||||
```bash
|
||||
netbird debug config
|
||||
```
|
||||
|
||||
The response includes a `mDMManagedFields` array that lists every key
|
||||
the daemon is currently honoring from the MDM source. If a key you
|
||||
expected to be locked is missing from that array, the MDM payload did
|
||||
not reach the device (or used a value name the daemon does not
|
||||
recognize).
|
||||
This prints the resolved configuration as JSON, after every layer including the policy. Its `mDMManagedFields` array lists every key in the policy the client reads. For the user device baseline it reads:
|
||||
|
||||
The client UI mirrors the same state: any submenu item, settings field,
|
||||
or kill switch driven by MDM appears greyed out with a **(MDM)** tag
|
||||
next to its label.
|
||||
```json
|
||||
"mDMManagedFields": [
|
||||
"disableProfiles",
|
||||
"disableUpdateSettings",
|
||||
"managementURL"
|
||||
]
|
||||
```
|
||||
|
||||
Daemon logs (`/var/log/netbird/client.log` on Linux/macOS,
|
||||
`%ProgramData%\Netbird\` on Windows) contain a one-line
|
||||
`MDM enrolled with N managed key(s): [...]` entry on every reload, plus
|
||||
one `MDM override <key> = <value>` line per applied key. Secrets are
|
||||
redacted.
|
||||
If a key you sent is missing from the array, the policy did not reach the device, or it used a name the client does not recognize.
|
||||
|
||||
`netbird debug config` reads the policy file at the moment you run it, so it shows a change straight away. The daemon applies the change at its next reload, within a minute, and logs it: `MDM policy changed: added=[...] removed=[...] changed=[...]`, followed, while the client is connected, by `MDM policy changed; restarting engine to apply new configuration`. Those lines are the confirmation that the policy is enforced, not just delivered. The client log records the same information on every reload: one `MDM enrolled with N managed key(s): [...]` line, and one `MDM override <key> = <value>` line for each setting it overrode, with secrets redacted. The log is `/var/log/netbird/client.log` on macOS and `C:\ProgramData\Netbird\client.log` on Windows.
|
||||
|
||||
On iOS there is no CLI. See [Verifying on a device](/manage/peers/mdm-deployment/ios-mdm-policy#verifying-on-a-device).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**The policy did not apply at all.**
|
||||
Check that the daemon can see the source.
|
||||
Problems specific to one delivery channel, such as a Group Policy that never reaches the registry or a macOS profile that disappears after a reboot, are covered on the [Windows](/manage/peers/mdm-deployment/windows-mdm-policy#intune-troubleshooting), [macOS](/manage/peers/mdm-deployment/macos-mdm-policy#troubleshooting), and [iOS](/manage/peers/mdm-deployment/ios-mdm-policy#troubleshooting) pages. The problems below look the same on every platform.
|
||||
|
||||
- Windows: `reg query HKLM\Software\Policies\NetBird` — if empty, the
|
||||
delivery channel did not write the values. Check `gpresult /h` for
|
||||
GPO failures or the Intune sync status in **Settings → Accounts →
|
||||
Access work or school → Info → Sync**.
|
||||
- macOS:
|
||||
`sudo defaults read /Library/Managed\ Preferences/io.netbird.client`
|
||||
— if the file is missing, the MDM payload was not pushed or the
|
||||
bundle id in the profile does not match `io.netbird.client`.
|
||||
**`mDMManagedFields` is empty or missing keys.**
|
||||
Start at the source. Confirm the values are in the registry or the managed-preferences file, using the commands on the platform page. If they are, look for `MDM ignoring unknown` in the client log: the value name does not match any key in the [reference](#policy-keys-reference). Names are matched without regard to case, but must otherwise be exact. `WireGuardPort`, for example, matches; `WgPort` does not.
|
||||
|
||||
**The key shows up in the registry / plist but not in `netbird debug
|
||||
config` `mDMManagedFields`.**
|
||||
The value name is misspelled. Names are case-insensitive but must match
|
||||
one of the keys in the reference table above. The daemon log emits an
|
||||
`MDM ignoring unknown <key>` warning when this happens.
|
||||
**A key is listed in `mDMManagedFields` but the setting did not change.**
|
||||
The value is the wrong type or out of range. The key still counts as managed, so users cannot change the setting, but the client keeps its previous value. The client log has a warning such as `MDM management URL "..." invalid` or `MDM wireguard port ... out of range`. On Windows, check that the value has the [registry type](/manage/peers/mdm-deployment/windows-mdm-policy#value-types) the key expects: a `managementURL` stored as a `REG_DWORD`, for example, is ignored. On macOS, check that booleans are `<true/>` or `<false/>`, not `<integer>`; the client logs no warning for those.
|
||||
|
||||
**The user can still change the field from the GUI / CLI.**
|
||||
The change is being rejected by the daemon but the UI may not have
|
||||
caught up yet. The UI refreshes within a couple of seconds after a
|
||||
config change; try closing and reopening the Settings window. If the
|
||||
change actually sticks, double-check that the MDM payload is still
|
||||
present on the device — it may have been removed by another policy.
|
||||
**A user gets `fields managed by MDM cannot be modified: [...]`.**
|
||||
The policy is working: the user, or a script, tried to change a pinned setting. The keys in the brackets are the pinned settings involved. Change those settings through your MDM instead.
|
||||
|
||||
**On macOS, the file disappears after a reboot.**
|
||||
The device is not MDM-enrolled. macOS protects
|
||||
`/Library/Managed Preferences/` by wiping it at boot if no MDM
|
||||
controls the directory. Enroll the device with a real MDM provider for
|
||||
persistent rollouts.
|
||||
**`netbird up --flag` prints `Already connected` and changes nothing.**
|
||||
On a connected client, `netbird up` ignores its flags, whether or not a policy is in place. Run `netbird down` first. On a managed device, the change is then rejected if the policy pins that setting.
|
||||
|
||||
**My MDM provider is not in the list above.**
|
||||
Any MDM that can push a Configuration Profile on macOS or write a
|
||||
registry value on Windows works. The mechanism is OS-native, not
|
||||
NetBird-specific. If you hit a quirk specific to your provider, please
|
||||
open an issue at
|
||||
https://github.com/netbirdio/netbird/issues with the provider name and
|
||||
what you observed.
|
||||
**A user gets `update settings are disabled, you cannot use this feature without update settings enabled`.**
|
||||
`disableUpdateSettings` is on, or the service was installed with `--disable-update-settings`. Every settings change is rejected, including changes to settings no key names. Lift the policy to make the change.
|
||||
|
||||
**A user gets `profiles are disabled, you cannot use this feature without profiles enabled`.**
|
||||
`disableProfiles` is on, or the service was installed with `--disable-profiles`. The device stays on its current profile.
|
||||
|
||||
**A user gets `network selection is disabled by the administrator`.**
|
||||
`disableNetworks` is on, or the service was installed with `--disable-networks`.
|
||||
|
||||
**Lazy connections do not follow the policy.**
|
||||
The `NB_LAZY_CONN` environment variable is set on the device, and it takes precedence over `lazyConnection`. Remove it from the service environment.
|
||||
|
||||
**Devices lost access to network resources after a policy change.**
|
||||
Check for `disableClientRoutes: true` or a `preSharedKey` in the policy. The first stops the device using routes; the second stops it connecting to any peer that does not have the same key. See [Device exposure keys](#device-exposure-keys) and [Encryption keys](#encryption-keys).
|
||||
|
||||
**A network became unreachable for everyone after a policy change.**
|
||||
A routing peer received `blockInbound: true` or `disableServerRoutes: true`, and stopped forwarding. Take routing peers out of the policy's scope.
|
||||
|
||||
**My MDM is not mentioned on these pages.**
|
||||
Any MDM that can write a registry value on Windows, push a configuration profile on macOS, or send an app configuration on iOS works, because the mechanism belongs to the operating system, not to NetBird.
|
||||
|
||||
## When not to use MDM policy
|
||||
|
||||
- **Linux and Android devices.** Neither has an MDM channel in NetBird today. On Linux, set the same options with `netbird up` flags or the service-install flags (`netbird service install --disable-update-settings --disable-profiles --disable-networks`), or [bootstrap the client from a config file](/manage/peers/bootstrap-via-config-file).
|
||||
- **One device, one change.** To change a setting on a single machine you manage by hand, use the app or the CLI. A policy is for settings that must hold across a fleet and must not drift.
|
||||
- **Access control.** A policy configures the client on one device. Which peers can reach which resources belongs in [access policies](/manage/access-control), which apply to every device and do not depend on an MDM.
|
||||
|
||||
## TL;DR
|
||||
|
||||
- **Find the goal, set the key.** [What you can achieve](#what-you-can-achieve) maps each goal to a setting, and [the user device baseline](#the-user-device-baseline) (`managementURL`, `disableUpdateSettings`, `disableProfiles`) is the usual start.
|
||||
- **Present means pinned, even as `false`.** Every key you leave out stays the user's. The client applies the policy last, locks what it pins, re-reads it every minute, and restarts the connection briefly when it changes.
|
||||
- **Keep routing peers out of the wrong policy.** Never send them `blockInbound` or `disableServerRoutes`, and send a `preSharedKey` to every peer or none.
|
||||
- **Verify with `netbird debug config`, then the log.** `mDMManagedFields` shows the policy the client reads; the `MDM policy changed` log line confirms it is enforced.
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
import { Note, Warning } from '@/components/mdx'
|
||||
|
||||
export const description =
|
||||
'Forward local ports into your NetBird network from an unprivileged process, without installing the agent or granting root.'
|
||||
|
||||
# nblink
|
||||
|
||||
`nblink` forwards local ports into your NetBird network from an ordinary,
|
||||
unprivileged process. It runs the client in userspace mode, so there is no TUN
|
||||
device to create and no root to grant.
|
||||
|
||||
That makes it useful where the regular agent cannot run:
|
||||
|
||||
- Managed laptops where you cannot install software that needs administrator rights.
|
||||
- Rootless containers, including OpenShift and Podman.
|
||||
- Short-lived environments such as CI jobs, where installing an agent is not worth it.
|
||||
|
||||
Names on the upstream side of a forward are resolved inside the tunnel, so
|
||||
`/etc/resolv.conf` is never modified.
|
||||
|
||||
<Note>
|
||||
`nblink` forwards HTTP. Support for raw TCP, UDP and SOCKS5 is planned, and
|
||||
the way a forward is written will not change when they arrive.
|
||||
</Note>
|
||||
|
||||
## Use cases
|
||||
|
||||
### An MCP server or local AI agent reaching an internal API
|
||||
|
||||
AI agents increasingly run in containers or restricted sandboxes where
|
||||
installing a client is not an option, and the consumer is code rather than a
|
||||
person, so opening a browser is not a substitute.
|
||||
|
||||
```shell
|
||||
nblink --forward 'http://3000=https://tickets.internal' --state-dir ~/.nblink
|
||||
```
|
||||
|
||||
Point the MCP server at `http://localhost:3000`. The agent carries a real peer
|
||||
identity, so what it can reach is decided by your policies rather than by
|
||||
whatever the agent was told to do.
|
||||
|
||||
### Agent Network access without installing a client
|
||||
|
||||
An [Agent Network](/agent-network) endpoint only answers when you are connected
|
||||
to your network and authenticated through your IdP, which normally means
|
||||
installing the client and therefore having root.
|
||||
|
||||
```shell
|
||||
nblink --forward 'http://8080=https://mirror.netbird.ai'
|
||||
```
|
||||
|
||||
Set the agent's base URL to `http://localhost:8080`. It still gets identity,
|
||||
spending limits and the models you approved, and no API key is handed to the
|
||||
agent itself.
|
||||
|
||||
### A sidecar for an app you cannot modify
|
||||
|
||||
A vendor or legacy application hardcodes an HTTP endpoint and ignores proxy
|
||||
settings, so pointing it somewhere else is not possible. Running `nblink`
|
||||
beside it gives it `localhost:8080` without touching the application, on
|
||||
platforms that forbid privileged containers and assign an arbitrary UID at run
|
||||
time.
|
||||
|
||||
## Install
|
||||
|
||||
Download the archive for your platform from the
|
||||
[releases page](https://github.com/netbirdio/netbird/releases), extract it, and
|
||||
run the binary. There is no installer and no service to register.
|
||||
|
||||
```shell
|
||||
tar -xzf nblink_<version>_linux_amd64.tar.gz
|
||||
./nblink --help
|
||||
```
|
||||
|
||||
## Forward a port
|
||||
|
||||
A forward is a single argument, and the flag repeats:
|
||||
|
||||
```shell
|
||||
nblink --forward 'http://8080=https://grafana.internal'
|
||||
```
|
||||
|
||||
The scheme on the left selects the listener type. The right side is the
|
||||
upstream reached over your network. Giving only a port binds loopback, so the
|
||||
command above listens on `127.0.0.1:8080`.
|
||||
|
||||
With no setup key, `nblink` opens your browser to log in. It always prints the
|
||||
login URL as well, so the same command works over SSH.
|
||||
|
||||
Forward several services from one session and one login:
|
||||
|
||||
```shell
|
||||
nblink --forward 'http://8080=https://grafana.internal' \
|
||||
--forward 'http://9090=https://prometheus.internal'
|
||||
```
|
||||
|
||||
Use `0` as the port to let the operating system pick a free one. The address
|
||||
actually bound is printed when the forward starts.
|
||||
|
||||
Check a configuration without connecting to anything:
|
||||
|
||||
```shell
|
||||
nblink --forward 'http://8080=https://grafana.internal' --check
|
||||
```
|
||||
|
||||
## Run without a browser
|
||||
|
||||
Pass a [setup key](/manage/peers/register-machines-using-setup-keys) to log in
|
||||
without any interaction:
|
||||
|
||||
```shell
|
||||
nblink --forward 'http://8080=https://grafana.internal' --setup-key "$NB_SETUP_KEY"
|
||||
```
|
||||
|
||||
## Run in a container
|
||||
|
||||
Every flag has a matching `NB_`-prefixed environment variable, which is how the
|
||||
image is configured. `NB_FORWARD` accepts a comma separated list.
|
||||
|
||||
```shell
|
||||
docker run --rm -p 127.0.0.1:8080:8080 \
|
||||
--mount type=bind,src="$(pwd)/nb_setup_key",dst=/run/secrets/nb_setup_key,readonly \
|
||||
-e NB_SETUP_KEY=file:/run/secrets/nb_setup_key \
|
||||
-e NB_FORWARD='http://0.0.0.0:8080=https://grafana.internal' \
|
||||
-e NB_ALLOW_PUBLIC_BIND=true \
|
||||
netbirdio/nblink
|
||||
```
|
||||
|
||||
The image needs no `--privileged`, no `--cap-add NET_ADMIN`, no
|
||||
`--device /dev/net/tun` and no host networking. It runs as a non-root user and
|
||||
works on platforms that assign an arbitrary UID at run time.
|
||||
|
||||
Inside a container the listener has to bind `0.0.0.0`, because a published
|
||||
port arrives on the container's own interface rather than its loopback. That
|
||||
still requires `NB_ALLOW_PUBLIC_BIND`, so exposing a forward is always
|
||||
something you wrote down.
|
||||
|
||||
<Warning>
|
||||
Publish the port on the host's loopback, as above. A bare `-p 8080:8080`
|
||||
publishes it on every host interface, and anything that can reach the host
|
||||
can then use the forward under the peer's identity. Publish on another
|
||||
address only when the network around the host restricts who can reach it.
|
||||
</Warning>
|
||||
|
||||
A forward on a public address accepts requests addressed to `localhost` or to
|
||||
an IP address. A name that other machines use to reach it, such as a DNS name
|
||||
or a Kubernetes Service name, has to be listed with `--allowed-host`
|
||||
(`NB_ALLOWED_HOST`); an unlisted name is answered with `421`. See
|
||||
[Access control](#access-control) for what listing a name trusts.
|
||||
|
||||
<Warning>
|
||||
The key file must exist before the run and be readable by the container user,
|
||||
UID 65532 in group 0. A bind mount keeps the host file's owner and mode, so a
|
||||
root-owned key written with the usual `chmod 600` is not readable and startup
|
||||
fails with `permission denied`. Either give the file to that UID
|
||||
(`chown 65532`) or keep it mode `640` in group 0 (`chgrp 0`). If the source
|
||||
path does not exist, `--mount` refuses to start the container; the older `-v`
|
||||
form creates a directory there instead.
|
||||
</Warning>
|
||||
|
||||
The image keeps its state in memory, so it runs on a read-only root filesystem
|
||||
and each start registers a new peer. To keep one identity across restarts, set
|
||||
`NB_STATE_DIR` and mount a writable volume there:
|
||||
|
||||
```shell
|
||||
docker run --rm --read-only -p 127.0.0.1:8080:8080 \
|
||||
-v nblink-state:/var/lib/nblink -e NB_STATE_DIR=/var/lib/nblink \
|
||||
...
|
||||
```
|
||||
|
||||
Without a state directory, use an ephemeral setup key so the peers left behind
|
||||
by earlier starts are removed once they go offline.
|
||||
|
||||
A kept identity is not yet a kept login. Without a setup key every start runs
|
||||
the browser flow, even when the peer's session is still valid. Give a restart
|
||||
that has to be unattended a setup key.
|
||||
|
||||
<Warning>
|
||||
A value given as `file:/path` is read from that file. Prefer it for setup
|
||||
keys in containers, so the secret is not visible to anything that can read
|
||||
the process environment.
|
||||
</Warning>
|
||||
|
||||
## Configuration
|
||||
|
||||
| Flag | Environment variable | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `--forward` | `NB_FORWARD` | | Forward spec, repeatable. Comma separated in the environment. |
|
||||
| `--setup-key` | `NB_SETUP_KEY` | | Log in without a browser. Accepts a `file:` prefix. |
|
||||
| `--management-url` | `NB_MANAGEMENT_URL` | NetBird cloud | Management server URL. |
|
||||
| `--hostname` | `NB_HOSTNAME` | host name | Peer name in your network. |
|
||||
| `--state-dir` | `NB_STATE_DIR` | memory | Directory for identity and state. |
|
||||
| `--log-level` | `NB_LOG_LEVEL` | `info` | Log level. |
|
||||
| `--allow-public-bind` | `NB_ALLOW_PUBLIC_BIND` | `false` | Required to bind an address other than loopback. |
|
||||
| `--allowed-host` | `NB_ALLOWED_HOST` | | Name a request may arrive under, beyond loopback names and addresses. Repeatable. Comma separated in the environment. |
|
||||
| `--no-browser` | `NB_NO_BROWSER` | `false` | Print the login URL instead of opening a browser. |
|
||||
| `--check` | `NB_CHECK` | `false` | Validate the configuration, print the forwards and exit. |
|
||||
|
||||
## Access control
|
||||
|
||||
A peer created by `nblink` is a peer like any other. It appears in your peer
|
||||
list and reaches only what your
|
||||
[access control policies](/manage/access-control) allow.
|
||||
|
||||
`nblink` only dials out. It refuses inbound connections and does not reach the
|
||||
network of the machine it runs on, so the peer cannot be used as a way into
|
||||
that machine.
|
||||
|
||||
A loopback forward answers only requests whose `Host` names the loopback
|
||||
interface or a name listed with `--allowed-host`, and refuses any request a
|
||||
browser marks as belonging to another page, through either a cross-origin
|
||||
`Origin` or a cross-site `Sec-Fetch-Site`.
|
||||
A web page you visit can point its own hostname at `127.0.0.1` or embed the
|
||||
address directly, and without those checks your browser could reach the
|
||||
upstream through the listener under your identity. Ordinary callers send
|
||||
neither header and are unaffected.
|
||||
|
||||
A browser too old to send `Sec-Fetch-Site`, which browsers have sent since
|
||||
2020, can still reach a loopback forward with an embedded no-cors GET.
|
||||
|
||||
A forward on a public address applies the same checks and also accepts any IP
|
||||
address in `Host`, since a page cannot rebind an address it does not control.
|
||||
|
||||
A name listed with `--allowed-host` is trusted rather than merely permitted,
|
||||
and the list applies to every forward in the process, loopback ones included.
|
||||
`Host` is the one signal a rebound page cannot forge, and listing a name gives
|
||||
that up for it: a page served on that name can resolve it to the listener and
|
||||
send a matching `Host` and `Origin`. List only names you control, and prefer a
|
||||
fully qualified one, since a single-label name can be answered by anything on
|
||||
the local network.
|
||||
|
||||
<Warning>
|
||||
Forwards bind `127.0.0.1` unless you pass `--allow-public-bind`. A forward on
|
||||
a public address lets anything that can reach the port use your network, so
|
||||
enable it only when the surrounding environment restricts access.
|
||||
</Warning>
|
||||
|
||||
## Limits
|
||||
|
||||
Userspace mode carries TCP, UDP and ping only. An `nblink` peer cannot:
|
||||
|
||||
- act as a [routing peer](/manage/networks/how-routing-peers-work) or an exit node,
|
||||
- take over system DNS for the machine,
|
||||
- bind privileged ports below 1024.
|
||||
|
||||
If you need any of those, install the
|
||||
[NetBird client](/get-started/install) instead.
|
||||
@@ -18,6 +18,7 @@ The NetBird client allows a peer to join a pre-existing NetBird deployment. If a
|
||||
## Additional Platforms
|
||||
* [Install on Synology](/get-started/install/synology)
|
||||
* [Install on TrueNAS](/get-started/install/truenas)
|
||||
* [Install on Unraid](/get-started/install/unraid)
|
||||
* [Install on pfSense](/get-started/install/pfsense)
|
||||
* [Install on OPNSense](/get-started/install/opnsense)
|
||||
* [Install on OpenWrt](/get-started/install/openwrt)
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import {Note} from "@/components/mdx";
|
||||
|
||||
export const description = 'Install the NetBird plugin on Unraid, register your server with a setup key, and access it remotely over your NetBird network.'
|
||||
|
||||
# Install NetBird on Unraid
|
||||
|
||||
To reach your Unraid server from another location, install the [NetBird plugin](https://github.com/netbirdio/netbird-unraid) and join it to your NetBird network. The plugin runs directly on the Unraid host, making the server a peer (a device connected to NetBird) with its own NetBird IP address.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Unraid **7.0.0 or later**, with access to its web interface.
|
||||
- A [NetBird Cloud account](https://app.netbird.io/) or a [self-hosted NetBird deployment](/selfhosted/selfhosted-quickstart).
|
||||
- A [setup key](/manage/peers/register-machines-using-setup-keys), which registers the server with your NetBird account. Create a one-off key under **Settings → Setup Keys** in the NetBird dashboard. Leave **Ephemeral Peers** disabled for this persistent server.
|
||||
- [NetBird installed](/get-started/install) on the laptop you will use to access Unraid, connected to the same account.
|
||||
|
||||
## Install the plugin
|
||||
|
||||
Choose either the app catalog or the direct plugin URL.
|
||||
|
||||
### From the app catalog
|
||||
|
||||
1. Open **Apps** in the Unraid web interface and search for **NetBird**.
|
||||
2. Select the **NetBird** plugin and click **Install**.
|
||||
3. Wait for installation to finish, then open **Settings → NetBird** and select the plugin's **Settings** tab.
|
||||
|
||||
### With the plugin URL
|
||||
|
||||
1. In the Unraid web interface, open **Plugins → Install Plugin**.
|
||||
2. Paste the following URL into the plugin URL field:
|
||||
|
||||
```text
|
||||
https://raw.githubusercontent.com/netbirdio/netbird-unraid/main/plugin/netbird.plg
|
||||
```
|
||||
|
||||
3. Click **Install** and wait for installation to finish.
|
||||
4. Open **Settings → NetBird**, then select the plugin's **Settings** tab.
|
||||
|
||||
The plugin installs the NetBird client and its service. A fresh installation stays disabled until you configure and enable it in the next step.
|
||||
|
||||
## Connect your server
|
||||
|
||||
1. In the plugin's **Settings** tab, configure these fields:
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| **Enable NetBird** | Select **Yes**. |
|
||||
| **Management URL** | Leave blank for NetBird Cloud. For self-hosted NetBird, enter your management server URL, such as `https://netbird.example.com`. |
|
||||
| **Setup Key** | Paste the setup key you created in the NetBird dashboard. |
|
||||
| **Hostname** | Optional. Enter a name you recognize, or leave blank to use your Unraid server's hostname. |
|
||||
|
||||
2. Leave the other settings at their defaults and click **Apply**. The plugin starts NetBird and registers the server. This plugin uses setup keys for registration, so there is no browser sign-in step.
|
||||
3. Select the **NetBird** tab to view the connection status. Confirm that **Daemon status**, **Management**, and **Signal** show **Connected**, and note the **NetBird IPv4** address.
|
||||
4. Open **Peers** in the [NetBird dashboard](https://app.netbird.io/), or your self-hosted dashboard, and confirm that your Unraid server appears as connected.
|
||||
|
||||
<img src="/docs-static/img/get-started/unraid/netbird-connected.png" alt="NetBird plugin in Unraid showing a running service, its assigned NetBird addresses, and connected management and signal services" className="imagewrapper-big"/>
|
||||
|
||||
The NetBird tab shows the server's connection details. Your addresses and management URL will differ from this example.
|
||||
|
||||
<Note>
|
||||
Changing **Management URL** or **Hostname** after registration creates a new peer identity and IP address. You will need a valid setup key to register it again, and the old peer remains in the NetBird dashboard until you delete it. Choose these values before the first connection.
|
||||
</Note>
|
||||
|
||||
## Access the Unraid web interface
|
||||
|
||||
1. In the NetBird dashboard, place your Unraid peer in a dedicated peer group and configure an [access control policy](/manage/access-control/manage-network-access) allowing your laptop's group to reach it on the TCP port used by the Unraid web interface. Limit the policy to the devices and ports that need access.
|
||||
2. On your connected laptop, open the server's NetBird IP address in a browser, using the protocol and port configured for your Unraid web interface. Use the address without the subnet suffix: for example, `100.64.0.10` if the plugin displays `100.64.0.10/16`.
|
||||
3. Sign in with your normal Unraid credentials. NetBird provides the network connection; Unraid still handles web interface authentication.
|
||||
|
||||
The plugin makes the web interface listen on the NetBird interface. You do not need a network route to reach the Unraid host through its NetBird IP.
|
||||
|
||||
To access other devices on your home network through this server, follow [Access Home Devices](/use-cases/remote-access/access-home-devices) and use the Unraid peer as the routing peer, the device that forwards traffic to those resources.
|
||||
|
||||
## Plugin options and functionality
|
||||
|
||||
### Settings
|
||||
|
||||
Beyond the registration fields, the **Settings** tab provides these options. Click **Apply** after making changes.
|
||||
|
||||
| Option | Default | What it does |
|
||||
| --- | --- | --- |
|
||||
| **Enable NetBird** | **No** on a fresh installation | Enables the service and automatic startup. Set it to **No** and apply to stop NetBird and keep it disabled. |
|
||||
| **Manage DNS** | **Yes** | Lets NetBird configure the host's DNS resolver for peer names and nameservers defined in your NetBird account. Set it to **No** to use Unraid's existing DNS configuration. |
|
||||
| **Enable NetBird SSH** | **No** | Enables NetBird's built-in SSH server, including root login on Unraid. You must also enable SSH for the peer and configure access policies in the NetBird dashboard. Follow the [SSH access guide](/manage/peers/ssh) and limit access to the users who need it. |
|
||||
| **Enable Rosenpass** | **No** | Adds [post-quantum key exchange](/client/post-quantum-cryptography) to WireGuard connections. **Yes** requires the other peer to use Rosenpass. **Yes (permissive)** uses it where supported and allows standard WireGuard connections to other peers. |
|
||||
| **Pre-shared Key** | Empty | Sets an optional additional WireGuard key for the selected profile. Leave blank unless your peers are configured to use one. |
|
||||
| **Log Level** | **Info** | Controls how much detail NetBird writes to `/var/log/netbird.log`. Use **Debug** or **Trace** when investigating a problem, then return to **Info**. |
|
||||
|
||||
DNS, SSH, Rosenpass, log level, and service enablement apply across profiles. Changing DNS, SSH, or Rosenpass settings reconnects NetBird to apply them.
|
||||
|
||||
<Note>
|
||||
A pre-shared key can be set or changed, but clearing the field does not remove an existing key. Removing it requires erasing the profile and setting it up again.
|
||||
</Note>
|
||||
|
||||
### Profiles
|
||||
|
||||
A profile keeps a separate NetBird identity and registration settings. You can use profiles to switch between accounts, such as NetBird Cloud and a self-hosted deployment. Only one profile is active at a time.
|
||||
|
||||
Profile controls are available while NetBird is running:
|
||||
|
||||
1. Enter a unique profile name in the **Settings** tab and click **+ Add**.
|
||||
2. Fill in that profile's **Management URL**, **Setup Key**, and optional **Hostname**. Click **Apply** to save, activate, and connect it.
|
||||
3. To return to a configured profile, choose it from the **Profile** list and click **Switch**.
|
||||
|
||||
Choosing a profile from the list only changes which settings you are viewing. **Switch** activates it; **Apply** saves the selected profile's settings and activates it. Switching disconnects the previous profile. **Delete** removes the selected profile and its stored credentials from this host.
|
||||
|
||||
### Status and service controls
|
||||
|
||||
The **NetBird** tab shows the connection state, assigned addresses, and peer connections. Its buttons let you manage the active connection:
|
||||
|
||||
- **Connect** reconnects an already configured profile.
|
||||
- **Disconnect** takes the profile offline while leaving the service running.
|
||||
- **Restart** restarts the NetBird service.
|
||||
- **Refresh** reloads the status page.
|
||||
|
||||
To keep NetBird stopped across reboots, use **Enable NetBird: No** and **Apply** in **Settings**. Disconnecting alone does not disable automatic startup.
|
||||
|
||||
The Unraid **Dashboard** also includes a NetBird tile for a quick status check. The plugin's **Info** tab lists the plugin and client versions, configuration paths, and log location.
|
||||
|
||||
## Reboots and updates
|
||||
|
||||
Once enabled, the plugin starts NetBird automatically when the Unraid array starts. It stores configuration and peer identity on the USB flash drive, so the server stays registered across reboots:
|
||||
|
||||
- Configuration: `/boot/config/plugins/netbird/etc`
|
||||
- State: `/boot/config/plugins/netbird/lib`
|
||||
|
||||
Install updates from Unraid's **Plugins** page. The plugin manages both its web interface and the bundled NetBird client.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### The server does not connect
|
||||
|
||||
Confirm that **Enable NetBird** is set to **Yes** and that you clicked **Apply**. For a new registration, check that the setup key is valid and that **Management URL** points to the account that issued it. A used one-off key cannot register a new identity.
|
||||
|
||||
Open the Unraid terminal to inspect the connection and recent logs:
|
||||
|
||||
```bash
|
||||
netbird status -d
|
||||
tail -n 100 /var/log/netbird.log
|
||||
```
|
||||
|
||||
After correcting the settings, click **Apply** and check the **NetBird** tab again.
|
||||
|
||||
### Internet names stop resolving on Unraid
|
||||
|
||||
The plugin enables **Manage DNS** by default. If Unraid cannot resolve internet names after connecting, check your [NetBird DNS configuration](/manage/dns) and configure a nameserver group that can resolve those names for the Unraid peer.
|
||||
|
||||
If you want Unraid to keep using its existing DNS configuration, set **Manage DNS** to **No** in the plugin's **Settings** tab and click **Apply**. This disables NetBird's DNS management on the host, so NetBird peer names will need another DNS configuration to resolve. You can still connect using NetBird IP addresses.
|
||||
@@ -10,6 +10,9 @@ The NetBird client allows a peer to join a pre-existing NetBird deployment. If a
|
||||
2. Execute the installer and proceed with the installation steps
|
||||
3. This will install the UI client in the `C:\Program Files\NetBird` and add the daemon service
|
||||
4. After installing, you can follow the steps from [Running NetBird with SSO Login](#running-net-bird-with-sso-login).
|
||||
<Note>
|
||||
Open a new terminal before running `netbird` commands. The installer adds `C:\Program Files\NetBird` to the system `PATH`, but terminals that were already open keep the old `PATH`.
|
||||
</Note>
|
||||
<Note>
|
||||
To uninstall the client and service, you can use Add/Remove programs
|
||||
</Note>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SupportBanner } from "@/components/SupportBanner"
|
||||
|
||||
export const description =
|
||||
"NetBird Support for (paying) Cloud customers and users, and commercial-license self-hosted deployments: reach the team with a pre-filled report."
|
||||
"NetBird Support, mainly for paying Cloud customers and commercial-license self-hosted deployments: reach the team with a pre-filled report."
|
||||
|
||||
export const reportTemplate = `NetBird issue report
|
||||
=====================
|
||||
@@ -39,7 +39,7 @@ export const supportMailto = `mailto:support@netbird.io?subject=${encodeURICompo
|
||||
<SupportBanner
|
||||
tone="support"
|
||||
badge="Cloud & commercial"
|
||||
description="Reserved for (paying) Cloud customers and users, and self-hosted deployments on a commercial license. Use it for the Cloud dashboard, the managed control plane, billing, your subscription, or commercial self-hosted support. Open source self-hosted and general client questions belong in Community Support."
|
||||
description="Mainly for paying NetBird Cloud customers and self-hosted deployments on a commercial license. Use it for the Cloud dashboard, the managed control plane, billing, your subscription, or commercial self-hosted support. Open source self-hosted and general client questions belong in Community Support."
|
||||
links={[
|
||||
{ label: "Open a pre-filled support email", href: supportMailto },
|
||||
{ label: "Attach a debug bundle", href: "/help/troubleshooting-client#debug-bundle" },
|
||||
|
||||
@@ -19,7 +19,7 @@ NetBird offers two ways to get help, depending on what you are running. Pick the
|
||||
{
|
||||
href: "/help/netbird-support",
|
||||
name: "NetBird Support",
|
||||
description: "For (paying) Cloud customers and users, and commercial-license self-hosted deployments: the dashboard, control plane, billing, and subscriptions.",
|
||||
description: "Mainly for paying NetBird Cloud customers and commercial-license self-hosted deployments: the dashboard, control plane, billing, and subscriptions.",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -42,8 +42,8 @@ Each sub-page is maintained alongside its component and updated when support win
|
||||
|
||||
## How to read these tables
|
||||
|
||||
- **Supported** — covered by bug fixes and security updates.
|
||||
- **Best-effort** — known to work; issues triaged but not prioritized.
|
||||
- **Unsupported** — outside the support window; upgrade recommended.
|
||||
- **Supported**: covered by bug fixes and security updates.
|
||||
- **Best-effort**: known to work; issues triaged but not prioritized.
|
||||
- **Unsupported**: outside the support window; upgrade recommended.
|
||||
|
||||
If you need help confirming compatibility for a version not listed, see [Report bugs and issues](/help/report-bug-issues).
|
||||
|
||||
@@ -55,15 +55,16 @@ The NetBird client runs across desktop, mobile, and TV platforms. Pick an OS fam
|
||||
|
||||
## Go toolchain
|
||||
|
||||
The client is built with the Go versions listed below. Building from source against an unsupported Go release is not guaranteed to succeed. Each Go version sets a floor on OS support for that NetBird release — see the per-OS pages above for the resulting cutoffs.
|
||||
The client is built with the Go versions listed below. Building from source against an unsupported Go release is not guaranteed to succeed. Each Go version sets a floor on OS support for that NetBird release. See the per-OS pages above for the resulting cutoffs.
|
||||
|
||||
| NetBird client version | Go version |
|
||||
|---|---|
|
||||
| v0.20.0 – v0.25.3 | 1.20 |
|
||||
| v0.25.4 – v0.29.1 | 1.21 |
|
||||
| v0.29.2 – v0.60.2 | 1.23 |
|
||||
| v0.60.3 – v0.62.3 | 1.24 |
|
||||
| v0.63.0 – current | 1.25 |
|
||||
| v0.20.0 to v0.25.3 | 1.20 |
|
||||
| v0.25.4 to v0.29.1 | 1.21 |
|
||||
| v0.29.2 to v0.60.2 | 1.23 |
|
||||
| v0.60.3 to v0.62.1 | 1.24 |
|
||||
| v0.62.2 to v0.77.1 | 1.25 |
|
||||
| v0.78.0 to current | 1.26 |
|
||||
|
||||
## Reporting compatibility issues
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ NetBird's Linux support depends on the client mode you run:
|
||||
- **Userspace mode** is supported wherever the Go toolchain is supported. The kernel floor for each NetBird release follows Go's minimum OS requirements (see the table below).
|
||||
- **Kernel mode** depends on the iptables / nftables features available on the host distribution.
|
||||
|
||||
The NetBird team's end-to-end tests cover the distributions below — **Ubuntu 20.04** is the oldest tested release:
|
||||
The NetBird team's end-to-end tests cover the distributions below. **Ubuntu 20.04** is the oldest tested release:
|
||||
|
||||
- Ubuntu 20.04, 22.04, 24.04
|
||||
- Debian 12
|
||||
@@ -20,7 +20,7 @@ The NetBird team's end-to-end tests cover the distributions below — **Ubuntu 2
|
||||
Other distributions are not actively tested; their compatibility follows the mode-specific rules above.
|
||||
|
||||
<Warning>
|
||||
The kernel range below is derived from Go's [minimum OS requirements](https://go.dev/wiki/MinimumRequirements) per NetBird release's Go version, and applies to userspace mode. Kernel mode may have stricter requirements depending on the distribution — confirm with the team for production deployments.
|
||||
The kernel range below is derived from Go's [minimum OS requirements](https://go.dev/wiki/MinimumRequirements) per NetBird release's Go version, and applies to userspace mode. Kernel mode may have stricter requirements depending on the distribution, so confirm with the team for production deployments.
|
||||
</Warning>
|
||||
|
||||
## Linux kernel (userspace mode)
|
||||
@@ -28,9 +28,9 @@ Other distributions are not actively tested; their compatibility follows the mod
|
||||
| Kernel range | NetBird release range |
|
||||
|---|---|
|
||||
| 3.2 and newer | v0.60.3 onwards (Go 1.24+) |
|
||||
| 2.6.32 – 3.1 | ≤ v0.60.2 (Go ≤1.23) |
|
||||
| 2.6.32 to 3.1 | ≤ v0.60.2 (Go ≤1.23) |
|
||||
|
||||
The 2.6.32 floor primarily affected RHEL 6, CentOS 6, Ubuntu 14.04, and Debian 8 — all upstream EOL.
|
||||
The 2.6.32 floor primarily affected RHEL 6, CentOS 6, Ubuntu 14.04, and Debian 8, all of which are upstream EOL.
|
||||
|
||||
## Reporting compatibility issues
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export const description =
|
||||
The NetBird client supports the macOS versions listed below. The floor moves whenever NetBird upgrades its Go toolchain and Go drops an older macOS release.
|
||||
|
||||
<Warning>
|
||||
The version cutoffs below are derived from the [Go toolchain's minimum OS requirements](https://go.dev/wiki/MinimumRequirements) for each NetBird release's Go version. NetBird may have stricter requirements than Go — confirm with the team before relying on them.
|
||||
The version cutoffs below are derived from the [Go toolchain's minimum OS requirements](https://go.dev/wiki/MinimumRequirements) for each NetBird release's Go version. NetBird may have stricter requirements than Go, so confirm with the team before relying on them.
|
||||
</Warning>
|
||||
|
||||
## macOS versions
|
||||
@@ -16,8 +16,9 @@ The NetBird client supports the macOS versions listed below. The floor moves whe
|
||||
| Version | Architectures | Version Supported |
|
||||
|---|---|---|
|
||||
| macOS 12 Monterey and newer | x86_64, arm64 | NetBird latest |
|
||||
| macOS 11 Big Sur | x86_64, arm64 | ≤ v0.62.3 |
|
||||
| macOS 11 Big Sur | x86_64, arm64 | ≤ v0.62.1 |
|
||||
| macOS 10.15 Catalina | x86_64 | ≤ v0.29.1 |
|
||||
| macOS 10.13 High Sierra, 10.14 Mojave | x86_64 | ≤ v0.25.3 |
|
||||
|
||||
## Reporting compatibility issues
|
||||
|
||||
|
||||
@@ -5,18 +5,18 @@ export const description =
|
||||
|
||||
# Windows
|
||||
|
||||
Windows 10 and 11 run on the latest NetBird client. Windows 7 and 8 are frozen at NetBird **v0.25.3** — the last release built with Go 1.20, the last Go toolchain to target those Windows versions. Older Windows releases may continue to work on pinned client versions but are not covered by bug fixes or security updates.
|
||||
Windows 10 and 11 run on the latest NetBird client. Windows 7 and 8 are frozen at NetBird **v0.25.3**, the last release built with Go 1.20, the last Go toolchain to target those Windows versions. Older Windows releases may continue to work on pinned client versions but are not covered by bug fixes or security updates.
|
||||
|
||||
<Warning>
|
||||
The version cutoffs below are derived from the [Go toolchain's minimum OS requirements](https://go.dev/wiki/MinimumRequirements) for each NetBird release's Go version. NetBird may have stricter requirements than Go — confirm with the team before relying on them.
|
||||
The version cutoffs below are derived from the [Go toolchain's minimum OS requirements](https://go.dev/wiki/MinimumRequirements) for each NetBird release's Go version. NetBird may have stricter requirements than Go, so confirm with the team before relying on them.
|
||||
</Warning>
|
||||
|
||||
## Windows versions
|
||||
|
||||
| Version | Architectures | Version Supported |
|
||||
|---|---|---|
|
||||
| Windows 11 | x86_64, arm64 | NetBird latest |
|
||||
| Windows 10 / Server 2016 and newer | x86_64, arm64 | NetBird latest |
|
||||
| Windows 11 | x86_64, arm64 (since v0.54.0) | NetBird latest |
|
||||
| Windows 10 / Server 2016 and newer | x86_64, arm64 (since v0.54.0) | NetBird latest |
|
||||
| Windows 8 / Server 2012 / 2012 R2 | all | ≤ v0.25.3 |
|
||||
| Windows 7 / Server 2008 R2 | all | ≤ v0.25.3 |
|
||||
|
||||
|
||||
@@ -1,24 +1,86 @@
|
||||
import {Note} from "@/components/mdx";
|
||||
|
||||
export const description = "Why your NetBird user might be pending approval, or added to an account you don't recognize, and how to resolve it safely."
|
||||
export const description = "Why NetBird shows you a Waiting for Approval screen, adds you to an account you don't recognize, asks you to approve yourself, or gives a teammate an account of their own, and how to resolve it safely."
|
||||
|
||||
# Pending approval and account invitations
|
||||
|
||||
Signed in and found your user **pending approval**, or landed in a NetBird account you don't recognize? This usually is not a bug. It almost always means you were added to an existing organization automatically, and an admin still has to approve you.
|
||||
Signed in to NetBird and landed on a **Waiting for Approval** screen, or in a NetBird account you don't recognize? This usually is not a bug. It almost always means you were added to an existing organization automatically, and someone in that organization still has to approve you.
|
||||
|
||||
The opposite also happens: a teammate signs in and gets a new, empty account of their own instead of joining yours. See [A teammate got their own account](#a-teammate-got-their-own-account).
|
||||
|
||||
If the screen asks you to wait for approval from your own email address, see [The screen names your own address](#the-screen-names-your-own-address).
|
||||
|
||||
## What the Waiting for Approval screen tells you
|
||||
|
||||
After you sign in, the dashboard shows three steps: **Account Created**, **Waiting for Approval**, and **Join Account**. Your user exists, but the account requires new users to be approved before they can use it, so you are held at the second step until someone approves you.
|
||||
|
||||
Below the steps, the screen names the owner of the account you are waiting to join, for example `li****g@example.com`. Most of the part before the `@` is hidden to protect the owner's privacy, but the domain is shown in full. That domain is the quickest way to tell which organization you are waiting on.
|
||||
|
||||
If the screen only says "Ask the owner of the account to approve your access", with no address, NetBird could not look up the owner's email. Everything below still applies.
|
||||
|
||||
The screen has two buttons:
|
||||
|
||||
- **Refresh** checks again whether you have been approved. Use it once someone tells you they approved you.
|
||||
- **Log Out** signs you out. Use it if you signed in with the wrong account, or you don't want to join this one.
|
||||
|
||||
## Why this happens
|
||||
|
||||
NetBird can add people to an account without a manual, one-by-one invite:
|
||||
|
||||
- **Indirect (domain) invites.** If an account has verified your email domain, new sign-ups with that domain join it automatically. See [Indirect user invites](/manage/team/add-users-to-your-network#indirect-user-invites).
|
||||
- **Domain matching.** On NetBird Cloud, new sign-ups join an existing account automatically when their email domain belongs to it: either the private domain the account signed up with, or another domain the account has verified as a [sign-in domain](/manage/team/sign-in-domains). See [Indirect user invites](/manage/team/add-users-to-your-network#indirect-user-invites).
|
||||
- **Identity provider sync.** If your organization connects NetBird to its identity provider, being added to a synced group in the IdP provisions your user in NetBird. See [Provision users and groups from your identity provider](/manage/team/idp-sync).
|
||||
- **Approval is required.** When an account turns on [user approval](/manage/team/approve-users#require-user-approval), new users stay blocked until an admin approves them. That block is the "pending approval" state you see.
|
||||
- **Approval is required.** When an account turns on [user approval](/manage/team/approve-users#require-user-approval), new users stay blocked until an admin approves them. That block is the Waiting for Approval screen you see.
|
||||
|
||||
## What to do
|
||||
|
||||
1. **Confirm it is your organization.** Check with your IT or security team whether they run NetBird and expect you on it. The account [Owner](/manage/team/user-roles#owner), of which there is exactly one per account, or an admin can confirm and approve you.
|
||||
2. **Ask an admin to approve your user.** Approval happens in the dashboard from the account's user list. See [Approve or reject user](/manage/team/approve-users#approve-or-reject-user).
|
||||
3. **If something looks off,** for example you don't recognize the account, your domain was claimed unexpectedly, or no one internally owns it, do not approve or accept anything. Treat it as suspicious and verify internally first.
|
||||
1. **Check the domain on the screen.** It is normally your organization's email domain. Confirm with your IT or security team that they run NetBird and expect you on it. If the address looks like your own, see [The screen names your own address](#the-screen-names-your-own-address) instead.
|
||||
2. **Ask the owner or an admin to approve you.** The screen names the account [Owner](/manage/team/user-roles#owner), of which there is exactly one per account, but any admin can approve you as well. They do it from the dashboard's user list. See [Approve or reject user](/manage/team/approve-users#approve-or-reject-user).
|
||||
3. **Click Refresh once you are approved.** The screen moves you into the account. If it still shows Waiting for Approval, your browser may be holding on to a cached session: open the dashboard in a private browser window and sign in again.
|
||||
4. **If something looks off,** for example the domain is not your organization's, you don't recognize the account, or no one internally owns it, do not ask anyone to approve you. Click **Log Out**, treat it as suspicious, and verify internally first.
|
||||
|
||||
<Note>
|
||||
On self-hosted NetBird, the Waiting for Approval screen needs dashboard v2.93.0 or later, and the owner's address on it needs management v0.80.0 or later. Older dashboards show a **User Approval Pending** error page with only a **Sign Out** button instead: the steps above still apply, but after you are approved, sign out and sign in again.
|
||||
</Note>
|
||||
|
||||
## The screen names your own address
|
||||
|
||||
You signed in as `lisa.wong@example.com`, and the Waiting for Approval screen says you are waiting on `li****g@example.com`. The Owner you are waiting on is you.
|
||||
|
||||
On NetBird Cloud, this happens when you created the account with one sign-in method, for example **Google** or **Microsoft**, and later signed in with a different one, for example an email address and password. NetBird tells users apart by how they sign in, not by their email address, so the second sign-in created a second user with the same email. Its domain, `example.com`, already belongs to the account you own, so [domain matching](#why-this-happens) added that second user to your account, where it waits for approval from the Owner: you, under your original sign-in method.
|
||||
|
||||
The two users are not linked, and approving the second one does not merge them. It only gives you a second user in your account with the User role, not Owner. To get back in as Owner:
|
||||
|
||||
1. **Click Log Out.**
|
||||
2. **Sign in with the method you used to create the account.** If you don't remember which one, try each option on the sign-in page with the same email address. The right one opens your account as its Owner, with your peers and settings.
|
||||
3. **Reject the extra user.** In **Team → Users**, the second user shows your email address and is waiting for approval. Click **Reject** to remove it. See [Approve or reject user](/manage/team/approve-users#approve-or-reject-user).
|
||||
|
||||
From then on, sign in with your original method only. If you use the other method again, NetBird creates the second user again and you are back on this screen.
|
||||
|
||||
If your account does not [require user approval](/manage/team/approve-users#require-user-approval), you don't see this screen. The second sign-in joins your account straight away with the User role, so you are in your own account but can't manage it. Log out and sign in with your original method as in steps 1 and 2. The extra user is not pending, so you can't reject it: in **Team → Users**, open the actions menu on its row and click **Delete** instead.
|
||||
|
||||
If you signed up with a public email address, such as a Gmail account, domain matching never applies. The second sign-in lands in a new, empty account of its own instead. Sign out and sign in with your original method to get back to your account. If you don't need the empty account, sign in to it once more and [delete it](/manage/settings/delete-account).
|
||||
|
||||
## A teammate got their own account
|
||||
|
||||
A teammate signs in to NetBird Cloud and lands in a new, empty account, with none of your peers, users, or policies. NetBird did not match their email domain to your account, so it created a separate one for them. One of these is usually the reason:
|
||||
|
||||
- **They use another of your business's domains.** Only the domain your account signed up with matches from the start. A country domain such as `company.co.uk`, a subsidiary, or a second brand matches only after an Owner or Admin adds and verifies it as a [sign-in domain](/manage/team/sign-in-domains).
|
||||
- **They signed in with a personal address**, such as a Gmail account. Public email domains never match an account. Ask them to sign in with their work address instead. You can't [invite them directly](/manage/team/add-users-to-your-network#direct-user-invites) now that they have their own account, so if they need to keep the personal address, contact [NetBird Support](/help/netbird-support).
|
||||
- **Your domain was not classified as private.** NetBird only groups sign-ups by domain when it classifies the domain as private. If it is unsure, it plays safe and does not group them. [NetBird Support](/help/netbird-support) can correct the classification. See [Indirect user invites](/manage/team/add-users-to-your-network#indirect-user-invites).
|
||||
|
||||
To fix it for the next person, an Owner or Admin adds and verifies the domain under **Settings → Authentication → Sign-in Domains**, or invites the user directly. Do this before onboarding a team on another domain, not after.
|
||||
|
||||
The **Sign-in Domains** section only appears once your account's own domain is classified as private. If you don't see it, invite the user directly or contact [NetBird Support](/help/netbird-support).
|
||||
|
||||
To confirm the cause, ask your teammate which email address they signed in with, and check its domain against the Sign-in Domains list.
|
||||
|
||||
Verifying a domain does not move a teammate who already has an account of their own: it only changes where new sign-ins land, and inviting or syncing them fails until they leave that account. For a teammate already in a separate account, see [User already belongs to another NetBird account](/help/troubleshooting-user-in-another-account).
|
||||
|
||||
If your account requires user approval, a teammate who joins through domain matching is held for approval. That is the [Waiting for Approval](#what-the-waiting-for-approval-screen-tells-you) screen above, and an admin approves them as described in [What to do](#what-to-do).
|
||||
|
||||
<Note>
|
||||
On self-hosted NetBird, domain matching and sign-in domains do not apply: by default, every user joins the same account.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
Still unsure who owns the account, or think you were added to the wrong one? Reach out through [NetBird Support](/help/netbird-support) and the team can help confirm the account and its owner.
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import {Warning} from "@/components/mdx";
|
||||
|
||||
export const description = "Why NetBird refuses to invite or sync a user who already has a NetBird account elsewhere, how to move them into your account, and how Sign-in Domains stops it happening again."
|
||||
|
||||
# User Already Belongs to Another NetBird Account
|
||||
|
||||
You try to add someone to your NetBird account and it doesn't work: the invite fails with a `409` error, or Identity Provider Sync never adds them, even though they are in the right group in your identity provider (IdP).
|
||||
|
||||
In almost every case, the person already signed in to NetBird before you added them, and that sign-in put them in a **different NetBird account**. A NetBird user belongs to exactly one account, so NetBird won't copy or move them into yours. You have to free the user from the other account first.
|
||||
|
||||
This page is for NetBird Cloud, where many organizations share one service. On self-hosted NetBird, see [Self-hosted](#self-hosted) at the end.
|
||||
|
||||
## Symptoms
|
||||
|
||||
You see one of these:
|
||||
|
||||
- **Invite fails with 409.** Inviting the user from **Team → Users → Invite User** shows `Code 409: Can't invite a user with an existing NetBird account`.
|
||||
- **IdP sync skips the user.** The user is assigned to NetBird in your IdP, other users sync fine, but this one never appears under **Team → Users**. Your IdP's provisioning log may show the user as provisioned successfully, because NetBird accepts the record first and adds users to your account in a later step. That later step is where it fails, and the integration's sync log records:
|
||||
|
||||
```text
|
||||
failed to sync new users: failed to process update for user <user-id>: user account ID mismatch
|
||||
```
|
||||
|
||||
The dashboard shows only when the integration last synced, not the log itself. To read the log, call the sync logs endpoint for your integration in the API: [Entra ID (API)](/ipa/resources/idp-azure-integrations#get-azure-integration-sync-logs), [Google Workspace](/ipa/resources/idp-google-integrations#get-google-integration-sync-logs), [Okta](/ipa/resources/idp-okta-scim-integrations#get-okta-scim-integration-sync-logs), or [the SCIM-based integrations](/ipa/resources/idp-scim-integrations#get-scim-integration-sync-logs).
|
||||
|
||||
Both errors usually mean the same thing: NetBird already knows this person, and they belong to another account. Rule out the simpler case below first.
|
||||
|
||||
## Check your own account first
|
||||
|
||||
An invite also fails with `409` when the person is already a user in **your** account. For example, they signed in on their own and joined through their email domain, or they are waiting for approval. Look for their email address under **Team → Users**, including users waiting for approval. If they are there, don't invite them again: [approve them](/manage/team/approve-users#approve-or-reject-user), or change their role if they need dashboard access.
|
||||
|
||||
## Why the user ended up in another account
|
||||
|
||||
When someone signs in to NetBird for the first time, NetBird decides which account they join from their email domain. They join your account only if the domain is one your account owns. Otherwise NetBird creates a new account with them as its Owner. Ordered by how often we see them:
|
||||
|
||||
1. **Their email domain isn't your signup domain.** You signed up as `admin@company.com`, and the user signed in as `jane@company.co.uk`, a subsidiary, or a second brand. NetBird doesn't know `company.co.uk` belongs to you, so Jane got an account of her own. This is the case [Sign-in Domains](/manage/team/sign-in-domains) solves.
|
||||
2. **They signed in before your rollout.** They tried NetBird early, or opened the dashboard link before you set up IdP sync or invited them. If their domain was one you hadn't added yet, they landed in an account of their own, as in case 1.
|
||||
3. **They are a member of another organization's account.** For example, a partner or customer account, a former employer's account, or a test account someone created.
|
||||
4. **Their domain isn't recognized as a business domain.** NetBird treats public email providers like Gmail as personal, and every user on them gets their own account. Rarely, a company domain is classified this way too. [NetBird Support](/help/netbird-support) can correct the classification.
|
||||
|
||||
## Fix it
|
||||
|
||||
### 1. Find out which account the user is in
|
||||
|
||||
Ask the user to sign in at [app.netbird.io](https://app.netbird.io) with the same email address you are adding, and look at what they see:
|
||||
|
||||
- **A network with only their own devices, and the Settings → Danger zone tab is available, not greyed out.** Only an Owner can open it, so they are the Owner of an account of their own (cases 1, 2, or 4). Go to [step 2a](#2a-the-user-owns-an-account-of-their-own).
|
||||
- **Someone else's network, or a Waiting for Approval screen naming another organization.** They are a member of another account (case 3). Go to [step 2b](#2b-the-user-is-a-member-of-another-organizations-account).
|
||||
- **A Waiting for Approval screen naming your own domain.** They are already in your account. See [Check your own account first](#check-your-own-account-first).
|
||||
|
||||
If neither fits, or the user doesn't know where the account came from, contact [NetBird Support](/help/netbird-support). The team verifies the user's identity and points them to whoever administers that account.
|
||||
|
||||
### 2a. The user owns an account of their own
|
||||
|
||||
<Warning>
|
||||
Deleting an account permanently deletes everything in it: its peers, groups, policies, setup keys, and any other users. Before the user deletes it, confirm it holds nothing they still need, and that no one else relies on it.
|
||||
</Warning>
|
||||
|
||||
1. **If their domain differs from yours, add it as a sign-in domain first.** In your account, go to **Settings → Authentication → Sign-in Domains** and add and verify their domain, for example `company.co.uk`. See [Sign-in Domains](/manage/team/sign-in-domains). Skip this step if they share your domain.
|
||||
2. **Have the user delete their account.** In their account, they go to **Settings → Danger zone** and click **Delete Account**. See [Delete your account](/manage/settings/delete-account).
|
||||
3. **Bring them into your account.** If you verified their domain in step 1, or they share your domain, their next sign-in joins your account, and IdP sync attaches their groups on its next run. If you don't use either, invite them again or run the sync. If your account requires [user approval](/manage/team/approve-users#approve-or-reject-user), approve them under **Team → Users**.
|
||||
|
||||
Doing step 1 before step 2 matters. Without a matching domain, a user who signs in again before your next sync gets a fresh account of their own, and you are back where you started.
|
||||
|
||||
If verification in step 1 fails, the user's own account may already hold that domain. Only one account can verify a domain, so do step 2 first, then verify the domain right away. Ask the user not to sign in again until the domain shows as verified.
|
||||
|
||||
### 2b. The user is a member of another organization's account
|
||||
|
||||
Only that account's admins can release the user. Ask them to delete the user from **Team → Users** in their account. Removing a user also removes the peers registered under them in that account.
|
||||
|
||||
Once the user is removed, invite them again, or let IdP sync pick them up on its next run.
|
||||
|
||||
### 3. Run the sync or the invite again
|
||||
|
||||
- **Direct invite.** Invite the user again from **Team → Users**.
|
||||
- **Entra ID (API) and Google Workspace.** Open the integration under **Integrations → Identity Provider Sync** and force a synchronization, or wait for the next scheduled one.
|
||||
- **SCIM integrations** (Entra ID (SCIM), Okta, JumpCloud, Keycloak, IIJ ID, generic SCIM). Your IdP pushes changes on its own schedule. Trigger provisioning for the user in your IdP, for example **Provision on demand** in Entra ID, or wait for the next cycle.
|
||||
|
||||
## Confirm it worked
|
||||
|
||||
The user appears under **Team → Users** in your account, with the groups from your IdP, and the next sync log no longer reports `user account ID mismatch` for them. When the user signs in at [app.netbird.io](https://app.netbird.io), they see your network, or a Waiting for Approval screen naming your own domain if your account requires user approval.
|
||||
|
||||
If the invite still fails with `409`, or the user can't sign in after their old account is gone, contact [NetBird Support](/help/netbird-support) with their email address.
|
||||
|
||||
## Prevent it
|
||||
|
||||
Most of these conflicts come from case 1: an organization uses more than one email domain and only the signup domain is known to NetBird.
|
||||
|
||||
- **Add every domain your organization uses as a [sign-in domain](/manage/team/sign-in-domains), before you roll NetBird out.** Sign-in domains decide where new users land, so a verified domain sends every first sign-in on it to your account. They don't move users who already have an account elsewhere, which is why adding them first matters.
|
||||
- **Set up IdP sync and sign-in domains before you announce NetBird to users.** Anyone who signs in early, on a domain you haven't added yet, gets an account of their own.
|
||||
|
||||
## Self-hosted
|
||||
|
||||
A self-hosted NetBird deployment runs one account by default, and every user joins it. A user can't be stuck in another account, so `user account ID mismatch` doesn't apply.
|
||||
|
||||
With the embedded identity provider, a `409` on invite means the email address already belongs to a user in your own account. Look for them under **Team → Users** instead of inviting them again.
|
||||
|
||||
## Recap
|
||||
|
||||
- One NetBird user belongs to exactly one account. A `409` on invite, or `user account ID mismatch` in the sync log, usually means the user is already in another one. Check **Team → Users** in your own account first.
|
||||
- Usually they signed in early, with an email domain your account didn't know yet.
|
||||
- Free the user first: they delete their own account, or the other account's admins remove them. Then invite or sync again.
|
||||
- Add and verify every domain you use as a sign-in domain before rollout, so first sign-ins land in your account.
|
||||
|
||||
## Related docs
|
||||
|
||||
- [Allow users from other domains to join your account](/manage/team/sign-in-domains)
|
||||
- [Add users to your network](/manage/team/add-users-to-your-network)
|
||||
- [Provision users and groups from your identity provider](/manage/team/idp-sync)
|
||||
- [Pending approval and account invitations](/help/troubleshooting-account-access)
|
||||
- [Delete your account](/manage/settings/delete-account)
|
||||
@@ -2,43 +2,91 @@ import { TroubleshootingTiles } from "@/components/TroubleshootingTiles"
|
||||
import { StillStuck } from "@/components/StillStuck"
|
||||
|
||||
export const description =
|
||||
"Start here to troubleshoot NetBird. Find your issue by area and jump straight to the relevant guide."
|
||||
"Start here to troubleshoot NetBird. See the common issues, or browse by area."
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
Something not working as expected? Pick the area closest to your problem and jump to the
|
||||
guide and section that covers it. Each section links to troubleshooting docs that live
|
||||
next to the feature they cover, so nothing here is a copy.
|
||||
Something not working as expected? Check the common issues below, or browse by area.
|
||||
|
||||
## Common issues
|
||||
|
||||
The problems support sees most often.
|
||||
|
||||
<div className="xl:max-w-none [&_table]:table-fixed [&_th]:w-1/2 [&_th:last-child]:pl-8 [&_td:last-child]:pl-8">
|
||||
|
||||
| Symptom | Start here |
|
||||
|---|---|
|
||||
| The dashboard shows **Waiting for Approval** after you sign in | [Pending approval and account invitations](/help/troubleshooting-account-access) |
|
||||
| Inviting a user fails with `409`, or IdP sync never adds them | [User already belongs to another NetBird account](/help/troubleshooting-user-in-another-account) |
|
||||
| The client keeps asking you to log in again, or a peer shows `Login required` | [Peer session expiration](/manage/settings/enforce-periodic-user-authentication) |
|
||||
| A peer shows `Relayed` instead of `P2P` | [Troubleshooting relayed connections](/help/troubleshooting-relayed-connections) |
|
||||
| The tunnel works but is slow, or takes long to connect | [Troubleshooting performance](/help/troubleshooting-performance) |
|
||||
| A service behind a routing peer is unreachable, or a new resource never shows up | [Troubleshooting resource connectivity](/help/troubleshooting-resource-connectivity) |
|
||||
| A network or host firewall blocks NetBird, or traffic stops at a security product such as a SASE or SSL-inspection service | [Ports & firewalls](/about-netbird/ports-and-firewalls) <br /> On the peer itself: [Symptoms of host-based firewall issues](/about-netbird/ports-and-firewalls#symptoms-of-host-based-firewall-issues) |
|
||||
| Names do not resolve, or DNS stops working after NetBird disconnects | [DNS troubleshooting](/manage/dns/troubleshooting) |
|
||||
| A NetBird Reverse Proxy service returns `502`, times out, or its certificate is never issued | [Reverse proxy troubleshooting](/manage/reverse-proxy/troubleshooting) <br /> Certificate: [Certificate is not issued after verification](/manage/reverse-proxy/custom-domains#certificate-is-not-issued-after-verification) |
|
||||
|
||||
</div>
|
||||
|
||||
<TroubleshootingTiles
|
||||
title="Find your issue by area"
|
||||
id="find-your-issue-by-area"
|
||||
description="Pick the surface you are working with. Links open the existing troubleshooting docs in place."
|
||||
description="Pick the part of NetBird you are working with, then the problem closest to yours."
|
||||
items={[
|
||||
{
|
||||
title: "NetBird Client",
|
||||
href: "/help/troubleshooting-client",
|
||||
icon: "laptop",
|
||||
description:
|
||||
"Desktop, CLI, and mobile client problems on a peer machine.",
|
||||
"Desktop, CLI, and mobile client problems on a peer machine: status, logs, login, and platform quirks.",
|
||||
chips: [
|
||||
{ label: "Connection issues", href: "/help/troubleshooting-client#debugging-ice-connections" },
|
||||
{ label: "Client status", href: "/help/troubleshooting-client#net-bird-client-status" },
|
||||
{ label: "Debug bundle", href: "/help/troubleshooting-client#debug-bundle" },
|
||||
{ label: "Debug logs", href: "/help/troubleshooting-client#enabling-debug-logs-on-the-client" },
|
||||
{ label: "Login & SSO", href: "/help/troubleshooting-client#client-login-failures" },
|
||||
{ label: "DNS resolution", href: "/manage/dns/troubleshooting" },
|
||||
{ label: "Routes & exit nodes", href: "/help/troubleshooting-client#debugging-access-to-network-resources" },
|
||||
{ label: "Windows SSO port 53000", href: "/help/troubleshooting-client/windows#sso-login-fails-to-bind-tcp-53000" },
|
||||
{ label: "Stuck at Connecting", href: "/help/troubleshooting-client#debugging-ice-connections" },
|
||||
{ label: "Host firewall fallback", href: "/help/troubleshooting-client#when-the-client-cannot-manage-firewall-rules" },
|
||||
{ label: "Linux", href: "/help/troubleshooting-client/linux" },
|
||||
{ label: "Windows", href: "/help/troubleshooting-client/windows" },
|
||||
{ label: "macOS", href: "/help/troubleshooting-client/macos" },
|
||||
{ label: "Android", href: "/help/troubleshooting-client/android" },
|
||||
{ label: "iOS", href: "/help/troubleshooting-client/ios" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Self-hosted",
|
||||
href: "/selfhosted/troubleshooting",
|
||||
icon: "server",
|
||||
// No dedicated troubleshooting page, so the title is plain text.
|
||||
title: "Connectivity & networking",
|
||||
icon: "firewall",
|
||||
description:
|
||||
"Your own Management, Signal, Relay, and Dashboard services.",
|
||||
"Peers that relay, never connect, or can't reach resources, plus DNS and speed, whether you use NetBird Cloud or self-host.",
|
||||
chips: [
|
||||
{ label: "Management service", href: "/selfhosted/troubleshooting/connectivity#management-service-unreachable" },
|
||||
{ label: "Signal service", href: "/about-netbird/how-netbird-works#signal-service" },
|
||||
{ label: "Relay / TURN", href: "/selfhosted/troubleshooting/connectivity#debugging-turn-connections" },
|
||||
{ label: "Dashboard", href: "/selfhosted/troubleshooting/dashboard" },
|
||||
{ label: "Relayed connections", href: "/help/troubleshooting-relayed-connections" },
|
||||
{ label: "Control plane unreachable", href: "/help/troubleshooting-relayed-connections#step-2-is-the-control-plane-reachable" },
|
||||
{ label: "Resource connectivity", href: "/help/troubleshooting-resource-connectivity" },
|
||||
{ label: "Routing peer forwarding", href: "/help/troubleshooting-resource-connectivity#step-3-does-the-peer-forward-it" },
|
||||
{ label: "Routing peer checks", href: "/manage/networks/how-routing-peers-work#observability-and-troubleshooting" },
|
||||
{ label: "Performance", href: "/help/troubleshooting-performance" },
|
||||
{ label: "Reconnect delays", href: "/help/troubleshooting-performance#startup-and-reconnect-delays" },
|
||||
{ label: "MTU", href: "/help/troubleshooting-performance#rule-out-packet-size-mtu" },
|
||||
{ label: "DNS", href: "/manage/dns/troubleshooting" },
|
||||
{ label: "Ports & firewalls", href: "/about-netbird/ports-and-firewalls" },
|
||||
{ label: "Corporate firewalls & SASE", href: "/about-netbird/ports-and-firewalls#corporate-firewalls" },
|
||||
{ label: "Other VPNs on Linux", href: "/client/environment-variables#networking-and-routing" },
|
||||
],
|
||||
},
|
||||
{
|
||||
// No dedicated troubleshooting page, so the title is plain text;
|
||||
// chips point to the sections that diagnose policy decisions.
|
||||
title: "Access control",
|
||||
icon: "shield",
|
||||
description:
|
||||
"Traffic allowed or denied unexpectedly by a policy or posture check.",
|
||||
chips: [
|
||||
{ label: "Check the matching policy", href: "/help/troubleshooting-resource-connectivity#step-2-is-there-a-route-and-a-matching-policy" },
|
||||
{ label: "Trace firewall rules", href: "/help/troubleshooting-client#tracing-firewall-rules" },
|
||||
{ label: "When posture checks run", href: "/manage/access-control/posture-checks#when-posture-checks-are-evaluated" },
|
||||
{ label: "Posture check limits", href: "/manage/access-control/posture-checks#known-limitations" },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -47,41 +95,49 @@ next to the feature they cover, so nothing here is a copy.
|
||||
title: "Cloud & identity",
|
||||
icon: "cloud",
|
||||
description:
|
||||
"SSO and user provisioning (Cloud and self-hosted), account approvals, and Cloud plans and quotas.",
|
||||
"Approvals, landing in the right account, session expiry, SSO and provisioning, MSP tenants, and NetBird Cloud plans.",
|
||||
chips: [
|
||||
{ label: "Pending approval", href: "/help/troubleshooting-account-access" },
|
||||
{ label: "IdP & SSO setup", href: "/manage/team/single-sign-on" },
|
||||
{ label: "User provisioning", href: "/manage/team/idp-sync" },
|
||||
{ label: "Plan limits & quotas", href: "/manage/settings/plans-and-billing" },
|
||||
{ label: "Waiting on your own approval", href: "/help/troubleshooting-account-access#the-screen-names-your-own-address" },
|
||||
{ label: "Teammate got their own account", href: "/help/troubleshooting-account-access#a-teammate-got-their-own-account" },
|
||||
{ label: "Can't invite or sync a user", href: "/help/troubleshooting-user-in-another-account" },
|
||||
{ label: "Sign-in domains", href: "/manage/team/sign-in-domains" },
|
||||
{ label: "Session expiration", href: "/manage/settings/enforce-periodic-user-authentication" },
|
||||
{ label: "SSO setup guides", href: "/manage/team/single-sign-on" },
|
||||
{ label: "User provisioning setup", href: "/manage/team/idp-sync" },
|
||||
{ label: "MSP tenants", href: "/manage/for-partners/msp-portal#delayed-tenant-domain-verification" },
|
||||
{ label: "Plans & billing", href: "/manage/settings/plans-and-billing" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Connectivity & networking",
|
||||
href: "/help/troubleshooting-relayed-connections",
|
||||
icon: "firewall",
|
||||
title: "Reverse Proxy",
|
||||
href: "/manage/reverse-proxy/troubleshooting",
|
||||
icon: "globe",
|
||||
description:
|
||||
"Cross-cutting network issues regardless of how you deploy.",
|
||||
"Services you expose with the NetBird Reverse Proxy: errors, timeouts, and custom domains.",
|
||||
chips: [
|
||||
{ label: "Relayed connections", href: "/help/troubleshooting-relayed-connections" },
|
||||
{ label: "Resource connectivity", href: "/help/troubleshooting-resource-connectivity" },
|
||||
{ label: "Performance", href: "/help/troubleshooting-performance" },
|
||||
{ label: "Reverse proxy", href: "/manage/reverse-proxy/troubleshooting" },
|
||||
{ label: "NAT & firewall ports", href: "/about-netbird/ports-and-firewalls" },
|
||||
{ label: "Corporate firewalls", href: "/about-netbird/ports-and-firewalls#corporate-firewalls" },
|
||||
{ label: "DNS", href: "/manage/dns/troubleshooting" },
|
||||
{ label: "Network routes", href: "/manage/network-routes" },
|
||||
{ label: "Quick checklist", href: "/manage/reverse-proxy/troubleshooting#quick-diagnostics-checklist" },
|
||||
{ label: "502 to the routing peer's own IP", href: "/manage/reverse-proxy/troubleshooting#issue-1-502-errors-when-routing-peer-forwards-to-its-own-ip" },
|
||||
{ label: "Service on localhost", href: "/manage/reverse-proxy/troubleshooting#issue-2-service-bound-to-localhost-is-unreachable" },
|
||||
{ label: "TCP service gets no response", href: "/manage/reverse-proxy/troubleshooting#issue-4-tcp-service-is-active-but-connections-get-no-response" },
|
||||
{ label: "Domain Pending Verification", href: "/manage/reverse-proxy/custom-domains#domain-shows-as-pending-verification" },
|
||||
{ label: "Certificate not issued", href: "/manage/reverse-proxy/custom-domains#certificate-is-not-issued-after-verification" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Access control",
|
||||
href: "/manage/access-control",
|
||||
icon: "shield",
|
||||
title: "Self-hosted",
|
||||
href: "/selfhosted/troubleshooting",
|
||||
icon: "server",
|
||||
description:
|
||||
"Traffic being allowed or denied unexpectedly.",
|
||||
"Your own NetBird server: installation, identity, dashboard, certificates, connectivity, and database.",
|
||||
chips: [
|
||||
{ label: "Policies", href: "/manage/access-control" },
|
||||
{ label: "Posture checks", href: "/manage/access-control/posture-checks" },
|
||||
{ label: "Groups", href: "/manage/access-control#understanding-groups" },
|
||||
{ label: "Installation", href: "/selfhosted/troubleshooting/installation" },
|
||||
{ label: "Embedded IdP", href: "/selfhosted/troubleshooting/identity-provider" },
|
||||
{ label: "Dashboard", href: "/selfhosted/troubleshooting/dashboard" },
|
||||
{ label: "Certificates", href: "/selfhosted/troubleshooting/certificates" },
|
||||
{ label: "Connectivity", href: "/selfhosted/troubleshooting/connectivity" },
|
||||
{ label: "Management unreachable", href: "/selfhosted/troubleshooting/connectivity#management-service-unreachable" },
|
||||
{ label: "Database", href: "/selfhosted/troubleshooting/database" },
|
||||
],
|
||||
},
|
||||
]}
|
||||
@@ -89,10 +145,16 @@ next to the feature they cover, so nothing here is a copy.
|
||||
|
||||
<StillStuck
|
||||
title="Still stuck?"
|
||||
description="Bring your debug bundle. The community and the team can help from there."
|
||||
id="still-stuck"
|
||||
separator="or"
|
||||
actions={[
|
||||
{ label: "Contact our Community", href: "/help/community-support", primary: true },
|
||||
{ label: "Contact NetBird Support", href: "/help/netbird-support", primary: true },
|
||||
{ label: "Contact our Community", href: "/help/community-support", primary: true, note: "Free for everyone." },
|
||||
{ label: "Contact NetBird Support", href: "/help/netbird-support", primary: true, note: "Mainly for paying NetBird Cloud customers and commercial-license deployments." },
|
||||
]}
|
||||
linksLabel="Preparing a report? Include your NetBird version (netbird version) and steps to reproduce. For problems on a peer, add a debug bundle."
|
||||
links={[
|
||||
{ label: "Debug bundle", href: "/help/troubleshooting-client#debug-bundle" },
|
||||
{ label: "Record a HAR file", href: "/help/recording-a-har-file" },
|
||||
{ label: "Support matrix", href: "/help/support-matrix" },
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -201,6 +201,7 @@ echo $response;
|
||||
"auto_update_version": "0.51.2",
|
||||
"auto_update_always": false,
|
||||
"metrics_push_enabled": false,
|
||||
"peer_hostname_collision_rejected": false,
|
||||
"agent_network_only": false,
|
||||
"dashboard_features": {
|
||||
"agent_network": true
|
||||
@@ -260,6 +261,7 @@ echo $response;
|
||||
"auto_update_version": "string",
|
||||
"auto_update_always": "boolean",
|
||||
"metrics_push_enabled": "boolean",
|
||||
"peer_hostname_collision_rejected": "boolean",
|
||||
"agent_network_only": "boolean",
|
||||
"dashboard_features": {
|
||||
"agent_network": "boolean"
|
||||
@@ -609,6 +611,11 @@ echo $response;
|
||||
|
||||
Enables or disables client metrics push for all peers in the account
|
||||
|
||||
</Property>
|
||||
<Property name="peer_hostname_collision_rejected" type="boolean" required={false}>
|
||||
|
||||
When enabled, registering or renaming a peer to a hostname already used by another peer in the account fails instead of receiving an IP-based suffix. Applies to ephemeral peers as well.
|
||||
|
||||
</Property>
|
||||
<Property name="agent_network_only" type="boolean" required={false}>
|
||||
|
||||
@@ -730,6 +737,7 @@ curl -X PUT https://api.netbird.io/api/accounts/{accountId} \
|
||||
"auto_update_version": "0.51.2",
|
||||
"auto_update_always": false,
|
||||
"metrics_push_enabled": false,
|
||||
"peer_hostname_collision_rejected": false,
|
||||
"agent_network_only": false,
|
||||
"dashboard_features": {
|
||||
"agent_network": true
|
||||
@@ -788,6 +796,7 @@ let data = JSON.stringify({
|
||||
"auto_update_version": "0.51.2",
|
||||
"auto_update_always": false,
|
||||
"metrics_push_enabled": false,
|
||||
"peer_hostname_collision_rejected": false,
|
||||
"agent_network_only": false,
|
||||
"dashboard_features": {
|
||||
"agent_network": true
|
||||
@@ -868,6 +877,7 @@ payload = json.dumps({
|
||||
"auto_update_version": "0.51.2",
|
||||
"auto_update_always": false,
|
||||
"metrics_push_enabled": false,
|
||||
"peer_hostname_collision_rejected": false,
|
||||
"agent_network_only": false,
|
||||
"dashboard_features": {
|
||||
"agent_network": true
|
||||
@@ -948,6 +958,7 @@ func main() {
|
||||
"auto_update_version": "0.51.2",
|
||||
"auto_update_always": false,
|
||||
"metrics_push_enabled": false,
|
||||
"peer_hostname_collision_rejected": false,
|
||||
"agent_network_only": false,
|
||||
"dashboard_features": {
|
||||
"agent_network": true
|
||||
@@ -1047,6 +1058,7 @@ request.body = JSON.dump({
|
||||
"auto_update_version": "0.51.2",
|
||||
"auto_update_always": false,
|
||||
"metrics_push_enabled": false,
|
||||
"peer_hostname_collision_rejected": false,
|
||||
"agent_network_only": false,
|
||||
"dashboard_features": {
|
||||
"agent_network": true
|
||||
@@ -1109,6 +1121,7 @@ RequestBody body = RequestBody.create(mediaType, '{
|
||||
"auto_update_version": "0.51.2",
|
||||
"auto_update_always": false,
|
||||
"metrics_push_enabled": false,
|
||||
"peer_hostname_collision_rejected": false,
|
||||
"agent_network_only": false,
|
||||
"dashboard_features": {
|
||||
"agent_network": true
|
||||
@@ -1187,6 +1200,7 @@ curl_setopt_array($curl, array(
|
||||
"auto_update_version": "0.51.2",
|
||||
"auto_update_always": false,
|
||||
"metrics_push_enabled": false,
|
||||
"peer_hostname_collision_rejected": false,
|
||||
"agent_network_only": false,
|
||||
"dashboard_features": {
|
||||
"agent_network": true
|
||||
@@ -1260,6 +1274,7 @@ echo $response;
|
||||
"auto_update_version": "0.51.2",
|
||||
"auto_update_always": false,
|
||||
"metrics_push_enabled": false,
|
||||
"peer_hostname_collision_rejected": false,
|
||||
"agent_network_only": false,
|
||||
"dashboard_features": {
|
||||
"agent_network": true
|
||||
@@ -1317,6 +1332,7 @@ echo $response;
|
||||
"auto_update_version": "string",
|
||||
"auto_update_always": "boolean",
|
||||
"metrics_push_enabled": "boolean",
|
||||
"peer_hostname_collision_rejected": "boolean",
|
||||
"agent_network_only": "boolean",
|
||||
"dashboard_features": {
|
||||
"agent_network": "boolean"
|
||||
|
||||
@@ -6,7 +6,7 @@ export const title = 'Agent Network'
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
Starts provisioning of a NetBird-managed Agent Network gateway for the account, allocating its endpoint under the managed zone on the first call. Idempotent — answers 202 when this call started (or, after a failure, restarted) provisioning and 200 when a deployment already exists, reporting current state either way. Returns 409 when the account already has an Agent Network endpoint that managed provisioning does not own, and 503 when endpoint allocation is temporarily exhausted.
|
||||
Starts provisioning of a NetBird-managed Agent Network gateway for the account, allocating its endpoint under the managed zone on the first call. Idempotent — answers 202 when this call started (or, after a failure, restarted) provisioning and 200 when a deployment already exists, reporting current state either way. A disabled deployment answers 200 with state `disabled` and stays disabled. Returns 409 when the account already has an Agent Network endpoint that managed provisioning does not own, and 503 when endpoint allocation is temporarily exhausted.
|
||||
</Col>
|
||||
|
||||
<Col sticky>
|
||||
@@ -194,7 +194,7 @@ echo $response;
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
Reports the account's managed gateway deployment and its derived state. Returns 404 when the account has no managed deployment.
|
||||
Reports the account's managed gateway deployment and its derived state, including `disabled` for a deployment that is turned off. Returns 404 when the account has no managed deployment.
|
||||
</Col>
|
||||
|
||||
<Col sticky>
|
||||
|
||||
@@ -845,6 +845,171 @@ echo $response;
|
||||
---
|
||||
|
||||
|
||||
## Delete MSP tenant {{ tag: 'DELETE' , label: '/api/integrations/msp/tenants/{id}' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
|
||||
### Path Parameters
|
||||
<Properties>
|
||||
|
||||
<Property name="id" type="string" required={true}>
|
||||
The unique identifier of a tenant account
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
|
||||
<Col sticky>
|
||||
<CodeGroup title="Request" tag="DELETE" label="/api/integrations/msp/tenants/{id}">
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X DELETE https://api.netbird.io/api/integrations/msp/tenants/{id} \
|
||||
-H 'Authorization: Token <TOKEN>'
|
||||
```
|
||||
|
||||
```js
|
||||
const axios = require('axios');
|
||||
|
||||
let config = {
|
||||
method: 'delete',
|
||||
maxBodyLength: Infinity,
|
||||
url: '/api/integrations/msp/tenants/{id}',
|
||||
headers: {
|
||||
'Authorization': 'Token <TOKEN>'
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then((response) => {
|
||||
console.log(JSON.stringify(response.data));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
```
|
||||
|
||||
```python
|
||||
import requests
|
||||
import json
|
||||
|
||||
url = "https://api.netbird.io/api/integrations/msp/tenants/{id}"
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Token <TOKEN>'
|
||||
}
|
||||
|
||||
response = requests.request("DELETE", url, headers=headers)
|
||||
|
||||
print(response.text)
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"net/http"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
url := "https://api.netbird.io/api/integrations/msp/tenants/{id}"
|
||||
method := "DELETE"
|
||||
|
||||
client := &http.Client {
|
||||
}
|
||||
req, err := http.NewRequest(method, url, nil)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
req.Header.Add("Authorization", "Token <TOKEN>")
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
```
|
||||
|
||||
```ruby
|
||||
require "uri"
|
||||
require "json"
|
||||
require "net/http"
|
||||
|
||||
url = URI("https://api.netbird.io/api/integrations/msp/tenants/{id}")
|
||||
|
||||
https = Net::HTTP.new(url.host, url.port)
|
||||
https.use_ssl = true
|
||||
|
||||
request = Net::HTTP::Delete.new(url)
|
||||
request["Authorization"] = "Token <TOKEN>"
|
||||
|
||||
response = https.request(request)
|
||||
puts response.read_body
|
||||
```
|
||||
|
||||
```java
|
||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url("https://api.netbird.io/api/integrations/msp/tenants/{id}")
|
||||
.method("DELETE")
|
||||
.addHeader("Authorization: Token <TOKEN>")
|
||||
.build();
|
||||
Response response = client.newCall(request).execute();
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl, array(
|
||||
CURLOPT_URL => 'https://api.netbird.io/api/integrations/msp/tenants/{id}',
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => '',
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 0,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => 'DELETE',
|
||||
CURLOPT_HTTPHEADER => array(
|
||||
'Authorization: Token <TOKEN>'
|
||||
),
|
||||
));
|
||||
|
||||
$response = curl_exec($curl);
|
||||
|
||||
curl_close($curl);
|
||||
echo $response;
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Unlink a tenant {{ tag: 'POST' , label: '/api/integrations/msp/tenants/{id}/unlink' }}
|
||||
|
||||
<Row>
|
||||
|
||||
@@ -18,6 +18,10 @@ export const title = 'Peers'
|
||||
<Property name="ip" type="string" required={false}>
|
||||
Filter peers by IP address
|
||||
</Property>
|
||||
|
||||
<Property name="mac" type="string" required={false}>
|
||||
Filter peers by MAC address of a network interface
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
|
||||
@@ -180,6 +184,12 @@ echo $response;
|
||||
"created_at": "2023-05-05T09:00:35.477782Z",
|
||||
"ip": "10.64.0.1",
|
||||
"ipv6": "fd00:4e42:ab12::1",
|
||||
"network_addresses": [
|
||||
{
|
||||
"net_ip": "192.168.0.11/24",
|
||||
"mac": "00:93:37:bd:83:0f"
|
||||
}
|
||||
],
|
||||
"connection_ip": "35.64.0.1",
|
||||
"connected": true,
|
||||
"last_seen": "2023-05-05T10:05:26.420578Z",
|
||||
@@ -242,6 +252,12 @@ echo $response;
|
||||
"created_at": "string",
|
||||
"ip": "string",
|
||||
"ipv6": "string",
|
||||
"network_addresses": [
|
||||
{
|
||||
"net_ip": "string",
|
||||
"mac": "string"
|
||||
}
|
||||
],
|
||||
"connection_ip": "string",
|
||||
"connected": "boolean",
|
||||
"last_seen": "string",
|
||||
@@ -475,6 +491,12 @@ echo $response;
|
||||
"created_at": "2023-05-05T09:00:35.477782Z",
|
||||
"ip": "10.64.0.1",
|
||||
"ipv6": "fd00:4e42:ab12::1",
|
||||
"network_addresses": [
|
||||
{
|
||||
"net_ip": "192.168.0.11/24",
|
||||
"mac": "00:93:37:bd:83:0f"
|
||||
}
|
||||
],
|
||||
"connection_ip": "35.64.0.1",
|
||||
"connected": true,
|
||||
"last_seen": "2023-05-05T10:05:26.420578Z",
|
||||
@@ -534,6 +556,12 @@ echo $response;
|
||||
"created_at": "string",
|
||||
"ip": "string",
|
||||
"ipv6": "string",
|
||||
"network_addresses": [
|
||||
{
|
||||
"net_ip": "string",
|
||||
"mac": "string"
|
||||
}
|
||||
],
|
||||
"connection_ip": "string",
|
||||
"connected": "boolean",
|
||||
"last_seen": "string",
|
||||
@@ -875,6 +903,12 @@ echo $response;
|
||||
"created_at": "2023-05-05T09:00:35.477782Z",
|
||||
"ip": "10.64.0.1",
|
||||
"ipv6": "fd00:4e42:ab12::1",
|
||||
"network_addresses": [
|
||||
{
|
||||
"net_ip": "192.168.0.11/24",
|
||||
"mac": "00:93:37:bd:83:0f"
|
||||
}
|
||||
],
|
||||
"connection_ip": "35.64.0.1",
|
||||
"connected": true,
|
||||
"last_seen": "2023-05-05T10:05:26.420578Z",
|
||||
@@ -934,6 +968,12 @@ echo $response;
|
||||
"created_at": "string",
|
||||
"ip": "string",
|
||||
"ipv6": "string",
|
||||
"network_addresses": [
|
||||
{
|
||||
"net_ip": "string",
|
||||
"mac": "string"
|
||||
}
|
||||
],
|
||||
"connection_ip": "string",
|
||||
"connected": "boolean",
|
||||
"last_seen": "string",
|
||||
|
||||
@@ -214,6 +214,11 @@ echo $response;
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,6 +274,11 @@ echo $response;
|
||||
"windows_path": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,6 +530,23 @@ echo $response;
|
||||
</Properties>
|
||||
</details>
|
||||
|
||||
</Property>
|
||||
<Property name="certificate_check" type="object" required={false}>
|
||||
|
||||
<details className="custom-details" open>
|
||||
<summary>Posture check for a certificate held by the peer that chains to one of the given CA certificates. Not supported on mobile devices (iOS and Android), which always fail this check and are denied access</summary>
|
||||
<Properties>
|
||||
|
||||
<Properties><Property name="ca_certificates" type="string[]" required={true}>
|
||||
|
||||
PEM encoded CA certificates the peer's certificate must chain to
|
||||
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Properties>
|
||||
</details>
|
||||
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
@@ -590,6 +617,11 @@ curl -X POST https://api.netbird.io/api/posture-checks \
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}'
|
||||
@@ -648,6 +680,11 @@ let data = JSON.stringify({
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -728,6 +765,11 @@ payload = json.dumps({
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -808,6 +850,11 @@ func main() {
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}`)
|
||||
@@ -907,6 +954,11 @@ request.body = JSON.dump({
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -969,6 +1021,11 @@ RequestBody body = RequestBody.create(mediaType, '{
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}');
|
||||
@@ -1047,6 +1104,11 @@ curl_setopt_array($curl, array(
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}',
|
||||
@@ -1120,6 +1182,11 @@ echo $response;
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1173,6 +1240,11 @@ echo $response;
|
||||
"windows_path": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1405,6 +1477,11 @@ echo $response;
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1458,6 +1535,11 @@ echo $response;
|
||||
"windows_path": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1716,6 +1798,23 @@ echo $response;
|
||||
</Properties>
|
||||
</details>
|
||||
|
||||
</Property>
|
||||
<Property name="certificate_check" type="object" required={false}>
|
||||
|
||||
<details className="custom-details" open>
|
||||
<summary>Posture check for a certificate held by the peer that chains to one of the given CA certificates. Not supported on mobile devices (iOS and Android), which always fail this check and are denied access</summary>
|
||||
<Properties>
|
||||
|
||||
<Properties><Property name="ca_certificates" type="string[]" required={true}>
|
||||
|
||||
PEM encoded CA certificates the peer's certificate must chain to
|
||||
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Properties>
|
||||
</details>
|
||||
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
@@ -1786,6 +1885,11 @@ curl -X PUT https://api.netbird.io/api/posture-checks/{postureCheckId} \
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}'
|
||||
@@ -1844,6 +1948,11 @@ let data = JSON.stringify({
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1924,6 +2033,11 @@ payload = json.dumps({
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -2004,6 +2118,11 @@ func main() {
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}`)
|
||||
@@ -2103,6 +2222,11 @@ request.body = JSON.dump({
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -2165,6 +2289,11 @@ RequestBody body = RequestBody.create(mediaType, '{
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}');
|
||||
@@ -2243,6 +2372,11 @@ curl_setopt_array($curl, array(
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}',
|
||||
@@ -2316,6 +2450,11 @@ echo $response;
|
||||
"windows_path": "C:
rogramData
etBird\netbird.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2369,6 +2508,11 @@ echo $response;
|
||||
"windows_path": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate_check": {
|
||||
"ca_certificates": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,6 +537,7 @@ echo $response;
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -635,6 +636,7 @@ echo $response;
|
||||
"target_id": "string",
|
||||
"target_type": "string",
|
||||
"path": "string",
|
||||
"access_action": "string",
|
||||
"protocol": "string",
|
||||
"host": "string",
|
||||
"port": "integer",
|
||||
@@ -772,6 +774,11 @@ echo $response;
|
||||
|
||||
URL path prefix for this target (HTTP only)
|
||||
|
||||
</Property>
|
||||
<Property name="access_action" type="string" required={false} enumList={["inherit","bypass","block"]}>
|
||||
|
||||
Access action applied after this target's HTTP path prefix is selected. "inherit" uses the service authentication configuration, "bypass" skips service authentication, and "block" denies access. HTTP services only.
|
||||
|
||||
</Property>
|
||||
<Property name="protocol" type="string" required={true} enumList={["http","https","tcp","udp"]}>
|
||||
|
||||
@@ -1056,6 +1063,7 @@ curl -X POST https://api.netbird.io/api/reverse-proxies/services \
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -1145,6 +1153,7 @@ let data = JSON.stringify({
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -1256,6 +1265,7 @@ payload = json.dumps({
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -1367,6 +1377,7 @@ func main() {
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -1497,6 +1508,7 @@ request.body = JSON.dump({
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -1590,6 +1602,7 @@ RequestBody body = RequestBody.create(mediaType, '{
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -1699,6 +1712,7 @@ curl_setopt_array($curl, array(
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -1805,6 +1819,7 @@ echo $response;
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -1901,6 +1916,7 @@ echo $response;
|
||||
"target_id": "string",
|
||||
"target_type": "string",
|
||||
"path": "string",
|
||||
"access_action": "string",
|
||||
"protocol": "string",
|
||||
"host": "string",
|
||||
"port": "integer",
|
||||
@@ -2169,6 +2185,7 @@ echo $response;
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -2265,6 +2282,7 @@ echo $response;
|
||||
"target_id": "string",
|
||||
"target_type": "string",
|
||||
"path": "string",
|
||||
"access_action": "string",
|
||||
"protocol": "string",
|
||||
"host": "string",
|
||||
"port": "integer",
|
||||
@@ -2409,6 +2427,11 @@ echo $response;
|
||||
|
||||
URL path prefix for this target (HTTP only)
|
||||
|
||||
</Property>
|
||||
<Property name="access_action" type="string" required={false} enumList={["inherit","bypass","block"]}>
|
||||
|
||||
Access action applied after this target's HTTP path prefix is selected. "inherit" uses the service authentication configuration, "bypass" skips service authentication, and "block" denies access. HTTP services only.
|
||||
|
||||
</Property>
|
||||
<Property name="protocol" type="string" required={true} enumList={["http","https","tcp","udp"]}>
|
||||
|
||||
@@ -2693,6 +2716,7 @@ curl -X PUT https://api.netbird.io/api/reverse-proxies/services/{serviceId} \
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -2782,6 +2806,7 @@ let data = JSON.stringify({
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -2893,6 +2918,7 @@ payload = json.dumps({
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -3004,6 +3030,7 @@ func main() {
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -3134,6 +3161,7 @@ request.body = JSON.dump({
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -3227,6 +3255,7 @@ RequestBody body = RequestBody.create(mediaType, '{
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -3336,6 +3365,7 @@ curl_setopt_array($curl, array(
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -3442,6 +3472,7 @@ echo $response;
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -3538,6 +3569,7 @@ echo $response;
|
||||
"target_id": "string",
|
||||
"target_type": "string",
|
||||
"path": "string",
|
||||
"access_action": "string",
|
||||
"protocol": "string",
|
||||
"host": "string",
|
||||
"port": "integer",
|
||||
@@ -4210,6 +4242,7 @@ echo $response;
|
||||
"target_id": "cs8i4ug6lnn4g9hqv7mg",
|
||||
"target_type": "subnet",
|
||||
"path": "/",
|
||||
"access_action": "inherit",
|
||||
"protocol": "http",
|
||||
"host": "10.10.0.1",
|
||||
"port": 8080,
|
||||
@@ -4306,6 +4339,7 @@ echo $response;
|
||||
"target_id": "string",
|
||||
"target_type": "string",
|
||||
"path": "string",
|
||||
"access_action": "string",
|
||||
"protocol": "string",
|
||||
"host": "string",
|
||||
"port": "integer",
|
||||
@@ -4398,7 +4432,7 @@ echo $response;
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
Delete an existing service custom domain
|
||||
Delete an existing service custom domain after removing or moving all services that use it or its subdomains, including disabled services.
|
||||
|
||||
### Path Parameters
|
||||
<Properties>
|
||||
|
||||
@@ -28,7 +28,7 @@ NetBird doesn't apply the MDM and EDR checks to all devices in the network. Inst
|
||||
the checks to apply.
|
||||
|
||||
<Note>
|
||||
This feature is only available in the cloud version of NetBird.
|
||||
This feature is available in NetBird Cloud on the [Business plan](https://netbird.io/pricing) and in self-hosted deployments with an [Enterprise Commercial License](/selfhosted/enterprise). The open source Community Edition does not include it.
|
||||
</Note>
|
||||
|
||||
## Supported EDR Platforms
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
import {Note, Warning} from "@/components/mdx";
|
||||
|
||||
export const description = "Use a Certificate posture check so only devices holding a certificate from your own CA, with the private key still on the device, reach the resources a policy protects."
|
||||
|
||||
# Requiring a device certificate
|
||||
|
||||
Most posture checks read something the device reports about itself: its client version, its operating system, a process name. Those are good at keeping unmanaged devices out, but a determined user can satisfy them on a personal laptop. What they cannot produce is a certificate your own certificate authority issued to a company device, and the private key that goes with it. The **Certificate** posture check asks for exactly that: the peer must hold a certificate that chains to a CA you name, and it must prove it still has the private key by signing a challenge from the management server. A copied certificate without its key proves nothing.
|
||||
|
||||
Think of it as a badge check at the door, repeated. The CA is the badge printer you trust, the certificate is the badge, and the signature is the guard asking the holder to sign their name rather than just wave the badge. NetBird repeats the check every 4 hours for as long as the peer is connected, so a key that is removed from a device, or a certificate that expires, closes the door within 4 hours.
|
||||
|
||||
## The scenario
|
||||
|
||||
A company runs a PKI with a root CA, `Corp Root CA`, that issues a certificate to every managed laptop. The laptops are NetBird peers in the group `laptops`, and an access policy from `laptops` to the group `internal-apps` grants them the internal services. The goal: a device that is in `laptops` but has no company certificate, or has lost its key, must not reach `internal-apps`. Everything below is built around that example.
|
||||
|
||||
Three things to do, in order:
|
||||
|
||||
1. Create a Certificate posture check that trusts `Corp Root CA`.
|
||||
2. Attach it to the policy from `laptops` to `internal-apps`.
|
||||
3. Make sure each laptop holds its certificate where the NetBird client can use it.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- NetBird management and client version [0.81.0](https://github.com/netbirdio/netbird/releases) or newer on both sides. Older clients never answer the challenge and fail the check.
|
||||
- The PEM encoded certificate of the CA that issues your device certificates. A root or an intermediate works; whichever you paste becomes a trust anchor, so an intermediate narrows the check to the devices it signed.
|
||||
- Device certificates with a P-256 or P-384 ECDSA key or an RSA key of at least 2048 bits, issued by that CA and valid at the time of the check. An Ed25519 key also works, as a PEM key file on Linux. The private key must be usable without a prompt: a key that asks for a PIN or a password on every signature cannot be used by a background service.
|
||||
|
||||
<Warning>
|
||||
The check accepts any certificate under the CA, whatever it was issued for, and it does not tie the certificate to a user or a hostname. Dedicate a CA, or an intermediate, to device identities rather than reusing the one that signs your TLS servers.
|
||||
</Warning>
|
||||
|
||||
## Create the posture check
|
||||
|
||||
Navigate to `Access Control` > `Posture Checks` in the NetBird dashboard and click `Create Posture Check`. Enable `Certificate`.
|
||||
|
||||

|
||||
|
||||
Paste the PEM of `Corp Root CA` into the `CA Certificates` field. `Add CA Certificate` adds further anchors, for example a second root during a CA rotation; a peer passes if its certificate chains to any of them. Give the check a name on the `Name & Description` tab, for example `Company device certificate`, and save.
|
||||
|
||||
## Attach it to the policy
|
||||
|
||||
Open the policy from `laptops` to `internal-apps`, go to its `Posture Checks` tab, choose `Browse Checks` and add `Company device certificate`. From now on the policy applies only to peers in `laptops` that pass the check; the devices keep whatever other policies grant them.
|
||||
|
||||
<Note>
|
||||
A peer that sends no certificate at all fails the check, including every iOS and Android device: the mobile apps do not support certificate proofs. Do not attach this check to a policy that mobile devices rely on.
|
||||
</Note>
|
||||
|
||||
## Put the certificate on the device
|
||||
|
||||
The client proves possession with whatever the operating system already uses to store certificates, so for a managed fleet this step is usually done by your MDM or enrollment tooling rather than by hand. What the client reads on each platform:
|
||||
|
||||
| Platform | Device certificates | User certificates |
|
||||
| --- | --- | --- |
|
||||
| Windows | `Local Computer` > `Personal` store, where Active Directory and Intune enroll device certificates | the `Personal` store of the signed-in user who owns the active NetBird profile |
|
||||
| macOS | the System keychain, where MDM installs device identities | the login keychain of the signed-in user who owns the active profile |
|
||||
| Linux | PEM files in `/etc/netbird/certs`, or a PKCS#11 token | not applicable |
|
||||
|
||||
The **profile owner** is the user who created the NetBird profile the client is currently running. Only that user's store is consulted, and only while that user has a session open, so a second person signed in to the same machine cannot answer for the profile, and a laptop sitting at the sign-in screen proves device certificates alone.
|
||||
|
||||
### Windows and macOS
|
||||
|
||||
On Windows there is nothing to configure. A certificate enrolled into the computer store is found by the service; a certificate in the owner's personal store is found through a helper that NetBird runs inside that user's session. Keys protected by a TPM work the same way, since the client only ever asks the operating system to sign.
|
||||
|
||||
On macOS the System keychain needs one setting, described below. A certificate in the owner's login keychain is found through the helper, like on Windows, and keys held in the Secure Enclave work too.
|
||||
|
||||
Two platform details are worth knowing. On macOS, the first signature from the login keychain shows the standard prompt that `netbird` wants to use the key; choose `Always Allow`, or the prompt returns on every check. If the user declines, the client does not ask that user again for an hour. On both platforms a key that would need a PIN or password is skipped rather than prompted for.
|
||||
|
||||
#### macOS: letting NetBird use a device key
|
||||
|
||||
The NetBird service reads the System keychain without a user session, so it cannot answer a keychain prompt. A device identity therefore works only when its key allows NetBird to use it.
|
||||
|
||||
- When the identity is installed with MDM, set `AllowAllAppsAccess` to `true` in the certificate payload. MDM cannot name a single application, so this lets every application on the device use the key, including to produce a proof for this check.
|
||||
- When importing it by hand, allow the NetBird binary explicitly. `security import` prompts for the `.p12` passphrase, which keeps it out of your shell history:
|
||||
|
||||
```bash
|
||||
sudo security import device.p12 -k /Library/Keychains/System.keychain \
|
||||
-T /Applications/NetBird.app/Contents/MacOS/netbird
|
||||
```
|
||||
|
||||
#### Which user counts
|
||||
|
||||
The user whose certificate counts is the owner of the active NetBird profile. For a profile without an owner it is the user signed in at the console. A user certificate is proven shortly after that user signs in.
|
||||
|
||||
### Linux: certificate files
|
||||
|
||||
Place the certificate and its key in `/etc/netbird/certs`, or in the directory named by the `NB_CERT_STORE_DIR` environment variable of the service. A certificate file ends in `.pem`, `.crt` or `.cer` and holds the device certificate first, followed by any intermediates. The key is either in the same file or in a sibling file with the same name and the `.key` extension:
|
||||
|
||||
```text
|
||||
/etc/netbird/certs/
|
||||
├── laptop.pem # device certificate, intermediates after it
|
||||
└── laptop.key # PKCS#8, EC or RSA private key
|
||||
```
|
||||
|
||||
The directory and the files must be owned by root and writable by nobody else, and no file in the directory may be a symlink. Keep plain key files readable by root only (`chmod 600 laptop.key`): the client does not refuse a world-readable key, but any local user could copy it and pass the check from another machine. Several certificates can sit side by side, and the client picks whichever one chains to the CA in the check.
|
||||
|
||||
### Linux: key in the TPM
|
||||
|
||||
A key that lives in the TPM never exists as a file in the clear. Two formats are supported.
|
||||
|
||||
**A TSS2 key file**, the format `tpm2-openssl` and `tpm2-tss-engine` write, goes into the same directory as a plain key would:
|
||||
|
||||
```bash
|
||||
openssl genpkey -provider tpm2 -algorithm EC -pkeyopt group:P-256 -out /etc/netbird/certs/laptop.key
|
||||
openssl req -provider tpm2 -provider default -new -key /etc/netbird/certs/laptop.key -subj "/CN=$(hostname)" -out laptop.csr
|
||||
# have Corp Root CA sign laptop.csr, then store the result as /etc/netbird/certs/laptop.pem
|
||||
```
|
||||
|
||||
The client signs through `/dev/tpmrm0` for every challenge and the key file stays useless on any other machine.
|
||||
|
||||
**A tpm2-pkcs11 token** is the other route, and the one Red Hat-based distributions document. Give the service the token through its environment file, `/etc/sysconfig/netbird`. The rpm package installs it readable by root only; on other distributions, such as Debian and Ubuntu, create it yourself before putting the PIN in it (`sudo install -m 0600 /dev/null /etc/sysconfig/netbird`):
|
||||
|
||||
```bash
|
||||
NB_CERT_PKCS11_URI=pkcs11:token=netbird?module-path=/usr/lib64/pkcs11/libtpm2_pkcs11.so
|
||||
NB_TPM_PIN=<user PIN of the token>
|
||||
```
|
||||
|
||||
The URI follows RFC 7512: `token` selects the token by label, `module-path` names the module and may be left out where p11-kit knows the module. A PIN is accepted only together with a token label, so a mistyped PIN can never be tried against a smartcard that happens to be plugged in. A PIN the token rejects is not tried again until the service restarts, so correct `NB_TPM_PIN` and restart the service. The certificate can be an object on the token, added with `tpm2_ptool addcert`, or a `.pem` file in `/etc/netbird/certs` without a key file next to it: the client pairs such a file with the token key that has the same public key.
|
||||
|
||||
<Warning>
|
||||
Do not pass the PIN with `netbird service install --service-env`: the command line ends up in your shell history. Put it in the environment file.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
The PKCS#11 store is part of the deb and rpm packages for amd64 and arm64. The 32-bit packages, the generic Linux tarball and the container images cannot load a PKCS#11 module; they log that the configured token is ignored. A TSS2 key file works with every Linux build.
|
||||
</Note>
|
||||
|
||||
## Verify
|
||||
|
||||
A device that proves no certificate yet tries again every five minutes, so a certificate placed later is picked up without a restart. To watch it work, raise the log level with `netbird debug log level debug` and read the client log. On Linux that is `/var/log/netbird/client.log`; the equivalent Windows and macOS paths are listed on the [troubleshooting page](/help/troubleshooting-client). A laptop that passes logs, in this order:
|
||||
|
||||
```text
|
||||
certificate posture: store holds 1 candidate certificates
|
||||
certificate posture: challenge 0 proven by "CN=laptop-42" with ecdsa-sha256, signature 71 bytes, chain of 2
|
||||
certificate posture: 1 challenges produced 1 proofs
|
||||
```
|
||||
|
||||
`challenge 0 matched none of the 1 candidates` means the certificate does not chain to a CA in the check, usually because an intermediate is missing from the file or from the check; the lines before it give the reason for each certificate. `certificate store holds no candidates` means no usable certificate and key pair was found at all. The management server logs nothing for a successful proof and a `dropping a certificate proof of peer` line with the reason for an invalid one.
|
||||
|
||||
Two other messages are worth knowing:
|
||||
|
||||
| Log message | Meaning |
|
||||
| --- | --- |
|
||||
| `SecKeyCreateSignature: CFError -25308` | macOS: the key's access list does not allow NetBird. See [letting NetBird use a device key](#mac-os-letting-net-bird-use-a-device-key). |
|
||||
| `rejected the PIN, not retrying it until the daemon restarts` | The token refused the PIN. Fix `NB_TPM_PIN` and restart the service. |
|
||||
|
||||
In the dashboard the effect is the same as for any other posture check: a peer that fails loses the policy, so it no longer sees the `internal-apps` peers, and regains them on the next sync after it passes.
|
||||
|
||||
## How the check keeps itself current
|
||||
|
||||
The management server sends each connected peer a fresh challenge every 4 hours. The client signs the newest challenge and reports its proofs only when the set of certificates it can prove has changed, so a steady fleet produces no extra traffic. A device whose key disappears fails within 4 hours, and a certificate that expires fails at the next evaluation. This is the one posture check that is refreshed on a timer; the others are driven purely by configuration and metadata changes, as described in [When Posture Checks Are Evaluated](/manage/access-control/posture-checks#when-posture-checks-are-evaluated).
|
||||
|
||||
## When not to use it
|
||||
|
||||
- Fleets with iOS or Android devices in the same policy: they always fail the check. Give mobile devices their own policy, or gate them with checks they support.
|
||||
- Certificates with keys you cannot make available without a prompt, such as smartcards that require a PIN on every signature.
|
||||
- As a substitute for user identity. The check proves the device holds a key from your CA; who is using the device is what your identity provider and the policy's groups decide.
|
||||
|
||||
## In one breath
|
||||
|
||||
- A Certificate posture check trusts `Corp Root CA`; attached to the `laptops` to `internal-apps` policy, it hides the policy from any laptop without a company certificate.
|
||||
- The laptop proves it holds the key by signing a challenge from management, every 4 hours while connected.
|
||||
- Windows and macOS find the certificate in the system and the profile owner's store; Linux reads `/etc/netbird/certs` or a tpm2-pkcs11 token.
|
||||
- Lose the key, or let the certificate expire, and the policy is gone within 4 hours.
|
||||
@@ -5,7 +5,7 @@ In this regard, NetBird posture checks verify various aspects of a connecting de
|
||||
|
||||
<YouTube videoId="nk7XM8oi-4Y" title="Understanding Posture Checks" />
|
||||
|
||||
The feature also allows for network-level restrictions by enabling you to **allow or block specific peer network ranges**. Furthermore, you can set constraints based on the operating system of the connecting device, **ensuring that only approved OS versions can gain access**. For an even more detailed level of control, Posture Checks can examine the running processes on a peer device, **allowing or denying access based on the presence of specific applications or services**.
|
||||
The feature also allows for network-level restrictions by enabling you to **allow or block specific peer network ranges**. Furthermore, you can set constraints based on the operating system of the connecting device, **ensuring that only approved OS versions can gain access**. For an even more detailed level of control, Posture Checks can examine the running processes on a peer device, **allowing or denying access based on the presence of specific applications or services**. Finally, a device can be required to **hold a certificate issued by your own certificate authority** and prove it still has the private key, which ties access to a credential you issued rather than to anything the device merely reports.
|
||||
|
||||
By using these diverse checking capabilities, NetBird empowers you to create a robust and finely-tuned security posture for your network, significantly reducing the risk of unauthorized access and potential security breaches.
|
||||
|
||||
@@ -85,6 +85,17 @@ Furthermore, this process-based posture check allows you to create specific poli
|
||||
|
||||

|
||||
|
||||
### Certificate
|
||||
Restrict access to peers holding a certificate issued by your own certificate authority. Unlike most other checks, this one is not satisfied by something the device reports: the peer must present a certificate that chains to one of the CAs you paste here, and sign a challenge from the management server with the certificate's private key. A certificate copied to another machine without its key does not pass, and a key that is removed or a certificate that expires fails the check within 4 hours.
|
||||
|
||||

|
||||
|
||||
The client finds certificates where the operating system keeps them: the computer and user stores on Windows, the System and login keychains on macOS, and on Linux a directory of PEM files or a PKCS#11 token, including keys held in a TPM. Which store to use, how to place a certificate on each platform and how to verify the result are covered in [Requiring a device certificate](/manage/access-control/posture-checks/device-certificates).
|
||||
|
||||
<Note>
|
||||
The Certificate check requires NetBird version [0.81.0](https://github.com/netbirdio/netbird/releases) or newer on both the management server and the client. It is not supported on iOS and Android: mobile peers always fail it and are denied the access the policy grants, so keep it off policies that mobile devices depend on.
|
||||
</Note>
|
||||
|
||||
## Name & Description
|
||||
After enabling the desired posture check, go to the `Name & Description` tab. Here, enter a descriptive name for your newly created posture check and save it.
|
||||
|
||||
@@ -120,7 +131,7 @@ Following these steps, you can effectively implement and manage NetBird's Postur
|
||||
|
||||
NetBird evaluates posture checks when a peer connects or logs in. Checks are also re-evaluated during an active session whenever a peer reports changed system metadata. The management server replays each check against the peer's previous and current metadata, and when a check's verdict flips, it recomputes the network map and distributes the update to the affected peers.
|
||||
|
||||
When you add or change a posture check on a policy, the updated configuration is distributed to connected peers immediately. A peer that is already connected is evaluated against the new check on its next connection. There is no periodic redistribution interval; distribution is driven by configuration changes and peer metadata changes, not a timer. For more on how changes reach peers, see [How Policy Changes Are Distributed](/manage/access-control#how-policy-changes-are-distributed).
|
||||
When you add or change a posture check on a policy, the updated configuration is distributed to connected peers immediately. A peer that is already connected is evaluated against the new check on its next connection. There is no periodic redistribution interval; distribution is driven by configuration changes and peer metadata changes, not a timer. The one exception is the Certificate check, whose challenge the management server renews every four hours so a connected peer keeps re-proving possession of its key; see [How the check keeps itself current](/manage/access-control/posture-checks/device-certificates#how-the-check-keeps-itself-current). For more on how changes reach peers, see [How Policy Changes Are Distributed](/manage/access-control#how-policy-changes-are-distributed).
|
||||
|
||||
<Note>
|
||||
Mid-session re-evaluation requires a NetBird management server running [0.74.0](https://github.com/netbirdio/netbird/releases) or newer, and is driven by the peer reporting a metadata change rather than by a continuous poll. A peer that passes a posture check and later changes state during a long-lived session (for example an operating system downgrade or a required process stopping) is re-evaluated on its next metadata sync. On management servers older than 0.74.0, that state change is not picked up until the peer reconnects.
|
||||
@@ -156,6 +167,10 @@ Upgrade the Android client to 0.77.1 or newer. While older Android clients are s
|
||||
- Use Peer Network Range checks based on the **public connection IP**, which work on every platform and every version
|
||||
- Apply local-NIC-based Peer Network Range posture checks only to policies targeting platforms that report them (Windows, macOS, Linux, iOS 0.69.0+, and Android 0.77.1+)
|
||||
|
||||
### Certificate Check on Mobile
|
||||
|
||||
The NetBird iOS and Android apps do not collect certificate proofs, so a mobile peer always fails a Certificate check and is denied the access of every policy that carries it. There is no version of the mobile apps that changes this yet. Keep Certificate checks on policies whose source groups hold desktop and server peers only, and gate mobile devices with the checks they support.
|
||||
|
||||
## Get started with NetBird
|
||||
<div>
|
||||
<Button name="button" className="button-5" onClick={() => window.open("https://netbird.io/pricing")}>Use NetBird</Button>
|
||||
|
||||
@@ -23,4 +23,19 @@ supported third-party platforms. To get started, select one of the following int
|
||||
- [Amazon Data Firehose](/manage/activity/event-streaming/amazon-firehose)
|
||||
- [SentinelOne Data Lake](/manage/activity/event-streaming/sentinelone-data-lake)
|
||||
- [Generic HTTP](/manage/activity/event-streaming/generic-http)
|
||||
- [Wazuh](/manage/activity/event-streaming/wazuh)
|
||||
- [Wazuh](/manage/activity/event-streaming/wazuh)
|
||||
|
||||
## Delivery
|
||||
|
||||
NetBird streams each audit event and traffic event after saving it, so an event that fails to stream is not lost: it remains in the [audit events log](/manage/activity) or the [traffic events](/manage/activity/traffic-events-logging) store. When the destination is unreachable or rejects an event, Datadog and Generic HTTP behave differently:
|
||||
|
||||
- **Datadog**: one delivery attempt per event. A failed delivery is not retried.
|
||||
- **Generic HTTP**: a failed delivery is retried twice, after 1 and then 2 seconds, before the event is dropped (three attempts in total by default).
|
||||
|
||||
Creating, updating, and deleting a stream destination are recorded in the audit events log as `Integration created`, `Integration updated`, and `Integration deleted`. Updating includes enabling and disabling the destination.
|
||||
|
||||
Changes to a stream destination take up to five minutes to apply, because NetBird caches the destination's settings. After you disable or delete a destination, audit and traffic events can keep arriving there for up to five minutes, including the event that records the change. After you enable it again, events can take the same time to resume. In self-hosted Enterprise deployments, this interval is set by `NB_EVENT_STREAMING_CACHE_TTL` on the management server and the enricher service.
|
||||
|
||||
NetBird removes any `name` or `email` key from `meta` before streaming an event. For many audit events, such as group, policy, and peer changes, `name` is where the object's name is, so look it up in the audit events log using `target_id`.
|
||||
|
||||
For the fields carried by streamed traffic events, see [Streamed event fields](/manage/activity/traffic-events-logging#streamed-event-fields).
|
||||
|
||||
@@ -93,7 +93,7 @@ NetBird emits **two distinct event shapes** through the same integration. Both a
|
||||
- **`ID` may be a number or a string** (test events use a string identifier; real audit events use a numeric ID). Wazuh normalises both to strings in the indexed `data.nb.ID`, so KQL queries don't need to handle the mixed type.
|
||||
- **`Message`** is the most reliable field to pivot rules on; it's a stable English phrase like "peer added" or "policy created".
|
||||
- **`reference`** is the canonical URL into NetBird's activity log; useful as a click-through pivot from a Wazuh alert back to the source event.
|
||||
- **`meta` is event-type-specific.** Peer events carry `meta.name`, setup-key events carry `meta.type`, integration events carry `meta.platform`. Treat unknown keys as opaque.
|
||||
- **`meta` is event-type-specific.** Peer events carry `meta.fqdn` and `meta.ip`, setup-key events carry `meta.type`, integration events carry `meta.platform`. Any `name` or `email` key is removed before streaming. Treat unknown keys as opaque.
|
||||
|
||||
### Traffic events
|
||||
|
||||
|
||||
@@ -104,6 +104,14 @@ The current version of NetBird tracks a wide range of network changes that occur
|
||||
- Integration updated
|
||||
- Integration deleted
|
||||
|
||||
- **Traffic Events Logging Management:**
|
||||
- Network traffic logging enabled
|
||||
- Network traffic logging disabled
|
||||
- Network traffic packet counting enabled
|
||||
- Network traffic packet counting disabled
|
||||
- Network traffic group added
|
||||
- Network traffic group removed
|
||||
|
||||
- **Other Events:**
|
||||
- Transferred owner role
|
||||
- Posture check created
|
||||
|
||||
@@ -153,12 +153,16 @@ choose the groups you want to include, and click `Save Groups`.
|
||||
<img src="/docs-static/img/manage/activity/traffic-events-logging/traffic-events-groups-logging-settings.png" alt="traffic-events-groups-logging-settings" className="imagewrapper-big"/>
|
||||
</p>
|
||||
|
||||
Changes to these settings, including the group scope, are pushed to connected peers and take effect without restarting the NetBird client. Every change is also recorded in the [audit events log](/manage/activity).
|
||||
|
||||
## Log Retention
|
||||
|
||||
While in experimental mode, logs are retained for **seven days**.
|
||||
Additionally, the current API returns a maximum of **50,000 flow records**.
|
||||
This limit may change.
|
||||
|
||||
In self-hosted Enterprise deployments, the retention period is set by `NB_PERSISTENCE_RETENTION_PERIOD` on the enricher service. The installer sets it to `168h` (seven days).
|
||||
|
||||
## Report rate
|
||||
Aggregated flows might take up to **ten minutes** to become available through the API and Dashboard. An end count for some TCP connections can appear in a later window, depending on OS settings and connection termination.
|
||||
|
||||
@@ -170,6 +174,27 @@ enhancing your ability to detect and respond to security events.
|
||||
|
||||
For detailed instructions on supported integrations and how to set them up, refer to the [integrations guide](/manage/activity/event-streaming).
|
||||
|
||||
### Streamed event fields
|
||||
|
||||
A streamed traffic event uses the same envelope as a streamed audit event: an event ID, a timestamp, and a message. Clients from v0.75 send aggregated events, whose message is always `TYPE_UNKNOWN`, so read activity from the `num_of_*` counts instead. The initiator and target fields are empty, and the reference field is empty or, for Datadog, omitted. Envelope field names depend on the integration: Datadog uses `id` and `timestamp`, Generic HTTP uses `ID` and `Timestamp`. The flow details are in `meta`, under the same keys for every integration unless you set a custom Generic HTTP body template:
|
||||
|
||||
* **User**: `user_id`, the ID of the user associated with the source peer.
|
||||
* **Flow**: `flow_id`, `reporter_id` (the peer that reported the event), `protocol`, `direction`, `policy_id`, `policy_name`, `icmp_type`, and `icmp_code`.
|
||||
* **Source and destination**: `source_id`, `source_name`, `source_addr`, `source_dns_label`, `source_connection_ip`, `source_geo_city`, and `source_geo_country`, with the same set of `destination_*` fields.
|
||||
* **Volume**: `rx_bytes`, `rx_packets`, `tx_bytes`, and `tx_packets`.
|
||||
* **Counts**: `num_of_starts`, `num_of_ends`, and `num_of_drops`.
|
||||
* **Timing**: `received_timestamp`, the time the event reached the NetBird servers.
|
||||
|
||||
Compared with API responses, streamed events do not include the user's name or email, the aggregation window start and end, or the operating system of the source and destination. `user_id` is empty when the source peer has no associated user, such as a peer registered with a [setup key](/manage/peers/register-machines-using-setup-keys), and when the source is not a peer.
|
||||
|
||||
### Streaming delivery
|
||||
|
||||
Each traffic event is saved to the traffic events store before it is streamed, so an event that fails to stream is still visible in the Dashboard and API. The NetBird client resends each event until the NetBird servers acknowledge it. Once an event reaches the servers, every step up to saving it is retried: the servers acknowledge an event only after queueing it, and remove it from the queue only after saving it. If server processing falls behind, events wait in the queue and arrive late rather than being dropped.
|
||||
|
||||
In rare cases, such as a server restart while an event is being processed, the same event can be streamed twice. Deduplicate on the event ID.
|
||||
|
||||
Changes to a stream destination take up to five minutes to apply, and what happens when the destination is unreachable depends on the integration; see [Delivery](/manage/activity/event-streaming#delivery).
|
||||
|
||||
## Traffic Events Data
|
||||
|
||||
When enabled, a NetBird peer will record metadata for each network flow that it participates in. The data collected by peers includes:
|
||||
@@ -192,7 +217,7 @@ In addition to the data collected by the peers, the NetBird API provides additio
|
||||
* **Peer ID**: The unique identifier of the peer.
|
||||
* **Resource name**: The name of the resource or network route.
|
||||
* **Policy Name**: The name of the policy that allowed the flow.
|
||||
* **User ID, name, and email**: The name and email of the user associated with the source peer.
|
||||
* **User ID, name, and email**: The name and email of the user associated with the source peer. The API looks these up when you query it; [streamed events](#streamed-event-fields) carry the user ID only.
|
||||
* **Reporter ID**: The unique identifier of the peer that reported the traffic event.
|
||||
* **Received timestamp**: The timestamp when the event was received by the NetBird servers.
|
||||
|
||||
@@ -352,6 +377,14 @@ On Linux, you can force a routing peer into userspace mode with three [environme
|
||||
sudo netbird service reconfigure --service-env NB_WG_KERNEL_DISABLED=true,NB_FORCE_USERSPACE_FIREWALL=true,NB_FORCE_USERSPACE_ROUTER=true
|
||||
```
|
||||
|
||||
### Traffic that is not recorded
|
||||
|
||||
Some traffic never produces a traffic event, whatever the peer mode:
|
||||
|
||||
* **DNS queries over UDP.** The NetBird client does not record UDP traffic to port `53`, or to ports `5353` and `22054`, which the NetBird DNS forwarder uses on routing peers. This also applies to other UDP traffic on these ports, such as mDNS. DNS over TCP is recorded as an ordinary flow.
|
||||
* **Traffic through an exit node.** Flows matched to an exit node route (`0.0.0.0/0`) are not recorded, on the client or on the exit node. Traffic to a destination covered by a more specific route or Networks resource is still recorded.
|
||||
* **Access withdrawn by a failed posture check.** When a peer fails a [posture check](/manage/access-control/posture-checks), the policy stops applying and the connection between the peers is removed. No traffic reaches either peer's firewall, so there is nothing to record as allowed or blocked.
|
||||
|
||||
## Conclusion
|
||||
Traffic events logging provides a powerful tool for monitoring and analyzing network traffic across your infrastructure.
|
||||
Enabling this feature can provide valuable insights into network activity, enhance security measures, and improve operational efficiency.
|
||||
|
||||
@@ -75,6 +75,8 @@ Then drag a **Group** onto the canvas for the access we are about to grant.
|
||||
|
||||
**Network** and **Resource** templates work the same way and integrate with [Networks](/manage/networks); connecting a policy to a network node asks which of its resources the policy should target.
|
||||
|
||||
When Agent Network is enabled, an **Agent Network** tab adds **Provider** and **Agent Policy** nodes; see [Agent Network in Control Center](/agent-network/control-center#build-access-in-draft-mode).
|
||||
|
||||
### Undo and Redo
|
||||
|
||||
`Ctrl/⌘+Z` undoes any canvas action, `Shift+Ctrl/⌘+Z` redoes it.
|
||||
|
||||
@@ -88,6 +88,10 @@ Use this to see who can access resources in your routed [networks](/manage/netwo
|
||||
- The network node shows its resources. Connecting lines display the port allowed by the policy and which groups have access.
|
||||
- Click any policy chip to edit it in the standard editor. NetBird Networks and routing peers enable access to private subnets and IP resources.
|
||||
|
||||
### Agent Network
|
||||
|
||||
When [Agent Network](/agent-network) is enabled for your account, the **Peers**, **Users**, and **Groups** views also show its providers and agent policies, so you can see which groups can reach which AI providers. You can edit providers and agent policies from the canvas and build new ones in Draft Mode. See [Agent Network in Control Center](/agent-network/control-center).
|
||||
|
||||
## Focus Mode
|
||||
|
||||
When the graph gets busy, focus a single node to see only what is connected to it. Right-click a node and choose **Focus**, or press `F` and then select the node.
|
||||
@@ -133,3 +137,4 @@ For changes that span several components, **Draft Mode** lets you stage the whol
|
||||
- [Apply posture checks to policies](/manage/access-control/posture-checks)
|
||||
- [Networks and routing peers](/manage/networks)
|
||||
- [MSP portal overview](/manage/for-partners/msp-portal)
|
||||
- [Agent Network in Control Center](/agent-network/control-center)
|
||||
|
||||
@@ -18,6 +18,8 @@ NetBird provides centralized DNS management to help your peers resolve domain na
|
||||
|
||||
Starting with [v0.11.0](https://github.com/netbirdio/netbird/releases), NetBird automatically assigns a domain name to each peer in a private `netbird.cloud` space. You can access machines using names like `my-server.netbird.cloud` instead of IP addresses.
|
||||
|
||||
When two peers in an account share a hostname, the second one gets an IP-based suffix such as `my-server-12-7.netbird.cloud` by default. You can make such registrations fail instead with the [Unique Peer Hostnames](/manage/settings/clients#unique-peer-hostnames) setting.
|
||||
|
||||
<Note>
|
||||
Available on both NetBird Cloud and self-hosted versions running v0.11.0 or later.
|
||||
</Note>
|
||||
|
||||
@@ -171,6 +171,16 @@ Use email subaddressing to create a user inside the tenant for your MSP engineer
|
||||
3. Click **Invite User** on the right.
|
||||
4. Enter the subaddressed email (e.g. `engineer+clientname@yourcompany.com`).
|
||||
|
||||
<Warning>
|
||||
The invitation email arrives in your own inbox, where your browser is likely still signed in to your MSP account. Opening the invitation link there signs you back in to the MSP account instead of accepting the invite. To accept it as the new user:
|
||||
|
||||
1. Copy the link from the invitation email's button and open it in a private browser window.
|
||||
2. Enter the subaddressed email and click **Continue**. Don't use Google, Microsoft, or another social login, because those sign you in with your main address.
|
||||
3. Set a password from the email you receive.
|
||||
|
||||
If you already tried to sign up with the subaddressed email and got **Invalid sign up**, the account exists already. Use **Forgot password** on the login page with the same address to set your password.
|
||||
</Warning>
|
||||
|
||||
This is the recommended approach as it gives the MSP engineer a proper identity inside the tenant. Use NetBird's [profile switching](/client/profiles) feature to easily switch between your MSP account and client tenant connections on the same device.
|
||||
|
||||
**Option 2: Add a Network and Routing Peer**
|
||||
|
||||
@@ -37,7 +37,7 @@ NetBird offers two ways to configure routing peers. Both are actively maintained
|
||||
## Mental model: how traffic flows
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/networks/netbird-network-routes.png" alt="Routing peer bridging NetBird overlay to a private network" className="imagewrapper-big"/>
|
||||
<img src="/docs-static/img/manage/networks/networks-overview.png" alt="Three NetBird peers on the overlay network connect through a WireGuard tunnel to a routing peer in a private network. Access policies on the routing peer allow a database on TCP 5432, a web app on TCP 443, and an RDP workstation on TCP/UDP 3389; a printer with no policy is unreachable." className="imagewrapper-big"/>
|
||||
</p>
|
||||
|
||||
The walkthrough below describes the **Linux kernel-mode** path, where forwarding and firewalling happen in the kernel via `nftables` / `iptables`. On other platforms, NetBird performs the same logical steps in a userspace filter, but the implementation differs.
|
||||
@@ -98,19 +98,45 @@ The routing peer must have direct network reachability to the resources it serve
|
||||
|
||||
## High availability
|
||||
|
||||
Multiple routing peers can serve the same network or route. Behavior depends on the **metric** you assign each routing peer in the dashboard. There are no tunable thresholds — the metric is the only high availability control.
|
||||
Multiple routing peers can serve the same network or route. Each peer that uses the network (a user's laptop, for example) picks one of those routing peers and sends all of that network's traffic through it until something makes it switch. The **metric** you assign each routing peer in the dashboard is the main control. There are no tunable thresholds.
|
||||
|
||||
### How a peer picks a routing peer
|
||||
|
||||
A peer compares the routing peers it is connected to, in this order:
|
||||
|
||||
1. **Metric.** The lowest metric wins, whatever the connection type or latency.
|
||||
2. **Connection type.** Among equal metrics, a direct (peer-to-peer) connection beats a relayed one.
|
||||
3. **Latency.** Among equal metrics and the same connection type, the lowest latency wins. To prevent flapping, a peer only moves to a routing peer whose latency is at least 10 ms lower than its current one.
|
||||
|
||||
Latency is measured once, when the connection to a routing peer is set up, and is not re-measured while that connection stays up.
|
||||
|
||||
### What makes a peer switch
|
||||
|
||||
A peer repeats this comparison whenever a routing peer's connection state changes or the network's configuration is updated. In practice it switches when:
|
||||
|
||||
- **Its routing peer goes offline.** The peer moves to the next routing peer in the order above, normally within seconds.
|
||||
- **A routing peer with a lower metric comes back online.** The peer moves back as soon as that routing peer reconnects, even while its connection is still relayed.
|
||||
- **Its routing peer's connection falls back from direct to relayed.** If another routing peer with the same metric still has a direct connection, the peer moves to it, although nothing went offline. With different metrics this does not happen, because metric outranks connection type.
|
||||
|
||||
### What a switch does to open connections
|
||||
|
||||
With masquerade on (the default), every switch resets established TCP connections through the network, whatever caused it: a failover, a failback, or a fallback to relayed. The new routing peer translates the traffic to a different source address, so applications such as SSH must reconnect. Expect the connection to stall for several seconds before the reset, while the peer notices the change. With masquerade off, connections are not translated and behave differently: see [High availability with masquerade off](/manage/networks/masquerade#high-availability-with-masquerade-off).
|
||||
|
||||
If users report dropped connections while no routing peer went offline, check whether the routing peers share a metric and whether their connections fall back to relayed. [Troubleshooting relayed connections](/help/troubleshooting-relayed-connections) covers why a connection is relayed.
|
||||
|
||||
### Primary / failover (different metrics)
|
||||
|
||||
The lower-metric peer carries all traffic. The higher-metric peer is held in reserve and only takes over when the primary becomes unreachable. Failover is automatic: clients start sending traffic through the standby once the primary is seen as unreachable, normally within seconds. When the primary comes back online, clients switch back to it. With masquerade on, the default, established TCP connections through the previous peer reset and applications must reconnect, because the standby translates them to a different source address. With masquerade off they are not translated, and behave differently: see [High availability with masquerade off](/manage/networks/masquerade#high-availability-with-masquerade-off).
|
||||
Use different metrics when you want every peer on one known routing peer, with the others in reserve. Peers stay on the primary even when its connection falls back to relayed, but they fail back to it as soon as it returns, which resets connections a second time with masquerade on. To give routing peers different metrics, add them to the Network individually; routing peers added as a group share one metric.
|
||||
|
||||
**Example.** Routing Peer A has a lower metric than Routing Peer B. When Peer A goes down, all traffic fails over to Peer B. When Peer A comes back online, all traffic switches back to Peer A.
|
||||
**Example.** Routing Peer A has a lower metric than Routing Peer B. Traffic uses Peer A, including while its connection is relayed. When Peer A goes down, traffic fails over to Peer B, and moves back as soon as Peer A reconnects.
|
||||
|
||||
### Latency switching (equal metrics)
|
||||
### Nearest routing peer (equal metrics)
|
||||
|
||||
When two routing peers share the same metric, each client picks the one with lower latency. A switch only happens when the latency difference exceeds **20 ms**, which prevents flapping between peers that are roughly equivalent.
|
||||
Use equal metrics when routing peers are spread across regions and you want each peer to use the closest one: for example, an EU-based and a US-based routing peer serving the same network, with users on both continents. Each peer picks the lowest-latency routing peer when it connects, though a direct connection still beats a relayed one whatever the latency. It does not move to another routing peer later just because latency changes, and routing peers within 10 ms of each other are effectively tied. The trade-off: a fallback to relayed can move peers to another routing peer and reset their connections.
|
||||
|
||||
Useful when routing peers are geographically distributed and you want each client to land on the closest one automatically — for example, an EU-based peer and a US-based peer serving the same network, with users on both continents.
|
||||
### Finding switches in the client log
|
||||
|
||||
The client logs each switch at the default log level as `New chosen route is …`, followed by the chosen routing peer's WireGuard public key and its score. The comparison behind the decision, including the score of the routing peer it kept, is logged only at the debug level.
|
||||
|
||||
### Failure domains
|
||||
|
||||
@@ -146,6 +172,10 @@ This is the subtlest part of the model and the source of most policy mistakes.
|
||||
- **Forward chain.** Packets routed *through* the peer to backend resources. Network resource policies (Networks) and ACL Groups (Routes) apply here.
|
||||
- **Input chain.** Packets destined to the routing peer's own IP. [Peer-to-peer policies](/manage/access-control/manage-network-access) apply here.
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/networks/through-or-to-the-routing-peer.png" alt="Two paths compared. Through the routing peer: a network resource policy, evaluated in the forward chain, lets a client reach a web app behind the routing peer on TCP 443. To the routing peer: a peer-to-peer policy, evaluated in the input chain, lets a client reach SSH on the routing peer's NetBird IP on TCP 22." className="imagewrapper-big"/>
|
||||
</p>
|
||||
|
||||
If users need to reach both the resources behind a routing peer **and** services running on the routing peer itself (Pi-hole, monitoring, jump-host SSH), you need one policy of each kind.
|
||||
|
||||
**Example.** A routing peer fronts the `10.10.0.0/16` office network and also runs Grafana on TCP/3000 plus SSH on TCP/22. To let the `Engineers` group reach both, create two policies:
|
||||
|
||||
@@ -21,10 +21,10 @@ Throughout this page, a capitalized **Network** means the configuration object y
|
||||
If you remember one thing, remember this: **a NetBird Network is a map of part of your infrastructure, drawn onto your overlay network.** The **resources** are the destinations, the **access policies** are the gatekeepers that decide who may pass, and the **routing peers** are the roads that carry traffic to each destination.
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/networks/netbird-network-routes.png" alt="A NetBird client reaching resources on a private network through a routing peer" className="imagewrapper-big"/>
|
||||
<img src="/docs-static/img/manage/networks/networks-overview.png" alt="Three NetBird peers on the overlay network connect through a WireGuard tunnel to a routing peer in a private network. Access policies on the routing peer allow a database on TCP 5432, a web app on TCP 443, and an RDP workstation on TCP/UDP 3389; a printer with no policy is unreachable." className="imagewrapper-big"/>
|
||||
</p>
|
||||
|
||||
*Left to right: a peer in a source group clears an access policy, reaches the routing peer, and connects to a resource behind it.*
|
||||
*Left to right: peers reach the routing peer through a WireGuard tunnel, and access policies on the routing peer decide which resources behind it each peer can reach. The printer has no policy, so nothing reaches it.*
|
||||
|
||||
## The four building blocks
|
||||
|
||||
@@ -90,7 +90,7 @@ A network resource policy grants access to the network **behind** the routing pe
|
||||
Here is the whole path, end to end:
|
||||
|
||||
1. Your client encrypts the packet and sends it through the tunnel to the routing peer.
|
||||
2. The routing peer forwards it onto the local network toward the resource.
|
||||
2. The routing peer decrypts the packet and forwards it onto the local network toward the resource.
|
||||
3. By default the routing peer **masquerades** the traffic, rewriting the source to its own LAN IP, so the destination network needs no awareness of NetBird.
|
||||
4. Replies follow the reverse path. The connection is stateful, so return traffic needs no extra policy.
|
||||
|
||||
@@ -100,13 +100,9 @@ One subtlety causes most policy mistakes. A network resource policy permits traf
|
||||
|
||||
If a resource stays unreachable even though the policy looks right, [Troubleshooting resource connectivity](/help/troubleshooting-resource-connectivity) helps you find which layer is dropping the traffic.
|
||||
|
||||
```
|
||||
THROUGH the routing peer → forward chain → network resource policy
|
||||
your client ──tunnel──► routing peer ──────► resource behind it
|
||||
|
||||
TO the routing peer → input chain → peer-to-peer policy
|
||||
your client ──tunnel──► routing peer (its own SSH, dashboard, …)
|
||||
```
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/networks/through-or-to-the-routing-peer.png" alt="Two paths compared. Through the routing peer: a network resource policy, evaluated in the forward chain, lets a client reach a web app behind the routing peer on TCP 443. To the routing peer: a peer-to-peer policy, evaluated in the input chain, lets a client reach SSH on the routing peer's NetBird IP on TCP 22." className="imagewrapper-big"/>
|
||||
</p>
|
||||
|
||||
<Note>
|
||||
For the full kernel-level traffic walkthrough, host requirements (IP forwarding, container capabilities), masquerade internals, and the forward-vs-input-chain rule in depth, see [How Routing Peers Work](/manage/networks/how-routing-peers-work).
|
||||
@@ -165,7 +161,7 @@ This single policy grants the `Development` group access to **every** resource i
|
||||
|
||||
Before you depend on a Network in production, work through these:
|
||||
|
||||
- **High availability.** Add more than one routing peer to the same Network for redundancy. Added individually, each peer gets its own metric: a lower metric is the primary and a higher one the failover, while equal metrics balance traffic by latency. Added as a group, the peers share one metric, so they balance by latency only and can't act as primary and failover. Keep highly available peers in different failure domains. See [High availability](/manage/networks/how-routing-peers-work#high-availability).
|
||||
- **High availability.** Add more than one routing peer to the same Network for redundancy. Added individually, each routing peer gets its own metric: the lowest is the primary and higher ones are failovers. With equal metrics, each peer picks one when it connects, by connection type and then latency; this is not load balancing. Added as a group, the routing peers share one metric, so they can't act as primary and failover. Keep highly available peers in different failure domains. See [High availability](/manage/networks/how-routing-peers-work#high-availability).
|
||||
- **Monitoring.** Enable the **Routing Peer Disconnected** event in [Notifications](/manage/settings/notifications) to get alerted by email, webhook, or Slack when a routing peer goes offline.
|
||||
- **Masquerade.** On by default and the simplest option. Turn it off only when you need source-IP visibility, and only on Linux routing peers, as that's the only platform where it can be disabled. Disabling it requires a return route in the destination network, and makes high availability something you have to arrange rather than something you get. See [Masquerade](/manage/networks/masquerade).
|
||||
- **Internal DNS.** Domain resources resolve on the routing peer, so it must be able to resolve the name. If it already can, nothing more is needed; if it can't, distribute a nameserver to the routing peer's group. See [Internal DNS Servers](/manage/dns/internal-dns-servers).
|
||||
|
||||
@@ -8,7 +8,7 @@ When enabled, devices connect to the management service without network access t
|
||||
Administrators then can assess whether the peer is eligible to join the network.
|
||||
|
||||
<Note>
|
||||
This feature is only available in the [NetBird cloud](https://app.netbird.io/) version.
|
||||
This feature is available in [NetBird Cloud](https://app.netbird.io/) on the [Business plan](https://netbird.io/pricing) and in self-hosted deployments with an [Enterprise Commercial License](/selfhosted/enterprise). The open source Community Edition does not include it.
|
||||
</Note>
|
||||
## Related Video Content
|
||||
|
||||
|
||||
@@ -22,12 +22,14 @@ When lazy connections are enabled, the client:
|
||||
The default inactivity threshold is `15m`. Change it with `NB_LAZY_CONN_INACTIVITY_THRESHOLD`, using a [Go duration](https://pkg.go.dev/time#ParseDuration) such as `30m` or `1h`. The minimum is `1m`; shorter or invalid values fall back to the default.
|
||||
|
||||
<Note>
|
||||
The first request to an idle peer can take slightly longer while NetBird establishes the connection.
|
||||
The first request to an idle peer can take slightly longer while NetBird establishes the connection. Since NetBird v0.74.0, the packet that triggers the connection is delivered once the connection is up instead of being dropped.
|
||||
</Note>
|
||||
|
||||
### DNS warm-up
|
||||
|
||||
When the local NetBird resolver returns an A or AAAA record for an idle peer, it starts waking that peer before the application sends its first packet. The resolver waits for up to two seconds by default for one matching peer to connect, reducing the chance that the application's first request races the lazy connection.
|
||||
When the local NetBird resolver answers with two or more A or AAAA records that point at idle peers, it wakes those peers before the application sends its first packet. The answer waits up to two seconds by default for one of them to connect, so the first request does not race the connection.
|
||||
|
||||
Warm-up only applies to names in [custom DNS zones](/manage/dns/custom-zones) and in the zones NetBird creates for [private services](/manage/reverse-proxy/authentication#net-bird-only-access-private-services). A name with a single record does not trigger it, and neither does a peer's own name, such as `peer-a.netbird.cloud`. Peer names are excluded on purpose: otherwise every lookup would wake idle connections across the network.
|
||||
|
||||
Set `NB_DNS_LAZY_WARMUP_TIMEOUT` on the daemon to change this per-query wait. The value must be a positive Go duration, for example `5s`. Invalid, zero, or negative values fall back to the `2s` default.
|
||||
|
||||
@@ -55,14 +57,36 @@ sudo netbird service reconfigure --service-env NB_LAZY_CONN=on
|
||||
sudo netbird service reconfigure --service-env NB_LAZY_CONN=off
|
||||
```
|
||||
|
||||
`on` and `off` override Management in both directions; boolean values such as `true`/`false` or `1`/`0` are also accepted. Leave the variable unset to follow the Management setting. See [Client Environment Variables](/client/environment-variables#ice-and-connectivity) for service configuration details.
|
||||
`on` and `off` override Management in both directions; boolean values such as `true`/`false` or `1`/`0` are also accepted. Leave the variable unset to follow the Management setting. Any other value logs a warning and is treated as unset. See [Client Environment Variables](/client/environment-variables#ice-and-connectivity) for service configuration details.
|
||||
|
||||
On MDM-managed clients, the boolean `lazyConnection` policy key provides the same local override: `true` forces lazy connections on, `false` forces them off, and an absent key defers to Management. If both are configured, `NB_LAZY_CONN` takes precedence over MDM.
|
||||
|
||||
To check a peer, run `netbird status` on it. The `Lazy connection` line shows whether lazy connections are on for that peer, with any override applied.
|
||||
|
||||
<Note>
|
||||
The deprecated `NB_ENABLE_EXPERIMENTAL_LAZY_CONN` variable is no longer used. The deprecated `netbird up --enable-lazy-connection` flag is also inert in v0.75. Use the Management setting, `NB_LAZY_CONN`, or the MDM policy instead.
|
||||
NetBird v0.74.1 removed the `Enable Lazy Connections` checkbox from the desktop client's `Settings` menu and made the `netbird up --enable-lazy-connection` flag inert; the flag now only prints a deprecation warning. Both could turn lazy connections on, but neither could turn them off once Management had enabled them. `NB_LAZY_CONN` replaces both. The deprecated `NB_ENABLE_EXPERIMENTAL_LAZY_CONN` variable is no longer used.
|
||||
</Note>
|
||||
|
||||
### Each peer decides for its own connections
|
||||
|
||||
Whether it comes from Management or from `NB_LAZY_CONN`, the setting only controls the peer it applies to. A peer with lazy connections off connects to every peer your policies let it reach, and a lazy peer accepts that connection when asked. So turning lazy connections on for a routing peer does not make the devices that use it lazy.
|
||||
|
||||
An unused connection only stays closed when both of its ends are lazy. If only one end is lazy, that end closes the connection after the inactivity threshold, the other end reopens it shortly afterwards, and the cycle repeats. This is also what happens between every lazy peer and a peer you opt out with `NB_LAZY_CONN=off` while the account has lazy connections on. Keep two limits in mind:
|
||||
|
||||
- For a few seconds after each close, the non-lazy end still treats the connection as up, and traffic it sends in that window is dropped.
|
||||
- A lazy peer running kernel WireGuard does not detect idle connections itself, so its connection to a non-lazy peer stays up instead of cycling.
|
||||
|
||||
### Try lazy connections on a few peers
|
||||
|
||||
To try lazy connections before turning them on for the whole account, leave the Management setting off and set `NB_LAZY_CONN=on` on every peer in the test, for example two peers that talk to each other:
|
||||
|
||||
```bash
|
||||
# On both peers
|
||||
sudo netbird service reconfigure --service-env NB_LAZY_CONN=on
|
||||
```
|
||||
|
||||
The connection between the two then opens on demand and closes after the inactivity threshold. Their connections to other peers keep closing and reopening, as described above, because the other end is not lazy. When the test is over, turn the Management setting on or remove `NB_LAZY_CONN` from the test peers.
|
||||
|
||||
## Get started
|
||||
<div>
|
||||
<Button name="button" className="button-5" onClick={() => window.open("https://netbird.io/pricing")}>Use NetBird</Button>
|
||||
|
||||
@@ -14,6 +14,8 @@ This division of security responsibilities creates a comprehensive zero trust im
|
||||
|
||||
In this hands-on tutorial, you'll learn how to deploy NetBird with Intune to grant tailored access permissions for different teams.
|
||||
|
||||
This page installs the NetBird client. To enforce its settings once it is installed, such as pinning the management server or making settings read-only, follow [Enforce NetBird Settings on Windows](/manage/peers/mdm-deployment/windows-mdm-policy), which uses an Intune configuration profile.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before beginning this tutorial, ensure you have the following prerequisites in place:
|
||||
@@ -185,7 +187,7 @@ Click `Next` to configure NetBird with the following details:
|
||||
- **Description**: NetBird
|
||||
- **Publisher**: NetBird
|
||||
- **App install context**: Device
|
||||
- **Ignore app version**: No (This ensures updates will be applied when available)
|
||||
- **Ignore app version**: No if Intune owns updates (so Intune applies new MSI versions). Yes if NetBird owns updates via [Automatic Updates](/manage/peers/auto-update), so Intune only checks that NetBird is installed. Pick one owner: see [Roll Out with MDM](/use-cases/deployment/mdm-rollout#step-5-pick-one-owner-for-updates).
|
||||
- **Command-line arguments**: Leave empty. The v0.75 MSI has no `AUTOSTART` property; manage launch at login with the `disableAutostart` MDM setting instead.
|
||||
- **Category**: Select any category that fits your needs (optional)
|
||||
- **Show this as a featured app in the Company Portal**: Yes
|
||||
@@ -219,4 +221,8 @@ To verify that NetBird was added to Intune, navigate to `Home > Apps | Windows`
|
||||
|
||||
While each platform has slightly different configuration options, adding NetBird and assigning it to groups follows the same pattern across Intune. For more information, refer to [Intune app management](https://learn.microsoft.com/en-us/intune/intune-service/apps/app-management).
|
||||
|
||||
With NetBird successfully deployed through Intune, your organization has the foundation for implementing a comprehensive zero trust access model that verifies user identity and device compliance before granting network access.
|
||||
With NetBird successfully deployed through Intune, your organization has the foundation for implementing a comprehensive zero trust access model that verifies user identity and device compliance before granting network access.
|
||||
|
||||
## Next: enforce the client's settings
|
||||
|
||||
With the client installed, use an Intune configuration profile to pin its settings, such as the management server, and to stop users changing them: see [Enforce NetBird Settings on Windows](/manage/peers/mdm-deployment/windows-mdm-policy).
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import { Note, Warning } from "@/components/mdx";
|
||||
|
||||
export const description =
|
||||
"Deliver NetBird MDM policy to iOS, iPadOS, and tvOS devices with Managed App Configuration: the payload, where to put it in Intune, Jamf Pro, and other MDMs, when a change reaches the tunnel, and what each key does in the app.";
|
||||
|
||||
# Enforce NetBird Settings on iOS
|
||||
|
||||
On iOS there is no file or registry for policy. Apple's channel for configuring a managed app is **Managed App Configuration**: your MDM sends a dictionary along with the app, and iOS hands it to that app only. The NetBird app reads it from its own preferences, so the payload must target the NetBird app's bundle id: `io.netbird.app` on iOS and iPadOS, `io.netbird.app.tv` on tvOS.
|
||||
|
||||
What each key does, and how the client applies and locks a policy, is on the [MDM Integration](/client/mdm-integration) page. Read its [How the client applies a policy](/client/mdm-integration#how-the-client-applies-a-policy) section first if you have not.
|
||||
|
||||
The examples use [the user device baseline](/client/mdm-integration#the-user-device-baseline): a self-hosted server at `https://netbird.example.com:443`, read-only settings, and no extra profiles.
|
||||
|
||||
<Note>
|
||||
**The app must be managed by the MDM that sends the configuration.** It does not have to be installed by it: an MDM can take over management of a copy the user installed from the App Store, and on an unsupervised device the user has to accept that change. Until the app is managed it receives no app configuration, and NetBird runs with no policy on that device.
|
||||
</Note>
|
||||
|
||||
## Push the payload
|
||||
|
||||
The payload is a plain property-list dictionary, with the same key names as on desktop. The user device baseline is:
|
||||
|
||||
```xml
|
||||
<dict>
|
||||
<key>managementURL</key>
|
||||
<string>https://netbird.example.com:443</string>
|
||||
<key>disableUpdateSettings</key>
|
||||
<true/>
|
||||
<key>disableProfiles</key>
|
||||
<true/>
|
||||
</dict>
|
||||
```
|
||||
|
||||
Start from <a href="/docs-static/files/netbird-ios-appconfig.plist" download>netbird-ios-appconfig.plist</a> and delete the keys you do not want to pin: a key that is present is pinned, even when its value is `false`. Then add it to the NetBird app's assignment in your MDM:
|
||||
|
||||
- **Microsoft Intune**: **Apps → App configuration policies → Add → Managed devices**, platform **iOS/iPadOS**, pick the NetBird app, then set **Configuration settings format** to **Enter XML data** and paste the dictionary.
|
||||
- **Jamf Pro**: **Devices → Mobile Device Apps → NetBird → App Configuration**, and paste the dictionary.
|
||||
- **Kandji**, **Mosyle**, **Workspace ONE**, **JumpCloud**: the same field exists on the managed-app assignment, usually labelled *App Configuration* or *Application Configuration*.
|
||||
|
||||
Booleans can be written as `<true/>` and `<false/>`, or as `<integer>1</integer>` and `<integer>0</integer>`. Integers and strings behave as on desktop.
|
||||
|
||||
## When a policy takes effect
|
||||
|
||||
Every key that applies to iOS is enforced: the keys that reshape the app, and the keys the client applies to the connection itself. When a change lands depends on one detail of how iOS works.
|
||||
|
||||
iOS delivers the configuration to the NetBird app. The tunnel, though, runs in a Network Extension: a separate process the system starts when the tunnel comes up and stops when it goes down, and which Apple's app-configuration channel does not reach. The app therefore passes the configuration on to the extension whenever the app is running: when it becomes active, when a settings screen appears, and on a timer about every 30 seconds. When the policy has changed, the tunnel restarts on its own and the user is told their IT policy was applied.
|
||||
|
||||
<Note>
|
||||
**A policy lands while the app is open.** With NetBird in the foreground, a change pushed from your MDM reaches the tunnel within about 30 seconds, or at once when the user switches back to the app. Nothing has to be restarted by hand.
|
||||
|
||||
**While the app is closed or in the background, iOS suspends it**, and a policy pushed or withdrawn in that time reaches the tunnel only the next time the app is opened. This matters most with VPN On Demand or the widget, where the tunnel can come up without the app ever appearing: it then runs on the last policy the app saw. Plan rollouts accordingly, and treat "applied" as "applied once the user next opens the app".
|
||||
</Note>
|
||||
|
||||
<Warning>
|
||||
**On tvOS, only the app is enforced, not the tunnel.** The hand-off described above relies on a shared app group, which does not work between the tvOS app and its extension, so the tvOS tunnel runs without the policy. Keys that lock or hide controls behave as described below; the keys the client applies to the connection (`managementURL`, `preSharedKey`, `rosenpassEnabled`, `rosenpassPermissive`, `blockInbound`, `wireguardPort`, `lazyConnection`) do not reach the tunnel on tvOS yet.
|
||||
</Warning>
|
||||
|
||||
## What each key does in the app
|
||||
|
||||
| Key | Effect in the iOS and tvOS app |
|
||||
| --- | --- |
|
||||
| `managementURL` | Hides **Settings → Connection** and the server step of onboarding. The profile-creation sheet shows and uses the pinned URL. |
|
||||
| `preSharedKey` | The key row reads *Configured* and is locked: no Save, no Remove. The value itself never reaches the app's interface. |
|
||||
| `rosenpassEnabled`, `rosenpassPermissive` | Locks its own switch and shows the pinned value. |
|
||||
| `disableClientRoutes` | Hides the exit-node selector. The resource list explains why, instead of listing resources. |
|
||||
| `disableAutoConnect` | VPN On Demand becomes read-only. |
|
||||
| `disableProfiles` | Hides the Profiles section: no creating, switching, or removing profiles. |
|
||||
| `disableNetworks` | Removes the Resources tab. |
|
||||
| `disableAdvancedView` | Hides Advanced (on tvOS, the whole section). |
|
||||
| `disableUpdateSettings` | Read-only mode: nothing is hidden, and every configuration control is locked. |
|
||||
|
||||
A second group of keys has no control to lock, because the client applies them straight to the connection: `blockInbound`, `wireguardPort`, `lazyConnection`, `allowRemoteJobs`, and `debugBundleUploadURL` (when a remote job produces a bundle). On iOS they are enforced all the same, with nothing on screen to mark: a device whose policy sets `allowRemoteJobs: false`, for example, refuses remote jobs. On tvOS none of them reach the tunnel, for the reason in the warning above.
|
||||
|
||||
The remaining keys do not apply to iOS and are ignored, so they are safe in a payload shared with desktop devices: `allowServerSSH` (there is no SSH server on iOS), `disableServerRoutes` (an iOS device does not route for other peers), `disableAutostart` (desktop app only), `disableMetricsCollection`, `enableLocalMetrics` and `localMetricsAddress` (the local endpoint is not reachable on iOS), and `splitTunnelMode` and `splitTunnelApps`.
|
||||
|
||||
## Verifying on a device
|
||||
|
||||
There is no CLI on iOS. A pinned setting shows a lock and a **Managed by your organization** note in the app, and a gated section disappears altogether. For the user device baseline, the Profiles section is gone and every configuration control is locked.
|
||||
|
||||
That confirms the app received the policy. To confirm it reached the *tunnel*, change the policy while the tunnel is connected and bring NetBird to the foreground: the tunnel restarts and the device shows "NetBird configuration was updated by your IT policy". That notice comes from the extension, so seeing it means the tunnel applied the new policy.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Nothing is locked in the app.**
|
||||
The app did not receive the configuration. Check that NetBird is a managed app on the device (installed by your MDM, or taken over by it), that the app-configuration policy is assigned to the same device group as the app, and that it targets the bundle id `io.netbird.app` (`io.netbird.app.tv` on tvOS).
|
||||
|
||||
**The app shows the policy, but the tunnel still behaves as before.**
|
||||
The app passes the policy to the tunnel only while it is running. Open NetBird and leave it in the foreground for half a minute; the tunnel restarts and reports that the policy was applied. On tvOS the tunnel does not receive the policy at all; see [When a policy takes effect](#when-a-policy-takes-effect).
|
||||
|
||||
For problems that look the same on every platform, see [Troubleshooting](/client/mdm-integration#troubleshooting) on the MDM Integration page.
|
||||
|
||||
## Recap
|
||||
|
||||
- iOS delivers the policy as Managed App Configuration to the managed NetBird app, bundle id `io.netbird.app` (tvOS: `io.netbird.app.tv`).
|
||||
- The user device baseline is the same three keys as on desktop: `managementURL`, `disableUpdateSettings`, and `disableProfiles`.
|
||||
- A change reaches the tunnel while the app is open, within about 30 seconds; on tvOS it reaches the app only.
|
||||
- Confirm in the app: pinned settings show **Managed by your organization**.
|
||||
@@ -0,0 +1,127 @@
|
||||
import { Note, Warning } from "@/components/mdx";
|
||||
|
||||
export const description =
|
||||
"Deliver NetBird MDM policy to macOS devices: a configuration profile for the io.netbird.client preference domain, with steps for Jamf Pro, Kandji, Mosyle, Intune, Workspace ONE, and JumpCloud, and how to confirm it arrived.";
|
||||
|
||||
# Enforce NetBird Settings on macOS
|
||||
|
||||
On macOS, the NetBird client reads its MDM policy from `/Library/Managed Preferences/io.netbird.client.plist`. Normally you do not write that file yourself: macOS writes it when your MDM installs a configuration profile with managed preferences for the preference domain (bundle id) `io.netbird.client`. This page shows how to build that profile, deliver it with the common MDMs, and confirm it arrived.
|
||||
|
||||
What each key does, and how the client applies and locks a policy, is on the [MDM Integration](/client/mdm-integration) page. Read its [How the client applies a policy](/client/mdm-integration#how-the-client-applies-a-policy) section first if you have not.
|
||||
|
||||
The examples use [the user device baseline](/client/mdm-integration#the-user-device-baseline): a self-hosted server at `https://netbird.example.com:443`, read-only settings, and no extra profiles. As managed preferences, the whole policy is:
|
||||
|
||||
```xml
|
||||
<key>managementURL</key>
|
||||
<string>https://netbird.example.com:443</string>
|
||||
<key>disableUpdateSettings</key>
|
||||
<true/>
|
||||
<key>disableProfiles</key>
|
||||
<true/>
|
||||
```
|
||||
|
||||
<Warning>
|
||||
**macOS clears `/Library/Managed Preferences/` at every boot on a Mac that is not enrolled in an MDM.** Writing the file by hand, with `defaults write` or a script, works for a quick test, but the policy is gone after the next reboot. Use a configuration profile delivered by an MDM for anything beyond a test.
|
||||
</Warning>
|
||||
|
||||
## Templates
|
||||
|
||||
Download the template that matches your MDM:
|
||||
|
||||
- <a href="/docs-static/files/netbird-macos.mobileconfig" download>netbird-macos.mobileconfig</a>: a complete configuration profile. Use it with Kandji, Mosyle, Intune, Workspace ONE, JumpCloud, and Apple Configurator.
|
||||
- <a href="/docs-static/files/io.netbird.client.plist" download>io.netbird.client.plist</a>: the bare managed-preferences dictionary, without the profile wrapper. Use it with Jamf Pro's Application & Custom Settings payload.
|
||||
- <a href="/docs-static/files/netbird-macos.sh" download>netbird-macos.sh</a>: a script that writes the policy file directly, for JumpCloud fleets that are not MDM-enrolled.
|
||||
|
||||
Each template lists every key. Delete the keys you do not want to pin: a key that is present is pinned, even when its value is `false`.
|
||||
|
||||
Write booleans as `<true/>` or `<false/>`. On macOS, a boolean written as `<integer>1</integer>` is listed as managed and locked, but never applied, so the setting is frozen at whatever the Mac had.
|
||||
|
||||
## Build the configuration profile
|
||||
|
||||
1. Open `netbird-macos.mobileconfig` in a text editor, or in a profile editor such as [iMazing Profile Editor](https://imazing.com/profile-editor) or [ProfileCreator](https://github.com/ProfileCreator/ProfileCreator).
|
||||
2. Inside the `mcx_preference_settings` dictionary, keep only the keys you want to pin, with your values. For the user device baseline, that is the three keys shown at the top of this page. Keep `io.netbird.client` as the preference domain.
|
||||
3. Replace each placeholder `PayloadUUID` with a freshly generated UUID (run `uuidgen`), so every deployment has unique identifiers.
|
||||
|
||||
Profiles delivered through an MDM are signed by the MDM, so there is nothing to sign by hand for a managed rollout. A profile you hand out some other way, with Apple Configurator or as a file a user double-clicks, installs unsigned, and recent macOS releases ask the user for an extra confirmation. To avoid that, sign the profile as a CMS message:
|
||||
|
||||
```bash
|
||||
security cms -S -N "<signing identity>" -i netbird-macos.mobileconfig -o signed.mobileconfig
|
||||
```
|
||||
|
||||
Use an identity the target Macs already trust. Configuration profiles are **not** signed with `productsign`, which is for installer packages, nor with an Apple Developer ID certificate.
|
||||
|
||||
## Deliver it with your MDM
|
||||
|
||||
- **Jamf Pro**: **Computers → Configuration Profiles → New → Application & Custom Settings → External Applications → Upload File (Plist file)**, with the preference domain `io.netbird.client`, and upload the edited `io.netbird.client.plist`. Jamf can also take the whole `.mobileconfig` through **Computers → Configuration Profiles → Upload**, but it may drop payload keys it does not recognize from an unsigned profile, so sign the profile first if you use that route.
|
||||
- **Kandji**: add a **Custom Profile** library item and upload the `.mobileconfig`.
|
||||
- **Mosyle**: **Management → Management Profiles → Certificates / Custom Profiles → Add new profile**, and upload the `.mobileconfig`.
|
||||
- **Microsoft Intune**: **Devices → Configuration → Create profile → macOS → Templates → Custom**, and upload the `.mobileconfig`.
|
||||
- **Workspace ONE** and other MDMs: upload the `.mobileconfig` as a custom configuration profile and scope it to the target device group.
|
||||
- **Apple Configurator 2**, for testing on a connected Mac without an MDM: drag the `.mobileconfig` onto the device and install it.
|
||||
|
||||
### JumpCloud
|
||||
|
||||
JumpCloud can deliver the policy two ways. Use the first if your Macs are MDM-enrolled with JumpCloud.
|
||||
|
||||
#### MDM Custom Configuration Profile (MDM-enrolled Macs)
|
||||
|
||||
1. In the JumpCloud admin console, open **Policy Management → Policies → +** and choose the **Mac** platform.
|
||||
2. Pick the **MDM Custom Configuration Profile** policy template.
|
||||
3. Under **Settings**, upload your edited `netbird-macos.mobileconfig` (see [Build the configuration profile](#build-the-configuration-profile)).
|
||||
4. Bind the policy to the target device group and save.
|
||||
|
||||
JumpCloud delivers the profile through MDM. Removing the policy in JumpCloud removes the file at the next sync, which unpins the settings on the client.
|
||||
|
||||
#### Shell command (Macs not enrolled in MDM)
|
||||
|
||||
For JumpCloud-managed Macs that are not MDM-enrolled, use <a href="/docs-static/files/netbird-macos.sh" download>netbird-macos.sh</a>:
|
||||
|
||||
1. Edit the `### POLICY VALUES ###` block at the top of the script: set the keys you want to pin, and leave the rest at `$NULL`.
|
||||
2. In the JumpCloud admin console, go to **Device Management → Commands → +**. Set **Type** to **Mac, Shell** and **Run as** to **root**.
|
||||
3. Paste the edited script into the command body.
|
||||
4. Bind the command to the target device group and run it.
|
||||
|
||||
The script writes `/Library/Managed Preferences/io.netbird.client.plist`, owned by `root:wheel` with mode `644`, and restarts the NetBird daemon so the policy applies at once. On a Mac that is not MDM-enrolled, macOS deletes the file at the next reboot, so run the command again after each reboot, or enroll the Macs and use the configuration profile.
|
||||
|
||||
<Note>
|
||||
**Every user on the Mac can read the policy file.** From a configuration profile, macOS writes it as a binary plist owned by `root:wheel` with mode `644`, and the shell script above uses the same owner and mode. It is readable by all users, so a `preSharedKey` or `debugBundleUploadURL` in it is visible to anyone who can sign in. Only root can change it. The client refuses to read a policy file that any user can write to, and then enforces no policy at all, so never loosen the file's permissions.
|
||||
</Note>
|
||||
|
||||
## Verify
|
||||
|
||||
On a target Mac, check that macOS wrote the policy file:
|
||||
|
||||
```bash
|
||||
sudo defaults read "/Library/Managed Preferences/io.netbird.client"
|
||||
```
|
||||
|
||||
The output should list the keys from your profile. Then check what the client is enforcing:
|
||||
|
||||
```bash
|
||||
netbird debug config
|
||||
```
|
||||
|
||||
The `mDMManagedFields` array in the output lists every key in the policy the client reads. For the user device baseline, it contains `disableProfiles`, `disableUpdateSettings`, and `managementURL`. The client log at `/var/log/netbird/client.log` has an `MDM enrolled with N managed key(s): [...]` line on every reload, and an `MDM policy changed: added=[...]` line once the daemon has applied a change, within a minute. See [Verifying enforcement](/client/mdm-integration#verifying-enforcement) for more.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**The policy file does not exist.**
|
||||
The profile was not installed, or it targets the wrong preference domain. On the Mac, open **System Settings → General → Device Management** and confirm the profile is listed, then confirm the domain in the profile is exactly `io.netbird.client`.
|
||||
|
||||
**The policy file disappears after a reboot.**
|
||||
The Mac is not MDM-enrolled, so macOS clears `/Library/Managed Preferences/` at boot. Enroll it and deliver the policy as a configuration profile.
|
||||
|
||||
**The file exists, but `mDMManagedFields` is empty.**
|
||||
Check the file's permissions with `ls -l "/Library/Managed Preferences/io.netbird.client.plist"`. If any user can write to it (a `w` in the last three permission characters), the client ignores the whole policy, and with default logging it says nothing about it. Reset the permissions with `sudo chown root:wheel` and `sudo chmod 644` on the file. If the permissions are fine, look in the client log for `MDM ignoring unknown plist key`: a key name does not match any key in the [reference](/client/mdm-integration#policy-keys-reference).
|
||||
|
||||
**A key is listed in `mDMManagedFields`, but the setting did not change.**
|
||||
Check the value's type. A boolean must be `<true/>` or `<false/>`; written as an `<integer>`, it is listed as managed and locked, but never applied. The client logs no warning for it.
|
||||
|
||||
For problems that look the same on every platform, see [Troubleshooting](/client/mdm-integration#troubleshooting) on the MDM Integration page.
|
||||
|
||||
## Recap
|
||||
|
||||
- The client reads `/Library/Managed Preferences/io.netbird.client.plist`, which macOS writes from a configuration profile for `io.netbird.client`.
|
||||
- The user device baseline is three keys: `managementURL`, `disableUpdateSettings`, and `disableProfiles`.
|
||||
- Upload `netbird-macos.mobileconfig` to most MDMs, JumpCloud included; Jamf Pro's Application & Custom Settings takes the bare `io.netbird.client.plist`.
|
||||
- Confirm with `sudo defaults read`, then `netbird debug config`.
|
||||
@@ -0,0 +1,171 @@
|
||||
import { Note, Warning } from "@/components/mdx";
|
||||
|
||||
export const description =
|
||||
"Enforce NetBird client settings on Windows with Microsoft Intune: import the NetBird administrative templates, build and assign a configuration profile, and verify it. Plus a registry reference for Group Policy, JumpCloud, and .reg files.";
|
||||
|
||||
# Enforce NetBird Settings on Windows
|
||||
|
||||
On Windows, you enforce NetBird settings with Microsoft Intune: import NetBird's administrative templates once, build a configuration profile from them, and assign it to your devices. The client then applies the settings and locks them. Behind the scenes, the profile writes values to one registry key, `HKLM\Software\Policies\NetBird`, which is what the client reads. The [registry reference](#registry-reference) at the end of this page covers that key directly, for Group Policy, JumpCloud, or a `.reg` file.
|
||||
|
||||
This page enforces settings on a client that is already installed. To install the client with Intune, follow [Deploying NetBird with Intune](/manage/peers/mdm-deployment/intune-netbird-integration) first. What each setting does, and how the client applies and locks a policy, is on the [MDM Integration](/client/mdm-integration) page.
|
||||
|
||||
The examples use [the user device baseline](/client/mdm-integration#the-user-device-baseline): a self-hosted server at `https://netbird.example.com:443`, read-only settings, and no extra profiles. In Intune that is three policies: **Management URL**, **Disable update settings**, and **Disable profiles**.
|
||||
|
||||
## Before you start
|
||||
|
||||
- An Intune admin account with at least the **Policy and Profile Manager** role.
|
||||
- Windows devices enrolled in Intune, with the NetBird client installed, in a device group you can assign to.
|
||||
- The NetBird templates: <a href="/docs-static/files/netbird.admx" download>netbird.admx</a> and <a href="/docs-static/files/netbird.adml" download>netbird.adml</a>. They cover every setting, have no dependency on other templates, and stay within Intune's limits for imported templates.
|
||||
|
||||
## Enforce settings with Intune
|
||||
|
||||
### Step 1: Import the NetBird templates
|
||||
|
||||
You do this once per tenant.
|
||||
|
||||
1. In the Intune admin center, go to **Devices → Manage devices → Configuration**, open the **Import ADMX** tab, and select **Import**.
|
||||
2. Upload `netbird.admx` as the ADMX file and `netbird.adml` as the ADML file for the default language (`en-us`, the only language Intune accepts).
|
||||
3. Select **Next**, then **Create**.
|
||||
4. Select **Refresh** until the template's status is **Available**.
|
||||
|
||||
<Note>
|
||||
**Updating the templates later.** Intune refuses to import a template whose settings are already imported. When NetBird ships updated templates, for example with new settings, delete the configuration profiles that use the NetBird template, delete the old template on the **Import ADMX** tab, and then import the new files. Note your profile settings before you delete them.
|
||||
</Note>
|
||||
|
||||
### Step 2: Create the configuration profile
|
||||
|
||||
1. Go to **Devices → Manage devices → Configuration → Create → New policy**.
|
||||
2. Set **Platform** to **Windows 10 and later** and **Profile type** to **Templates**, then select **Imported Administrative templates (Preview)** and **Create**.
|
||||
3. Give the profile a name you can find later, such as `NetBird: user device baseline`.
|
||||
4. Under **Configuration settings**, open **NetBird**. Each policy is named after its setting in plain words. For the user device baseline:
|
||||
- **Management URL**: **Enabled**, value `https://netbird.example.com:443`.
|
||||
- **Disable update settings**: **Enabled**.
|
||||
- **Disable profiles**: **Enabled**.
|
||||
|
||||
Leave every other policy **Not configured**. A policy set to **Disabled** is not the same as **Not configured**: it pins the setting to `false`, out of the user's reach. See [How the client applies a policy](/client/mdm-integration#how-the-client-applies-a-policy).
|
||||
5. Under **Assignments**, assign the profile to a **device group**. NetBird's policies are machine settings, so a device group is what you want. To build the profile for each device role, use [Recommended policies](/client/mdm-integration#recommended-policies), and keep routing peers out of any group that gets **Block inbound** or **Disable server routes**.
|
||||
6. Select **Create**.
|
||||
|
||||
### Step 3: Sync and verify
|
||||
|
||||
Devices pick up the profile at their next Intune check-in. To speed up a test device, open **Settings → Accounts → Access work or school**, select the work account, select **Info**, and then **Sync**. The NetBird client applies the policy within a minute of it arriving, and restarts its connection to do so: expect a brief interruption.
|
||||
|
||||
In the Intune admin center, open the profile and check its device and user check-in status. On the device itself:
|
||||
|
||||
```
|
||||
reg query HKLM\Software\Policies\NetBird
|
||||
netbird debug config
|
||||
```
|
||||
|
||||
`reg query` shows the values Intune wrote. In `netbird debug config`, the `mDMManagedFields` array lists every setting in the policy the client reads; for the user device baseline it contains `disableProfiles`, `disableUpdateSettings`, and `managementURL`. The client log at `C:\ProgramData\Netbird\client.log` has an `MDM policy changed: added=[...]` line once the client has applied the change. See [Verifying enforcement](/client/mdm-integration#verifying-enforcement) for more.
|
||||
|
||||
### If you cannot import templates: custom OMA-URI
|
||||
|
||||
Imported templates are the simpler path; prefer them. Without them, a custom OMA-URI profile (**Templates → Custom**) takes two kinds of setting, following Microsoft's [ADMX-backed policy ingestion](https://learn.microsoft.com/en-us/windows/client-management/win32-and-centennial-app-policy-configuration):
|
||||
|
||||
1. **Ingest the template**: OMA-URI `./Device/Vendor/MSFT/Policy/ConfigOperations/ADMXInstall/NetBird/Policy/NetBirdAdmx`, data type **String**, value: the full contents of `netbird.admx`. This only makes the policies known to the device; it sets nothing.
|
||||
2. **Set each policy**: OMA-URI `./Device/Vendor/MSFT/Policy/Config/NetBird~Policy~NetBird/<PolicyName>`, where `<PolicyName>` is the policy's `name` in `netbird.admx`, with data type **String**. The value depends on what you want to pin:
|
||||
- **On, or a value**: `<enabled/>`, plus a `<data id="..." value="..."/>` element for a policy that carries a value, with the element `id` from the same policy in `netbird.admx`.
|
||||
- **Off**: `<disabled/>`. For the on/off policies the template writes `0`, which pins the setting off. `<enabled/>` always writes `1`, so never use it to turn a setting off.
|
||||
|
||||
For the user device baseline's server, the **Management URL** policy is `.../NetBird~Policy~NetBird/ManagementURL` with the value `<enabled/><data id="ManagementURL_Text" value="https://netbird.example.com:443"/>`.
|
||||
|
||||
To stop pinning a setting, it has to go back to **Not configured**, not `<disabled/>`. Removing an OMA-URI from a custom profile is not guaranteed to clear the value on the device, so check with `reg query HKLM\Software\Policies\NetBird` afterwards.
|
||||
|
||||
The URIs above follow Microsoft's naming rules for this template; they have not been tested in an Intune tenant.
|
||||
|
||||
### Intune troubleshooting
|
||||
|
||||
**The template import fails.**
|
||||
If the error says the settings already exist, an earlier version of the NetBird template is imported: see **Updating the templates later** above. Otherwise, check that you uploaded `netbird.adml` with `netbird.admx`, as the `en-us` language file.
|
||||
|
||||
**The profile shows an error or never reaches the device.**
|
||||
Check the profile's per-device status in the admin center, and that the device is in the assigned device group. On the device, trigger a sync from **Settings → Accounts → Access work or school → Info → Sync**, then run `reg query HKLM\Software\Policies\NetBird`. If the key is empty, the profile has not been applied yet.
|
||||
|
||||
**The values are in the registry, but the setting did not change.**
|
||||
The client applies a change at its next reload, within a minute. If `mDMManagedFields` still does not list the setting, or lists it without the setting changing, see [Registry troubleshooting](#registry-troubleshooting).
|
||||
|
||||
For problems that look the same on every platform, see [Troubleshooting](/client/mdm-integration#troubleshooting) on the MDM Integration page.
|
||||
|
||||
## Registry reference
|
||||
|
||||
Whatever writes it, the client reads its policy from `HKLM\Software\Policies\NetBird`: an Intune profile, a Group Policy object, a script, or `reg import` all end in the same key. This section is for working with that key directly, and for checking what Intune wrote.
|
||||
|
||||
### Value types
|
||||
|
||||
Each setting needs a value of the right registry type. Value names are matched without regard to case.
|
||||
|
||||
| Registry type | Value names |
|
||||
| --- | --- |
|
||||
| `REG_SZ` (string) | `ManagementURL`, `PreSharedKey`, `DebugBundleUploadURL`, `LocalMetricsAddress`, `SplitTunnelMode`, `SplitTunnelApps` |
|
||||
| `REG_DWORD`, `0` or `1` (boolean) | `AllowRemoteJobs`, `AllowServerSSH`, `BlockInbound`, `DisableAdvancedView`, `DisableAutoConnect`, `DisableAutostart`, `DisableClientRoutes`, `DisableMetricsCollection`, `DisableNetworks`, `DisableProfiles`, `DisableServerRoutes`, `DisableUpdateSettings`, `EnableLocalMetrics`, `LazyConnection`, `RosenpassEnabled`, `RosenpassPermissive` |
|
||||
| `REG_DWORD` (integer) | `WireguardPort`, for example `0x0000ca6c` for `51820` |
|
||||
|
||||
A boolean can also be a `REG_SZ` holding `true`/`false`, `1`/`0`, `yes`/`no`, or `on`/`off`. A string setting stored with any type other than `REG_SZ` or `REG_EXPAND_SZ` is ignored: a `ManagementURL` stored as a `REG_DWORD`, for example, has no effect. The client reads `REG_SZ`, `REG_EXPAND_SZ`, `REG_DWORD`, `REG_QWORD`, and `REG_MULTI_SZ` values. Any other type, such as `REG_BINARY`, is skipped with a warning in its log and is not listed in `mDMManagedFields`.
|
||||
|
||||
<Note>
|
||||
**Standard users can read these values.** By default, the NetBird policy key inherits read access for Authenticated Users, so a `PreSharedKey` or `DebugBundleUploadURL` stored there is visible to them. Only administrators can change the values. An administrator on the device can always edit or delete the key, so give users standard accounts.
|
||||
</Note>
|
||||
|
||||
The user device baseline as raw registry values:
|
||||
|
||||
```
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[HKEY_LOCAL_MACHINE\Software\Policies\NetBird]
|
||||
"ManagementURL"="https://netbird.example.com:443"
|
||||
"DisableUpdateSettings"=dword:00000001
|
||||
"DisableProfiles"=dword:00000001
|
||||
```
|
||||
|
||||
### Group Policy
|
||||
|
||||
For domain-joined devices, follow [Deploying NetBird with Group Policy (GPO)](/manage/peers/mdm-deployment/windows-gpo-deployment). It walks through the Central Store, creating the policy GPO, installing the MSI silently, and scoping, end to end. The same `netbird.admx` and `netbird.adml` templates appear under **Computer Configuration → Administrative Templates → NetBird**.
|
||||
|
||||
To test the templates on a single machine with the local Group Policy editor:
|
||||
|
||||
1. Copy `netbird.admx` to `C:\Windows\PolicyDefinitions\` and `netbird.adml` to `C:\Windows\PolicyDefinitions\en-US\`.
|
||||
2. Open `gpedit.msc` and go to **Computer Configuration → Administrative Templates → NetBird**.
|
||||
3. Open a policy, such as **Management URL**, set it to **Enabled**, enter the value, and click **OK**.
|
||||
4. Run `gpupdate /force`.
|
||||
|
||||
### A `.reg` file
|
||||
|
||||
For a device without an MDM, or a quick test, carry the whole policy in one `.reg` file, such as the user device baseline above, and apply it from an elevated prompt with `reg import netbird-policy.reg`. To build one from a reference machine that already has the policy you want, export the key with `reg export "HKLM\Software\Policies\NetBird" netbird-policy.reg /y`.
|
||||
|
||||
<a href="/docs-static/files/netbird-policy.reg" download>netbird-policy.reg</a> is a sample that sets every setting, including `PreSharedKey` and `DisableClientRoutes`, which cut a laptop off from most of the network. Delete every line you do not mean to pin before you use it.
|
||||
|
||||
`reg import` adds and overwrites values, but never removes them. To take a setting out of the policy, delete its value with `reg delete "HKLM\Software\Policies\NetBird" /v <ValueName> /f`.
|
||||
|
||||
### JumpCloud
|
||||
|
||||
NetBird ships a companion script for JumpCloud, <a href="/docs-static/files/netbird-policy.reg.ps1" download>netbird-policy.reg.ps1</a>, that applies a `.reg` file as the complete policy for the device.
|
||||
|
||||
1. In the JumpCloud admin console, go to **Device Management → Commands → +**.
|
||||
2. Set **Type** to **Windows PowerShell** and **Run as** to **SYSTEM**.
|
||||
3. Paste `netbird-policy.reg.ps1` into the command body, unchanged.
|
||||
4. Attach your `.reg` file to the same command. JumpCloud copies attached files into the command's working directory before it runs the script.
|
||||
5. Bind the command to the target device group and run it.
|
||||
|
||||
The script deletes the existing `HKLM\Software\Policies\NetBird` key before it imports the `.reg` file, so the file is the **complete** policy for the device: any setting it does not contain is no longer pinned. To remove the whole policy, attach a `.reg` file that contains only the header line.
|
||||
|
||||
### Registry troubleshooting
|
||||
|
||||
**`reg query` shows no values.**
|
||||
Nothing wrote them. For Group Policy, run `gpresult /h report.html` and look for errors on the NetBird GPO. For Intune, see [Intune troubleshooting](#intune-troubleshooting).
|
||||
|
||||
**The values are in the registry, but `mDMManagedFields` does not list them.**
|
||||
Look for `MDM ignoring unknown registry value` in the client log: the value name does not match a setting. Then look for `MDM ignoring unsupported registry value type`: the value has a registry type the client does not read.
|
||||
|
||||
**A setting is listed in `mDMManagedFields`, but it did not change.**
|
||||
The value has the wrong type for its setting, such as a `ManagementURL` stored as a `REG_DWORD`. Check it against [Value types](#value-types).
|
||||
|
||||
**A setting stays pinned after you removed it from a `.reg` file.**
|
||||
`reg import` does not delete values. Delete the value with `reg delete`, or use the [JumpCloud](#jump-cloud) script, which replaces the whole key.
|
||||
|
||||
## Recap
|
||||
|
||||
- Import `netbird.admx` and `netbird.adml` into Intune once, build a configuration profile from **Imported Administrative templates**, and assign it to a device group.
|
||||
- The user device baseline enables three policies: **Management URL**, **Disable update settings**, and **Disable profiles**. Everything else stays **Not configured**.
|
||||
- Confirm in Intune, then on the device with `reg query` and `netbird debug config`.
|
||||
- Without Intune, the same settings are registry values under `HKLM\Software\Policies\NetBird`, written by Group Policy, JumpCloud, or a `.reg` file.
|
||||
@@ -77,7 +77,7 @@ On the machine you want to access via SSH, enable the NetBird SSH server.
|
||||
On a machine where you do not have those rights, which is the normal case for a
|
||||
managed workstation, you cannot enable the SSH server yourself: an administrator
|
||||
has to run the command. For the SSH server itself there is also
|
||||
[`allowServerSSH`](/client/mdm-integration#policy-keys-reference), which the client
|
||||
[`allowServerSSH`](/client/mdm-integration#allowServerSSH), which the client
|
||||
applies from MDM policy and so needs nothing from you; root login and SSH
|
||||
authentication have no policy key and must be set on the machine. A switch already in its safe
|
||||
state stays operable: you can turn the SSH server off, turn root login off, or
|
||||
|
||||
@@ -37,9 +37,10 @@ Every log entry (HTTP and L4) shares a common set of fields. Some fields are onl
|
||||
| **Bytes Downloaded** | Bytes sent from backend to client | Yes | Yes |
|
||||
| **Source IP** | The client's IP address | Yes | Yes |
|
||||
| **Location** | Country, city, and subdivision based on source IP geolocation | Yes | Yes |
|
||||
| **Auth Method** | Raw `auth_method_used` value: `oidc` (shown as SSO in the dashboard), `password`, `pin`, or `header`. For denied requests, carries the restriction code instead (e.g. `ip_restricted`, `crowdsec_ban`). Omitted from the API response when empty | Yes | Restriction code on denials |
|
||||
| **Auth Method** | Raw `auth_method_used` value: `oidc` (shown as SSO in the dashboard), `password`, `pin`, `header`, or a target access action such as `path_bypass` or `path_block`. For denied requests, carries the restriction code instead (e.g. `ip_restricted`, `crowdsec_ban`). Omitted from the API response when empty | Yes | Restriction code on denials |
|
||||
| **User** | The authenticated user's ID, set when `oidc` authentication was used. Omitted from the API response when empty | Yes | N/A |
|
||||
| **Reason** | Exactly one of two values: `Authentication failed` when authentication or an access restriction rejected the request, or `Request failed` when an authenticated request returned `4xx`/`5xx`. Set for HTTP entries only and omitted from the API response when empty. Never a specific denial code: see the note under [Deny reasons](#deny-reasons) | Yes | Omitted |
|
||||
| **Metadata** | Additional decision details. Target access records `access_action` as `bypass`, `block`, or `invalid_path` (an ambiguous request path rejected with `400`); CrowdSec records its mode and verdict here | Yes | Yes |
|
||||
|
||||
## Understanding log entries
|
||||
|
||||
@@ -47,8 +48,8 @@ Every log entry (HTTP and L4) shares a common set of fields. Some fields are onl
|
||||
|
||||
HTTP log entries fall into three categories based on the status code:
|
||||
|
||||
- **Allowed requests**: successful requests show a `2xx` status code along with the authentication method used to access the service.
|
||||
- **Denied requests**: failed authentication or access restriction blocks show `401` or `403` status codes with `reason` set to `Authentication failed`. The specific cause (invalid password, missing SSO session, IP restricted, country restricted, CrowdSec verdict) is carried in `auth_method_used`, not in `reason`.
|
||||
- **Allowed requests**: successful requests show a `2xx` status code along with the authentication method used to access the service. A target that bypasses authentication records `path_bypass`. The dashboard marks these entries with a small open-lock icon in the **User** column, at the right end of the row, labeled **Auth Bypassed** on hover.
|
||||
- **Denied requests**: failed authentication, access restriction blocks, and blocked target paths show `401` or `403` status codes with `reason` set to `Authentication failed`. The specific cause (invalid password, missing SSO session, blocked path, IP restricted, country restricted, CrowdSec verdict) is carried in `auth_method_used`, not in `reason`. A blocked target path records `path_block`. The dashboard marks these entries with a small shield icon in the **User** column, at the right end of the row, labeled **Path Blocked** on hover.
|
||||
- **Errors**: backend errors or proxy issues show `5xx` status codes. These typically indicate that the target service is unreachable or returned an error.
|
||||
|
||||
### L4 log entries
|
||||
@@ -63,6 +64,7 @@ The following deny reasons identify why a connection was rejected. Note that for
|
||||
|
||||
| Reason | Description |
|
||||
|--------|-------------|
|
||||
| `path_block` | The matching HTTP target is configured to block access |
|
||||
| `ip_restricted` | The client IP was blocked by a CIDR access restriction |
|
||||
| `country_restricted` | The client's country was blocked by a country access restriction |
|
||||
| `geo_unavailable` | Country restrictions are configured but the GeoIP database is unavailable (fail-closed) |
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {Note, Warning} from "@/components/mdx"
|
||||
|
||||
export const description =
|
||||
'Configure SSO, password, PIN, and header authentication methods, plus IP and country access restrictions, for NetBird Reverse Proxy services.'
|
||||
'Configure service authentication, per-target access actions, and IP, country, and reputation restrictions for NetBird Reverse Proxy services.'
|
||||
|
||||
# Reverse Proxy Authentication and Access Restrictions
|
||||
|
||||
NetBird Reverse Proxy supports multiple authentication methods and connection-level access restrictions to control who can access your exposed services. You can enable one or more methods on each service, or leave a service completely public. Authentication and access restrictions are configured per service in the **Authentication** tab when creating or editing a service.
|
||||
NetBird Reverse Proxy supports multiple authentication methods and connection-level access restrictions to control who can access your exposed services. You can enable one or more methods on each service, leave a service completely public, or choose a different authentication action for an individual HTTP target path. Authentication and access restrictions are configured when creating or editing a service.
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/reverse-proxy/authentication/reverse-proxy-add-service-auth.png" alt="Authentication tab showing all available authentication methods" className="imagewrapper"/>
|
||||
@@ -175,6 +175,59 @@ Services can also be configured without any authentication. When no authenticati
|
||||
|
||||
**Best for:** Public-facing websites, APIs that handle their own authentication internally, or services that are intentionally open to the internet.
|
||||
|
||||
## Per-target access actions
|
||||
|
||||
HTTP targets can override the service authentication behavior for their matching path. Open or add a target, expand **Optional Settings**, and choose **Access**:
|
||||
|
||||
| Action | Behavior |
|
||||
|--------|----------|
|
||||
| **Use service authentication** | Apply the authentication configured on the service. This is the default. |
|
||||
| **Bypass authentication** | Skip SSO, password, PIN, and header authentication for requests routed to this target. Service-level access restrictions still apply. |
|
||||
| **Block access** | Return `403` for requests routed to this target without contacting its backend. |
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/reverse-proxy/authentication/target-access-setting.png" alt="Edit Target modal with Optional Settings expanded, showing the Access dropdown and its three options: Use service authentication, Bypass authentication, and Block access" className="imagewrapper"/>
|
||||
</p>
|
||||
|
||||
For example, a service can require SSO on its `/` target, use **Bypass authentication** for `/public/`, and use **Block access** for `/admin/`. Target selection uses the longest matching, case-sensitive literal prefix. It does not support glob patterns or regular expressions. See [Path-based routing](/manage/reverse-proxy#path-based-routing) for matching examples and forwarding behavior.
|
||||
|
||||
The action only controls reverse proxy authentication for the selected target. **Bypass authentication** does not bypass CIDR, country, or CrowdSec restrictions configured on the service. It also does not change path rewriting; the existing **Preserve Full Path** setting continues to control whether the matched prefix is sent to the backend.
|
||||
|
||||
Targets that override the default are marked in target lists. Hover the access indicator to see its action.
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/reverse-proxy/authentication/target-access-badges.png" alt="Services list with a service expanded to show its three targets: / with no indicator, /public/ with an open-lock indicator whose tooltip reads Bypass authentication, and /admin/ with a red shield indicator for Block access" className="imagewrapper"/>
|
||||
</p>
|
||||
|
||||
### When to use per-target access actions
|
||||
|
||||
Most services need one rule for every path, so leave targets on **Use service authentication** unless a specific path has to behave differently from the rest of the service.
|
||||
|
||||
Use **Bypass authentication** when clients that cannot complete a login must reach a path, and the path either holds nothing sensitive or the backend authorizes requests itself:
|
||||
|
||||
- **Webhook receivers**, such as `/webhooks/`, where a third-party service posts events and the backend verifies the request signature.
|
||||
- **Health checks**, such as `/healthz`, polled by an external uptime monitor.
|
||||
- **Public assets**, such as `/public/` or `/static/`, that hold nothing sensitive.
|
||||
|
||||
Use **Block access** when a path should never be reachable through the public domain, even by users who pass service authentication, such as `/admin/` or `/metrics`. Reach those paths over NetBird instead, for example through a [NetBird-Only](#net-bird-only-access-private-services) service or a [Network resource](/manage/networks).
|
||||
|
||||
For **Block access**, prefer the broader prefix. A target on `/admin/` blocks `/admin/users` but not `/admin` itself, which falls through to the next matching target, such as the `/` target with service authentication. To block both, use `/admin`, which also matches `/administrator`.
|
||||
|
||||
Do not use **Bypass authentication** to make a whole application public. If every path should be public, create the service without authentication, so the service settings show its real exposure.
|
||||
|
||||
<Warning>
|
||||
**Bypass authentication makes the path public.** Anyone who can reach the service domain and passes its CIDR, country, and CrowdSec restrictions can request every path under the bypassed prefix. Requests arrive at the backend with no NetBird user identity, so the backend must authorize them on its own.
|
||||
|
||||
- Bypass the narrowest prefix that works. Prefixes match as text, so `/public` also matches `/public-admin`; end the path with `/` to match a single segment.
|
||||
- Disabling or deleting a **Block access** target removes its rule. Requests for that path then fall through to the next matching target, which may be a bypassed one.
|
||||
- **Custom Headers** configured on the target are still sent to the backend. Do not attach credentials to a bypassed target through headers.
|
||||
- When any target uses an access action, the proxy rejects ambiguous request paths, such as those with `..` segments, `//`, or encoded separators, with `400` before matching. This stops a request like `/public/../admin/` from reaching a protected path through a bypassed prefix.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
Per-target access actions are available only for HTTP services. Upgrade all proxy instances in the selected cluster before using them. Older proxies ignore these actions and continue applying the service's authentication settings. **Bypass authentication** is unavailable for NetBird-Only services and Agent Network targets.
|
||||
</Note>
|
||||
|
||||
## Combining authentication methods
|
||||
|
||||
You can enable multiple **operator auth methods** on a single service simultaneously. When more than one is active, users authenticate using **any** of the enabled methods — they choose which one to use when accessing the service.
|
||||
@@ -201,6 +254,8 @@ Access restrictions control which connections are allowed to reach your service
|
||||
|
||||
Access restrictions are evaluated **before** authentication. If a connection is blocked by an access restriction rule, it is rejected immediately without any authentication check.
|
||||
|
||||
These restrictions also apply to targets set to **Bypass authentication**. A target action cannot relax a service-level CIDR, country, or CrowdSec denial.
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/reverse-proxy/authentication/access-restrictions-geo-ip.png" alt="Authentication tab showing all available authentication methods" className="imagewrapper"/>
|
||||
</p>
|
||||
|
||||
@@ -141,7 +141,7 @@ Switching to the **Run the Proxy** tab automatically generates a one-time, accou
|
||||
|
||||
```shell
|
||||
docker run -d \
|
||||
-v /var/lib/certs:/certs \
|
||||
-v proxy_certs:/certs \
|
||||
-e NB_PROXY_CERTIFICATE_DIRECTORY=/certs \
|
||||
-e NB_PROXY_ALLOW_INSECURE=true \
|
||||
-e NB_PROXY_MANAGEMENT_ADDRESS=https://api.netbird.io \
|
||||
@@ -149,6 +149,8 @@ docker run -d \
|
||||
-e NB_PROXY_DOMAIN=proxy.company.com \
|
||||
-e NB_PROXY_LOG_LEVEL=info \
|
||||
-e NB_PROXY_TOKEN=nbx_... \
|
||||
-e NB_PROXY_PRIVATE=true \
|
||||
-e NB_PROXY_ADDRESS=:443 \
|
||||
-p 80:80 -p 443:443 \
|
||||
netbirdio/reverse-proxy:latest
|
||||
```
|
||||
@@ -167,6 +169,10 @@ The wizard substitutes `NB_PROXY_MANAGEMENT_ADDRESS` based on the dashboard's co
|
||||
The `-p 80:80` mapping is only needed if you switch to the `http-01` ACME challenge (see [TLS configuration](#tls-configuration)). With the default `tls-alpn-01` challenge you can drop it and publish only `-p 443:443`.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
Keep `NB_PROXY_ADDRESS=:443`. The `netbirdio/reverse-proxy` image listens on port `8443` by default, so without this variable nothing answers on the published port `443`. The certificates go in a named volume because the proxy runs as an unprivileged user (uid `1000`). If you bind-mount a host directory instead, make it writable by that user first (`sudo chown 1000:1000 /var/lib/certs`); otherwise the proxy cannot write to it and logs `permission denied`.
|
||||
</Note>
|
||||
|
||||
Run the command on your server. The container will:
|
||||
|
||||
1. Connect to NetBird's management server (`https://api.netbird.io` for NetBird Cloud, or the management URL of your self-hosted deployment) over gRPC and authenticate with the token. The wizard fills this in based on the dashboard's configured management endpoint.
|
||||
@@ -201,6 +207,52 @@ Once the cluster is connected, your BYOP domain shows up in the service creation
|
||||
|
||||
Traffic to `subdomain.proxy.company.com` is now received by your BYOP proxy, terminated locally with a Let's Encrypt certificate, and forwarded over WireGuard to the target peer or network resource.
|
||||
|
||||
## Expose TCP and UDP services
|
||||
|
||||
HTTP services, and TLS services on port `443`, share the proxy's main port, so the command above is all they need. Every other listen port is different. When you create a TCP, UDP or TLS service on a port other than `443`, the proxy opens a listener on that port inside its container, and clients cannot reach it until Docker publishes the port: TCP connections to it are refused.
|
||||
|
||||
For every such listen port:
|
||||
|
||||
1. Allow the port through the firewall or security group in front of the server.
|
||||
2. Publish it on the container: `-p 5432:5432` for TCP, `-p 5353:5353/udp` for UDP.
|
||||
|
||||
Docker fixes a container's published ports when it creates the container, so publishing another port means recreating it (`docker rm -f` and `docker run` again, or `docker compose up -d` after editing `ports:`). A recreate briefly interrupts every service on the proxy, not only the new one. Publishing ports this way is the default, and it keeps Docker as a second filter in front of the proxy. If you add L4 services often, there are two ways to avoid a recreate for each one.
|
||||
|
||||
**Publish a range up front.** For example, add `-p 20000-20099:20000-20099 -p 20000-20099:20000-20099/udp` and choose listen ports inside that range. Ranges cost memory: with Docker's default userland proxy, Docker starts helper processes for every published port and protocol, and a 100-port range for TCP and UDP uses about 500 MB. Keep the range as small as your plans allow. A host firewall such as UFW does not filter ports that Docker publishes ([Docker and ufw](https://docs.docker.com/engine/network/packet-filtering-firewalls/#docker-and-ufw)), so restrict the range with a firewall or security group in front of the server, or with rules in Docker's `DOCKER-USER` chain, and open only the ports you use.
|
||||
|
||||
**Run the proxy on the host network.** This suits a server dedicated to the proxy, because it needs a host-wide setting (below) and leaves the firewall as the only filter. The proxy then binds each listen port on the server itself, so a new TCP or UDP service is reachable as soon as you create it, with no change to the container and no interruption to other services. The firewall becomes the only thing that decides which ports are reachable, so keep it closed by default and open each listen port as you add its service.
|
||||
|
||||
The image runs as an unprivileged user, and on the host network an unprivileged process cannot bind ports below `1024`. `NB_PROXY_ADDRESS=:443` then fails with `bind: permission denied`, and `--cap-add NET_BIND_SERVICE` does not change that. Before you start the proxy, lower the host's unprivileged port floor to the lowest port you need:
|
||||
|
||||
```shell
|
||||
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=443
|
||||
echo 'net.ipv4.ip_unprivileged_port_start=443' | sudo tee /etc/sysctl.d/90-netbird-proxy.conf
|
||||
```
|
||||
|
||||
This setting applies to every process on the server, so give the proxy a host of its own. Then start it with the wizard's command, replacing the `-p` flags with `--network host`:
|
||||
|
||||
```shell
|
||||
docker run -d \
|
||||
--network host \
|
||||
-v proxy_certs:/certs \
|
||||
-e NB_PROXY_CERTIFICATE_DIRECTORY=/certs \
|
||||
-e NB_PROXY_ALLOW_INSECURE=true \
|
||||
-e NB_PROXY_MANAGEMENT_ADDRESS=https://api.netbird.io \
|
||||
-e NB_PROXY_ACME_CERTIFICATES=true \
|
||||
-e NB_PROXY_DOMAIN=proxy.company.com \
|
||||
-e NB_PROXY_LOG_LEVEL=info \
|
||||
-e NB_PROXY_TOKEN=nbx_... \
|
||||
-e NB_PROXY_PRIVATE=true \
|
||||
-e NB_PROXY_ADDRESS=:443 \
|
||||
netbirdio/reverse-proxy:latest
|
||||
```
|
||||
|
||||
An L4 service on a port below the floor still fails to bind: its status becomes `error`, and the proxy logs `bind: permission denied` for that port.
|
||||
|
||||
<Warning>
|
||||
Do not run the container as root (`--user 0:0`) to get around the port limit. As root, the proxy's embedded NetBird client does not connect to management, and HTTP services return `504`.
|
||||
</Warning>
|
||||
|
||||
## Private services (NetBird-Only Access)
|
||||
|
||||
A BYOP cluster whose proxy runs embedded as a NetBird peer (`netbird proxy` mode — the default for the `netbirdio/reverse-proxy` image) reports the `Private` capability if started with the flag `--private` or environment variable `NB_PROXY_PRIVATE=true`. With that capability set, the cluster unlocks two new options anywhere it is selected:
|
||||
@@ -283,12 +335,13 @@ curl -X POST <MANAGEMENT_URL>/api/reverse-proxies/proxy-tokens \
|
||||
|
||||
```shell
|
||||
docker run -d \
|
||||
-v /var/lib/certs:/certs \
|
||||
-v proxy_certs:/certs \
|
||||
-e NB_PROXY_CERTIFICATE_DIRECTORY=/certs \
|
||||
-e NB_PROXY_MANAGEMENT_ADDRESS=<MANAGEMENT_URL> \
|
||||
-e NB_PROXY_ACME_CERTIFICATES=true \
|
||||
-e NB_PROXY_DOMAIN=proxy.company.com \
|
||||
-e NB_PROXY_TOKEN=<plain_token_from_step_1> \
|
||||
-e NB_PROXY_ADDRESS=:443 \
|
||||
-p 80:80 -p 443:443 \
|
||||
netbirdio/reverse-proxy:latest
|
||||
```
|
||||
@@ -340,3 +393,4 @@ For deeper debugging, raise the log level to `debug` or `trace` (`NB_PROXY_LOG_L
|
||||
- [Authentication](/manage/reverse-proxy/authentication) — SSO, password, PIN, header, and access restrictions.
|
||||
- [Access Logs](/manage/reverse-proxy/access-logs) — auditing for traffic going through your proxies.
|
||||
- [Running Multiple Proxy Instances](/selfhosted/maintenance/scaling/multiple-proxy-instances) — operational patterns for HA proxy clusters.
|
||||
- [Reverse Proxy on OpenShift](/manage/reverse-proxy/openshift) — deploy the proxy on OpenShift under the `restricted-v2` SCC with a TCP LoadBalancer or TLS-passthrough Routes.
|
||||
|
||||
@@ -53,16 +53,27 @@ The service mode determines how the proxy handles traffic between clients and yo
|
||||
| Mode | Layer | Description |
|
||||
|------|-------|-------------|
|
||||
| **HTTP** | L7 | TLS termination at the proxy, HTTP-level forwarding. Supports path-based routing, host header forwarding, redirect rewriting, and browser-based authentication (SSO, password, PIN). |
|
||||
| **TCP** | L4 | Raw TCP relay. The proxy accepts TCP connections on a dedicated port and forwards them to your backend. |
|
||||
| **UDP** | L4 | UDP relay with session tracking. The proxy accepts UDP packets on a dedicated port and forwards them to your backend. Sessions are reaped after an idle timeout. |
|
||||
| **TCP** | L4 | Raw TCP relay. The proxy accepts TCP connections on the service's listen port and forwards them to your backend. |
|
||||
| **UDP** | L4 | UDP relay with session tracking. The proxy accepts UDP packets on the service's listen port and forwards them to your backend. Sessions are reaped after an idle timeout. |
|
||||
| **TLS** | L4 | TLS passthrough with SNI-based routing. The proxy inspects the TLS ClientHello to read the SNI hostname and forwards the encrypted connection to your backend without terminating TLS. |
|
||||
|
||||
L4 services (TCP, UDP, TLS) listen on a dedicated port on the proxy cluster. Depending on the cluster, the port may be auto-assigned or you can specify one manually. The proxy cluster's `supports_custom_ports` capability determines whether manual port selection is available.
|
||||
L4 services (TCP, UDP, TLS) listen on a port on the proxy cluster, and several services can share a port. TCP and UDP services can have their port auto-assigned or, on clusters that support custom ports (`supports_custom_ports`), chosen by you. TLS services always need a port you choose. See [Port allocation for L4 services](#port-allocation-for-l4-services) and [How services share ports](#how-services-share-ports).
|
||||
|
||||
<Note>
|
||||
L4 services do not support browser-based authentication (SSO, password, PIN) or header authentication because there is no HTTP layer. You can use [access restrictions](/manage/reverse-proxy/authentication#access-restrictions) (IP CIDR, country, and CrowdSec rules) to protect L4 services.
|
||||
</Note>
|
||||
|
||||
### Protocol support
|
||||
|
||||
HTTP services carry WebSocket connections and Server-Sent Events streams, and serve clients over HTTP/2 or HTTP/1.1. HTTP/3 is not advertised.
|
||||
|
||||
Toward the backend, the proxy uses HTTP/2 only over TLS: a target with protocol `http` is always reached over HTTP/1.1, even if the backend supports HTTP/2 without TLS (h2c). This matters for gRPC, which runs over HTTP/2:
|
||||
|
||||
- **gRPC backend with TLS**: use an HTTP service with an `https` target.
|
||||
- **gRPC backend without TLS**: use a TCP service, which relays the connection unchanged.
|
||||
|
||||
TCP services relay other TCP protocols the same way, such as SSH and RDP.
|
||||
|
||||
### Targets
|
||||
|
||||
A target defines where proxied traffic is sent within your NetBird network. Every target specifies a type and port. HTTP services additionally support path-based routing.
|
||||
@@ -81,6 +92,7 @@ Target properties vary by service mode:
|
||||
- **Path** (optional) - a URL path prefix for path-based routing (e.g., `/api`). See [Path-based routing](#path-based-routing) below.
|
||||
- **Protocol** - `HTTP` or `HTTPS`, depending on what the backend service speaks
|
||||
- **Port** - the port on the target machine (defaults to `80` for HTTP, `443` for HTTPS)
|
||||
- **Access** (optional) - use the service authentication, bypass authentication, or block access for the target's matching path. Configure this under **Optional Settings**. See [Per-target access actions](/manage/reverse-proxy/authentication#per-target-access-actions).
|
||||
- **Enabled/Disabled toggle** - individually enable or disable targets without removing them
|
||||
|
||||
**L4 services (TCP, UDP, TLS):**
|
||||
@@ -252,6 +264,7 @@ In the **Details** tab:
|
||||
|
||||
6. In the target configuration, select the **type** (Peer, Host, Domain, Subnet, or Proxy Cluster), then choose the specific peer, resource, or cluster.
|
||||
7. For HTTP services, set the **protocol** (HTTP or HTTPS) and **port** for the target. Optionally, enter a **path** for path-based routing. For L4 services, set the target **host/IP** and **port**. For **Proxy Cluster** targets, the host field accepts any hostname or IP the cluster's embedded proxy can resolve from its own host stack — see [Private services](/manage/reverse-proxy/bring-your-own-proxy#private-services-net-bird-only-access) for details.
|
||||
8. For an HTTP target, expand **Optional Settings** and choose an **Access** action if this path should differ from the service default. **Use service authentication** is the default. See [Per-target access actions](/manage/reverse-proxy/authentication#per-target-access-actions).
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/reverse-proxy/reverse-proxy-add-target.png" alt="Add Target configuration modal showing the unified Peer / Resource / Proxy Cluster picker" className="imagewrapper"/>
|
||||
@@ -349,15 +362,17 @@ Within a service, you can:
|
||||
|
||||
## Port allocation for L4 services
|
||||
|
||||
L4 services (TCP, UDP, TLS) require a dedicated port on the proxy cluster. How ports are assigned depends on whether the proxy cluster supports custom port selection:
|
||||
L4 services (TCP, UDP, TLS) listen on a port on the proxy cluster. How ports are assigned depends on whether the proxy cluster supports custom port selection:
|
||||
|
||||
- **Custom port selection available**: you choose the exact port the proxy listens on. This is useful when clients expect a well-known port (e.g., 5432 for PostgreSQL, 3306 for MySQL). Self-hosted proxy clusters support this when configured to allow it.
|
||||
- **Auto-assigned ports only**: the proxy cluster automatically assigns an available port. This is the case for NetBird's shared cloud proxy clusters, where port allocation is managed to avoid conflicts between accounts. The assigned port is shown in the service details after creation.
|
||||
- **Auto-assigned ports only**: the proxy cluster automatically assigns an available port to TCP and UDP services. This is the case for NetBird's shared cloud proxy clusters, where port allocation is managed to avoid conflicts between accounts. The assigned port is shown in the service details after creation.
|
||||
|
||||
The dashboard indicates whether a proxy cluster supports custom ports when you select the domain. If custom ports are not supported, the listen port field is read-only and populated after creation.
|
||||
TLS services are the exception: they always need a listen port that you choose, including on clusters without custom port selection, because TLS services share ports by SNI hostname. Creating a TLS service without a listen port fails.
|
||||
|
||||
The dashboard indicates whether a proxy cluster supports custom ports when you select the domain. If custom ports are not supported, the listen port field of a TCP or UDP service is read-only and populated after creation. For a TLS service, the field stays editable.
|
||||
|
||||
<Warning>
|
||||
Each port on a proxy cluster can only be used by one service at a time. If you specify a port that is already in use by another service, creation will fail. L4 listen ports also cannot conflict with the proxy's tunnel port.
|
||||
A port on a proxy cluster can be shared by any number of TLS services, told apart by their SNI hostname, plus at most one TCP service and at most one UDP service. The TCP service receives every connection that matches no TLS service (or, on the main port, no HTTP service). Creating a TCP or UDP service fails if the port already has one of that protocol, and creating any service fails if another service already uses its domain. L4 listen ports also cannot conflict with the proxy's tunnel port.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
@@ -365,7 +380,7 @@ The dashboard indicates whether a proxy cluster supports custom ports when you s
|
||||
</Note>
|
||||
|
||||
<Warning>
|
||||
**Self-hosted Docker deployments:** The default Docker Compose configuration only routes port 443 (via Traefik TLS passthrough) to the proxy container. L4 services that listen on additional TCP or UDP ports require you to manually expose those ports in your `docker-compose.yml`. See the [migration guide](/selfhosted/migration/enable-reverse-proxy#exposing-l4-ports) for instructions.
|
||||
**Self-hosted Docker deployments:** The default Docker Compose configuration only routes port 443 (via Traefik TLS passthrough) to the proxy container, where the proxy's main port is `8443` (`NB_PROXY_ADDRESS`). A TLS service given listen port `443` in such a deployment therefore listens on a custom port inside the container, not on the main port. L4 services that listen on additional TCP or UDP ports require you to manually expose those ports in your `docker-compose.yml`. See the [migration guide](/selfhosted/migration/enable-reverse-proxy#exposing-l4-ports) for instructions. A Bring Your Own Proxy deployment has the same requirement; see [Expose TCP and UDP services](/manage/reverse-proxy/bring-your-own-proxy#expose-tcp-and-udp-services). In both cases, running the proxy on the host network removes the per-port Docker step.
|
||||
</Warning>
|
||||
|
||||
### How services share ports
|
||||
@@ -376,32 +391,38 @@ The proxy's main port always runs an SNI router that peeks at the TLS ClientHell
|
||||
- Each incoming connection is matched against configured domains.
|
||||
- If the SNI hostname matches an **HTTP** service, the connection is forwarded to the HTTP reverse proxy for L7 handling.
|
||||
- If the SNI hostname matches a **TLS** service, the encrypted connection is passed through directly to the backend without TLS termination.
|
||||
- If the SNI hostname does not match any service, the connection falls through to the HTTP reverse proxy, which returns an error since no matching service exists.
|
||||
- Connections with no SNI (e.g., TLS 1.3 with Encrypted Client Hello) follow the same fallback path.
|
||||
- If the SNI hostname does not match any service, the connection goes to the TCP service on that port if there is one, and otherwise falls through to the HTTP reverse proxy, which returns an error since no matching service exists.
|
||||
- Connections with no SNI, and connections that are not TLS at all, follow the same fallback path.
|
||||
|
||||
<Warning>
|
||||
Do not configure an HTTP and a TLS service with the same hostname. The HTTP route takes priority and the TLS service becomes unreachable. Use different domain names for each.
|
||||
A domain can belong to only one service, so an HTTP and a TLS service cannot share a hostname: creating the second one fails with `domain already taken`. Use a different domain for each.
|
||||
</Warning>
|
||||
|
||||
**On custom ports**, TLS and TCP services can coexist:
|
||||
**On custom ports**, any number of TLS services and at most one TCP service can share the same port:
|
||||
- TLS connections are matched by SNI and passed through to the backend.
|
||||
- Connections that do not match any SNI route (or are not TLS at all) fall back to the TCP relay.
|
||||
- This is useful for running a TLS passthrough alongside a plain TCP catch-all on the same port.
|
||||
- If the port has no TCP service, connections that match no TLS service are dropped.
|
||||
- HTTP services are served only on the main port. On a custom port, a request for an HTTP service's hostname matches no route, so it goes to the TCP service if there is one and is dropped otherwise.
|
||||
|
||||
## Path-based routing
|
||||
|
||||
When a service has multiple targets, you can assign each target a unique path prefix to route different URL paths to different backends. For example:
|
||||
When a service has multiple targets, you can assign each target a unique path prefix to route different URL paths to different backends. Each HTTP target can also choose what happens after its path matches:
|
||||
|
||||
| Path | Target | Description |
|
||||
|------|--------|-------------|
|
||||
| `/` | Peer A (port 3000) | Main web application |
|
||||
| `/api` | Peer B (port 8080) | API service |
|
||||
| `/docs` | Resource C (port 80) | Documentation server |
|
||||
| Path | Target | Access | Example result |
|
||||
|------|--------|--------|----------------|
|
||||
| `/` | Peer A (port 3000) | Use service authentication | `/account` uses the authentication configured on the service |
|
||||
| `/public/` | Peer B (port 8080) | Bypass authentication | `/public/logo.svg` reaches Peer B without service authentication |
|
||||
| `/admin/` | Resource C (port 80) | Block access | `/admin/users` returns `403`; Resource C is not contacted |
|
||||
|
||||
Incoming requests are matched against the configured path prefixes and forwarded to the corresponding target. Each path must be unique within a service.
|
||||
Incoming requests are matched against the configured path prefixes. When an enabled target uses **Bypass authentication** or **Block access**, all enabled target paths must be unique within the service. If more than one prefix matches, the **longest matching prefix** wins. In the example above, `/public/logo.svg` matches both `/` and `/public/`, so the `/public/` target and its access action are selected.
|
||||
|
||||
This is useful for consolidating multiple internal services under a single public domain, reducing the number of domains and TLS certificates you need to manage.
|
||||
|
||||
Prefix matching is literal and case-sensitive. Glob patterns such as `*` or `**` and regular expressions are not supported. Prefixes match the request path as text, not by path segment, so a target on `/api` also receives `/api-docs`. To match a whole segment, end the path with a slash: a target on `/api/` receives `/api/x`, but not `/api-docs` or `/api` itself. Likewise, `/Public/x` does not match `/public/`.
|
||||
|
||||
The access action does not change how paths are forwarded. By default, the matched prefix is removed before the request reaches the backend: `/api/x` arrives as `/x`, and `/api-docs` as `/-docs`. To keep the full path, use the existing **Preserve Full Path** target setting (`path_rewrite: preserve` in the API).
|
||||
|
||||
## Integration with Networks
|
||||
|
||||
If you have already configured [Networks](/manage/networks) with resources and routing peers, you can expose a network resource directly from the Networks page.
|
||||
@@ -430,6 +451,7 @@ For the complete guide including setup, authentication options, session lifecycl
|
||||
|
||||
- [Expose from CLI](/manage/reverse-proxy/expose-from-cli) - expose local services from the command line using `netbird expose`
|
||||
- [Multiple Proxy Instances](/selfhosted/maintenance/scaling/multiple-proxy-instances) - deploy multiple proxy instances for high availability and redundancy
|
||||
- [Reverse Proxy on OpenShift](/manage/reverse-proxy/openshift) - run the rootless UBI proxy image on OpenShift with automatic certificates
|
||||
- [Custom Domains](/manage/reverse-proxy/custom-domains) - configure your own domain names for reverse proxy services
|
||||
- [Authentication](/manage/reverse-proxy/authentication) - SSO, password, PIN, header authentication, and access restrictions
|
||||
- [Access Logs](/manage/reverse-proxy/access-logs) - monitor and audit traffic to your reverse proxy services
|
||||
|
||||
@@ -0,0 +1,580 @@
|
||||
import {Note, Warning} from "@/components/mdx"
|
||||
|
||||
export const description =
|
||||
'Run the NetBird reverse proxy on Red Hat OpenShift with the rootless UBI image, automatic Let\'s Encrypt certificates, and either a TCP LoadBalancer or TLS-passthrough Routes for public exposure.'
|
||||
|
||||
# Reverse Proxy on OpenShift
|
||||
|
||||
This guide deploys the NetBird reverse proxy on OpenShift so an external client can reach a backend Service over the NetBird overlay. The proxy obtains and renews its own TLS certificates with ACME, runs under the stock `restricted-v2` security context constraint, and needs no custom SCC, privileged mode, host networking, or elevated capabilities.
|
||||
|
||||
It uses the rootless proxy image built on Red Hat Universal Base Image (UBI) 9 and published for Linux AMD64 and ARM64. The image runs as a non-root user and keeps its writable directories group-writable so OpenShift can assign an arbitrary UID. The manifest in [Step 2](#step-2-deploy-the-proxy) pins the image tag; pin a version or digest for reproducible deployments and see [NetBird releases](https://github.com/netbirdio/netbird/releases) for new versions.
|
||||
|
||||
By the end of this guide, the namespace contains:
|
||||
|
||||
| Resource | Name | Purpose |
|
||||
|----------|------|---------|
|
||||
| Secret | `netbird-proxy-config` | Proxy token, cluster domain, and management address |
|
||||
| PersistentVolumeClaim | `netbird-proxy-certs` | ACME account key and issued certificates |
|
||||
| Deployment | `netbird-proxy` | The NetBird reverse proxy, one replica |
|
||||
| Service | `netbird-proxy` | `ClusterIP`, or `LoadBalancer` for the preferred exposure |
|
||||
| Route | One per service hostname | Only for the TLS-passthrough fallback |
|
||||
| Deployment and Service | `nginx-server` | Optional test backend |
|
||||
|
||||
## How it works
|
||||
|
||||
The proxy is an ordinary NetBird reverse proxy instance: it registers with your management server over gRPC, receives the service mappings for its cluster domain, requests a certificate from Let's Encrypt for each service hostname, terminates TLS, and forwards traffic through the NetBird overlay to a routing peer that reaches the backend Service.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
internet[Internet] -->|TCP 443| entry[Public TCP entrypoint]
|
||||
entry -->|TLS intact| proxy[NetBird reverse proxy]
|
||||
proxy ==>|NetBird overlay| peer[Routing peer]
|
||||
peer -->|HTTP| app[Backend Service]
|
||||
proxy -.->|gRPC: token, service mappings| mgmt[NetBird management]
|
||||
proxy -.->|ACME| le[Let's Encrypt]
|
||||
```
|
||||
|
||||
Only the proxy is reachable from the Internet. The backend Service stays `ClusterIP` and is reached through the routing peer only.
|
||||
|
||||
The public TCP entrypoint must forward TCP 443 to the proxy **without terminating TLS**, so the proxy can answer the Let's Encrypt `tls-alpn-01` challenge and present the issued certificate itself. OpenShift offers two ways to do that. Decide which one applies to your cluster before you start, because it determines your DNS records and your ongoing work:
|
||||
|
||||
| | [TCP LoadBalancer](#preferred-exposure-a-tcp-load-balancer) (preferred) | [TLS-passthrough Routes](#fallback-without-a-load-balancer-tls-passthrough-routes) (fallback) |
|
||||
|---|---|---|
|
||||
| Requires | A LoadBalancer provider that allocates a publicly reachable address | The public OpenShift ingress router |
|
||||
| DNS | Wildcard `CNAME` for service hostnames | Wildcard `CNAME` for service hostnames |
|
||||
| Work per new service | None | Create one Route for the service hostname |
|
||||
| Custom domains | DNS records only | DNS records and one Route per hostname |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you start, make sure you have:
|
||||
|
||||
- An OpenShift login with permission to create Deployments, Services, Secrets, PersistentVolumeClaims and, for the Route fallback, Routes in the target namespace. No additional SCC is needed.
|
||||
- A default StorageClass that can provision a 1 GiB PVC.
|
||||
- A **proxy access token** from your NetBird management server. This is not a client setup key or a personal access token:
|
||||
- **NetBird Cloud** - create an account-scoped token in the dashboard under **Reverse Proxy** > **Clusters** > **Setup Self-Hosted Cluster**, or through the API. See [Bring Your Own Proxy](/manage/reverse-proxy/bring-your-own-proxy). The management address is `https://api.netbird.io`.
|
||||
- **Self-hosted** - create a management-wide token with `netbird-server admin token create` (combined container) or `netbird-mgmt admin token create` (multi-container). See [Enable Reverse Proxy](/selfhosted/migration/enable-reverse-proxy). The management address is your management server's public URL. Because this proxy runs outside the management host's Docker network, the management server's Traefik must also route the `ProxyService` gRPC path and have its idle timeout disabled. Follow [Prepare the management server for cross-host proxies](/selfhosted/maintenance/scaling/multiple-proxy-instances#prepare-the-management-server-for-cross-host-proxies) first. This [Traefik requirement](/manage/reverse-proxy#traefik-requirement) applies to the management server, not to the proxy deployed here.
|
||||
- Public DNS names you control, and inbound TCP 443 connectivity from the Internet to the proxy.
|
||||
- Outbound access from the cluster to your NetBird management server, the configured signal and relay services, Let's Encrypt, and `pkgs.netbird.io`, from which the proxy downloads its geolocation database at startup.
|
||||
- A NetBird routing peer that can reach the backend Service. [Step 4](#step-4-connect-a-backend) uses the rootless UBI client from the [OpenShift installation guide](/get-started/install/openshift) deployed in the same cluster.
|
||||
|
||||
<Note>
|
||||
If the namespace has a default-deny NetworkPolicy, allow egress from the proxy pod to DNS, the management server, signal, relays, Let's Encrypt, and `pkgs.netbird.io` on TCP 443. Also allow ingress to the proxy pod on TCP 8443: from the OpenShift router for the Route fallback (namespaces labeled `policy-group.network.openshift.io/ingress: ""`), or from the load balancer's source addresses for the preferred exposure. Allow the kubelet to reach TCP 8080 for the health probes if your policy restricts host-network traffic.
|
||||
</Note>
|
||||
|
||||
The guide uses three values. Replace the examples with your own:
|
||||
|
||||
| Variable | Example | Purpose |
|
||||
|----------|---------|---------|
|
||||
| `NB_PROXY_DOMAIN` | `proxy.example.com` | The **cluster domain**. The proxy registers under this domain, and services are created as subdomains of it. It is also the value of the proxy's `NB_PROXY_DOMAIN` environment variable. |
|
||||
| `NB_PROXY_MANAGEMENT_ADDRESS` | `https://netbird.example.com:443` | The management server that issued your token, `https://api.netbird.io` for NetBird Cloud. It is also the value of the proxy's `NB_PROXY_MANAGEMENT_ADDRESS` environment variable. |
|
||||
| `SERVICE_HOSTNAME` | `nginx.proxy.example.com` | The **service hostname** users visit. Shell variable only; the proxy learns service hostnames from management. Here it is a subdomain of the cluster domain; a verified [custom domain](/manage/reverse-proxy/custom-domains) works the same way. |
|
||||
|
||||
Export them for the commands in this guide, then create the namespace:
|
||||
|
||||
```bash
|
||||
export NB_PROXY_DOMAIN=proxy.example.com
|
||||
export NB_PROXY_MANAGEMENT_ADDRESS=https://netbird.example.com:443
|
||||
export SERVICE_HOSTNAME=nginx.proxy.example.com
|
||||
export NAMESPACE=netbird-proxy
|
||||
|
||||
oc create namespace "$NAMESPACE" --dry-run=client -o yaml | oc apply -f -
|
||||
```
|
||||
|
||||
## Step 1: Create the configuration Secret
|
||||
|
||||
The Deployment reads its token, cluster domain, and management address from a Secret named `netbird-proxy-config`. Create it with your proxy access token:
|
||||
|
||||
```bash
|
||||
export NB_PROXY_TOKEN=nbx_replace_with_your_proxy_token
|
||||
|
||||
oc create secret generic netbird-proxy-config -n "$NAMESPACE" \
|
||||
--from-literal=proxy-token="$NB_PROXY_TOKEN" \
|
||||
--from-literal=proxy-domain="$NB_PROXY_DOMAIN" \
|
||||
--from-literal=management-address="$NB_PROXY_MANAGEMENT_ADDRESS" \
|
||||
--dry-run=client -o yaml | oc apply -f -
|
||||
```
|
||||
|
||||
The command is safe to re-run to change a value. The proxy picks up changes only after a restart: `oc rollout restart deployment/netbird-proxy -n "$NAMESPACE"`.
|
||||
|
||||
## Step 2: Deploy the proxy
|
||||
|
||||
Self-issued ACME certificates and their account keys live on a single writable volume. The Deployment therefore runs one replica with the `Recreate` strategy, uses filesystem locking for the certificate cache, and mounts a PersistentVolumeClaim at `/certs` so the ACME account and issued certificates survive pod replacement.
|
||||
|
||||
Save the following as `netbird-proxy.yaml`. It needs no edits; every environment-specific value comes from the Secret, and the namespace comes from the `oc apply` command.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: netbird-proxy-certs
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: netbird-proxy
|
||||
labels:
|
||||
app.kubernetes.io/name: netbird-proxy
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: netbird-proxy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: netbird-proxy
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: proxy
|
||||
image: ghcr.io/netbirdio/reverse-proxy:0.80.0-ubi
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: NB_PROXY_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: netbird-proxy-config
|
||||
key: proxy-token
|
||||
- name: NB_PROXY_DOMAIN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: netbird-proxy-config
|
||||
key: proxy-domain
|
||||
- name: NB_PROXY_MANAGEMENT_ADDRESS
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: netbird-proxy-config
|
||||
key: management-address
|
||||
- name: NB_PROXY_ADDRESS
|
||||
value: :8443
|
||||
- name: NB_PROXY_HEALTH_ADDRESS
|
||||
value: :8080
|
||||
- name: NB_PROXY_ACME_CERTIFICATES
|
||||
value: "true"
|
||||
- name: NB_PROXY_ACME_CHALLENGE_TYPE
|
||||
value: tls-alpn-01
|
||||
- name: NB_PROXY_CERTIFICATE_DIRECTORY
|
||||
value: /certs
|
||||
- name: NB_PROXY_CERT_LOCK_METHOD
|
||||
value: flock
|
||||
- name: NB_PROXY_SUPPORTS_CUSTOM_PORTS
|
||||
value: "false"
|
||||
ports:
|
||||
- name: https
|
||||
containerPort: 8443
|
||||
- name: health
|
||||
containerPort: 8080
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /healthz/startup
|
||||
port: health
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 60
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz/ready
|
||||
port: health
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz/live
|
||||
port: health
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
volumeMounts:
|
||||
- name: certificates
|
||||
mountPath: /certs
|
||||
volumes:
|
||||
- name: certificates
|
||||
persistentVolumeClaim:
|
||||
claimName: netbird-proxy-certs
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: netbird-proxy
|
||||
labels:
|
||||
app.kubernetes.io/name: netbird-proxy
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/name: netbird-proxy
|
||||
ports:
|
||||
- name: https
|
||||
port: 443
|
||||
targetPort: https
|
||||
```
|
||||
|
||||
Apply it and wait for the rollout:
|
||||
|
||||
```bash
|
||||
oc apply -n "$NAMESPACE" -f netbird-proxy.yaml
|
||||
oc rollout status deployment/netbird-proxy -n "$NAMESPACE" --timeout=5m
|
||||
```
|
||||
|
||||
The Service is `ClusterIP` on purpose. [Step 3](#step-3-expose-the-proxy) either switches it to `LoadBalancer` or leaves it internal and puts Routes in front of it.
|
||||
|
||||
<Note>
|
||||
On OpenShift, leave `runAsUser`, `runAsGroup`, and `fsGroup` unset. The `restricted-v2` admission applies the namespace's allowed identity and volume group for you, and the image is built to work under that arbitrary UID.
|
||||
</Note>
|
||||
|
||||
**Checkpoint.** The startup probe passes only after the proxy has connected to management and completed its initial mapping sync, so a finished rollout confirms the token and management address are correct. Check the PVC and the logs:
|
||||
|
||||
```bash
|
||||
oc get pvc netbird-proxy-certs -n "$NAMESPACE"
|
||||
oc logs deployment/netbird-proxy -n "$NAMESPACE" --tail=50
|
||||
```
|
||||
|
||||
The PVC is `Bound`, the logs contain `Initial mapping sync complete`, and the cluster appears in the dashboard under **Reverse Proxy** > **Clusters** with an **Online** badge. If the rollout times out, see [Troubleshooting](#troubleshooting).
|
||||
|
||||
### Configuration explained
|
||||
|
||||
| Setting | Why |
|
||||
|---------|-----|
|
||||
| `replicas: 1` and `strategy: Recreate` | One process owns the certificate cache on the PVC. A rolling update would briefly run two writers against the same volume. See [Running more than one replica](#running-more-than-one-replica). |
|
||||
| `NB_PROXY_CERT_LOCK_METHOD=flock` | Filesystem locking on the PVC. The `auto` default may select the Kubernetes lease backend, which needs a service account with lease RBAC. This Deployment mounts no service account token. |
|
||||
| `NB_PROXY_ADDRESS=:8443` | The proxy listens on an unprivileged port. The Service maps port 443 to it. |
|
||||
| `NB_PROXY_HEALTH_ADDRESS=:8080` | Binds the health endpoint to all interfaces so kubelet can reach the startup, readiness, and liveness probes. The same endpoint serves [Prometheus metrics](/selfhosted/observability/proxy). |
|
||||
| `NB_PROXY_ACME_CERTIFICATES=true` and `NB_PROXY_ACME_CHALLENGE_TYPE=tls-alpn-01` | The proxy issues and renews certificates itself over port 443. Port 80 is not required. `tls-alpn-01` issues certificates for individual hostnames, not wildcards. See [TLS-ALPN-01 requirements](/manage/reverse-proxy#tls-alpn-01-requirements) and, if you need `http-01`, [Using http-01 instead of tls-alpn-01](#using-http-01-instead-of-tls-alpn-01). |
|
||||
| `NB_PROXY_CERTIFICATE_DIRECTORY=/certs` on a PVC | Retains the ACME account key and certificates across pod replacement. Keep the PVC; deleting it forces fresh issuance and counts against Let's Encrypt rate limits. |
|
||||
| `NB_PROXY_SUPPORTS_CUSTOM_PORTS=false` | Neither the Service nor the Routes expose arbitrary TCP or UDP ports, so the cluster does not advertise the **Custom Ports** capability. |
|
||||
| `automountServiceAccountToken: false` and `capabilities: drop: [ALL]` | The proxy needs no Kubernetes API access and no Linux capabilities. |
|
||||
|
||||
For the full list of variables, see the [environment variable reference](/selfhosted/migration/enable-reverse-proxy#environment-variable-reference).
|
||||
|
||||
## Step 3: Expose the proxy
|
||||
|
||||
Pick the option you chose in [How it works](#how-it-works). In both cases, public TCP 443 must reach the proxy's port 8443 for the initial certificate issuance as well as every renewal.
|
||||
|
||||
### Preferred exposure: a TCP LoadBalancer
|
||||
|
||||
If your cluster has a LoadBalancer provider, change the Service type. No Route is needed in this mode:
|
||||
|
||||
```bash
|
||||
oc patch service netbird-proxy -n "$NAMESPACE" -p '{"spec":{"type":"LoadBalancer"}}'
|
||||
oc get service netbird-proxy -n "$NAMESPACE"
|
||||
```
|
||||
|
||||
A working provider allocates an externally reachable address in the `EXTERNAL-IP` column and forwards TCP 443 to the proxy as plain TCP. A cloud network load balancer or MetalLB with public routing both qualify. MetalLB alone does not make a private address reachable from the Internet.
|
||||
|
||||
Point the cluster domain at the provisioned address with an `A`/`AAAA` record for an IP, or a `CNAME` for a load balancer hostname. Then point the service hostnames at the cluster domain with a wildcard `CNAME`:
|
||||
|
||||
| Record type | Name | Value |
|
||||
|-------------|------|-------|
|
||||
| `A` or `CNAME` | `proxy.example.com` | The load balancer's IP or hostname |
|
||||
| `CNAME` | `*.proxy.example.com` | `proxy.example.com` |
|
||||
|
||||
Publish an `AAAA` record only if the IPv6 path also works end to end. The NetBird proxy routes by the hostname in the TLS handshake and handles every configured service hostname itself, so new services need no further OpenShift changes.
|
||||
|
||||
**Checkpoint.** `EXTERNAL-IP` shows an address instead of `<pending>`, and `dig "$SERVICE_HOSTNAME"` resolves to it.
|
||||
|
||||
### Fallback without a LoadBalancer: TLS-passthrough Routes
|
||||
|
||||
If the cluster has no LoadBalancer provider, use the existing public OpenShift ingress router instead. Leave the `netbird-proxy` Service as `ClusterIP`. If you already switched it to `LoadBalancer` and `EXTERNAL-IP` stayed `<pending>`, switch it back:
|
||||
|
||||
```bash
|
||||
oc patch service netbird-proxy -n "$NAMESPACE" -p '{"spec":{"type":"ClusterIP"}}'
|
||||
```
|
||||
|
||||
The OpenShift router picks a backend by matching the Route's hostname against the SNI in the TLS handshake, not by Service name. **Every service hostname NetBird knows about therefore needs its own Route with TLS passthrough, pointing at the proxy Service.** It is one Route per hostname, not one Kubernetes Service per hostname.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
internet[Internet] -->|TCP 443| router[OpenShift router]
|
||||
router -->|SNI match, TLS passthrough| svc[proxy Service]
|
||||
svc -->|TCP 8443| proxy[NetBird reverse proxy]
|
||||
proxy ==>|NetBird overlay| peer[Routing peer]
|
||||
peer -->|HTTP| app[Backend Service]
|
||||
```
|
||||
|
||||
Create the Route for the example service hostname:
|
||||
|
||||
```bash
|
||||
oc create route passthrough nginx-proxy -n "$NAMESPACE" \
|
||||
--hostname="$SERVICE_HOSTNAME" \
|
||||
--service=netbird-proxy \
|
||||
--port=https
|
||||
```
|
||||
|
||||
The equivalent manifest, applied with `oc apply -n "$NAMESPACE" -f`:
|
||||
|
||||
```yaml
|
||||
apiVersion: route.openshift.io/v1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: nginx-proxy
|
||||
spec:
|
||||
host: nginx.proxy.example.com
|
||||
to:
|
||||
kind: Service
|
||||
name: netbird-proxy
|
||||
port:
|
||||
targetPort: https
|
||||
tls:
|
||||
termination: passthrough
|
||||
```
|
||||
|
||||
Then configure DNS. The cluster domain points at the public OpenShift ingress endpoint, and the service hostnames point at the cluster domain:
|
||||
|
||||
| Record type | Name | Value |
|
||||
|-------------|------|-------|
|
||||
| `CNAME` | `proxy.example.com` | The router's public endpoint, for example `router-default.apps.<cluster>.example.com` |
|
||||
| `CNAME` | `*.proxy.example.com` | `proxy.example.com` |
|
||||
|
||||
DNS gets the connection to the router. The Route's hostname match gets it from the router to the NetBird proxy. A `CNAME` does not change the hostname sent in the TLS handshake, so the Route must match the service hostname exactly. A Route for the bare cluster domain or for a parent domain does not match `nginx.proxy.example.com`.
|
||||
|
||||
<Warning>
|
||||
Use `passthrough` termination only. With `edge` or `reencrypt`, the router terminates TLS, clients receive the cluster's wildcard certificate, and the `tls-alpn-01` challenge never completes.
|
||||
</Warning>
|
||||
|
||||
**Checkpoint.** The router admitted the Route:
|
||||
|
||||
```bash
|
||||
oc get route nginx-proxy -n "$NAMESPACE" \
|
||||
-o jsonpath='{range .status.ingress[*].conditions[*]}{.type}={.status}: {.message}{"\n"}{end}'
|
||||
```
|
||||
|
||||
The output shows `Admitted=True`, and `dig "$SERVICE_HOSTNAME"` resolves to the router's public endpoint.
|
||||
|
||||
#### Adding more services
|
||||
|
||||
Each service you add in NetBird later, including services on a custom domain, needs its own Route before its certificate can be issued. Create it with a unique name, keeping `--service=netbird-proxy --port=https`:
|
||||
|
||||
```bash
|
||||
oc create route passthrough app-proxy -n "$NAMESPACE" \
|
||||
--hostname=app.proxy.example.com \
|
||||
--service=netbird-proxy \
|
||||
--port=https
|
||||
```
|
||||
|
||||
If a Route for the hostname already exists, update it rather than creating a duplicate.
|
||||
|
||||
## Step 4: Connect a backend
|
||||
|
||||
The backend proves end-to-end connectivity. It should be reachable only through the proxy and the NetBird overlay, never directly from the Internet.
|
||||
|
||||
### Deploy a test backend
|
||||
|
||||
Deploy an unprivileged nginx listening on port 8080, fronted by a `ClusterIP` Service named `nginx-server` that maps HTTP port 80 to the container's port 8080. Save it as `nginx-server.yaml` and apply it with `oc apply -n "$NAMESPACE" -f nginx-server.yaml`. Keep this Service internal; the public entrypoint targets the NetBird proxy, not nginx.
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-server
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx-server
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx-server
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: nginx
|
||||
image: docker.io/nginxinc/nginx-unprivileged:stable-alpine
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: [ALL]
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 16Mi
|
||||
limits:
|
||||
memory: 64Mi
|
||||
volumeMounts:
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
volumes:
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-server
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: nginx-server
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: http
|
||||
```
|
||||
|
||||
### Add the backend to NetBird
|
||||
|
||||
The proxy reaches the backend through a routing peer, so the backend has to exist as a network resource first:
|
||||
|
||||
1. **Deploy a routing peer** in the cluster. Follow [OpenShift Installation](/get-started/install/openshift) to run the rootless UBI client, preferably with a [persistent peer identity](/get-started/install/openshift#persistent-peer-identity) so the routing peer assignment survives restarts.
|
||||
2. **Create a network and a domain resource** with the address `nginx-server.netbird-proxy.svc.cluster.local` (replace `netbird-proxy` if you changed `NAMESPACE`), and assign the UBI client as its routing peer. Any pod in the cluster can resolve and reach this Service. See [Networks](/manage/networks).
|
||||
3. **Add an access policy** that lets the proxy's embedded peer reach the resource. The proxy joins your NetBird network as a peer when it receives its first service mapping. See [Access Control](/manage/access-control/manage-network-access).
|
||||
4. **Create the reverse proxy service** under **Reverse Proxy** > **Services** > **Add Service**. See the [Reverse Proxy quick start](/manage/reverse-proxy#quick-start) for the full dialog.
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Subdomain | `nginx` |
|
||||
| Base domain | Your cluster domain, `proxy.example.com` |
|
||||
| Mode | **HTTP** |
|
||||
| Target | The `nginx-server` **Domain** resource |
|
||||
| Protocol | **HTTP** |
|
||||
| Port | `80` |
|
||||
| Authentication | As needed. See [Authentication](/manage/reverse-proxy/authentication). |
|
||||
|
||||
The proxy requests a certificate for a service hostname only when management sends it the service mapping, so this last step is what triggers issuance. A DNS record or a Route alone does not register a service.
|
||||
|
||||
**Checkpoint.** The service under **Reverse Proxy** > **Services** moves from `certificate_pending` to `active`, and the proxy logs contain `certificate for domain "nginx.proxy.example.com" ready`.
|
||||
|
||||
<Note>
|
||||
Until the first certificate is ready, the proxy logs `TLS handshake error ... acme/autocert: missing certificate` for connections that arrive through the router or load balancer, and may log a single `orderNotReady` error while issuance completes. These warnings are expected and stop once the certificate is issued. If they continue for more than a few minutes, see [Troubleshooting](#troubleshooting).
|
||||
</Note>
|
||||
|
||||
## Step 5: Verify end to end
|
||||
|
||||
From outside the cluster, check HTTPS without disabling certificate verification:
|
||||
|
||||
```bash
|
||||
curl --head --show-error --connect-timeout 10 "https://$SERVICE_HOSTNAME/"
|
||||
```
|
||||
|
||||
HTTPS must present a trusted certificate for the service hostname. An authentication redirect or denial is expected if you enabled authentication on the service; after authenticating in a browser, the nginx welcome page confirms the backend is reachable over the overlay.
|
||||
|
||||
Optionally, confirm the pod was admitted under `restricted-v2` with an OpenShift-assigned UID:
|
||||
|
||||
```bash
|
||||
oc get pods -n "$NAMESPACE" -l app.kubernetes.io/name=netbird-proxy \
|
||||
-o 'custom-columns=NAME:.metadata.name,SCC:.metadata.annotations.openshift\.io/scc,UID:.spec.containers[0].securityContext.runAsUser'
|
||||
```
|
||||
|
||||
## Operating the proxy
|
||||
|
||||
### Upgrading
|
||||
|
||||
Change the image tag in `netbird-proxy.yaml` and re-apply it. Because the Deployment uses `Recreate`, the old pod stops before the new one starts, so expect a short interruption for every service on this cluster. The PVC keeps the issued certificates, so the new pod does not request new ones.
|
||||
|
||||
### Running more than one replica
|
||||
|
||||
This configuration runs a single replica because the certificate cache is on a `ReadWriteOnce` volume. Do not raise `replicas` as-is. For high availability, run additional instances with the same cluster domain, each with its own certificate volume, or switch to a shared wildcard certificate as described in [Using cert-manager instead of ACME](#using-cert-manager-instead-of-acme). See [Running Multiple Proxy Instances](/selfhosted/maintenance/scaling/multiple-proxy-instances#tls-certificate-management) for the trade-offs.
|
||||
|
||||
### Using cert-manager instead of ACME
|
||||
|
||||
If cert-manager already issues certificates in your cluster, you can mount its TLS Secret at `/certs` as a read-only volume, set `NB_PROXY_ACME_CERTIFICATES=false`, and drop the PVC and lock settings. The certificate must cover the cluster domain and `*.{cluster domain}` for the proxy to serve every service hostname. Choose one certificate-management mode for the Deployment rather than combining both. See [TLS certificate configuration](/manage/reverse-proxy#tls-certificate-configuration).
|
||||
|
||||
### Using http-01 instead of tls-alpn-01
|
||||
|
||||
This guide uses `tls-alpn-01` because it needs only TCP 443. Switch to `http-01` only if your ACME CA or network policy requires it.
|
||||
|
||||
With `http-01`, Let's Encrypt validates each service hostname by connecting to it on **public port 80**. The proxy answers on a separate plain-HTTP listener set by `NB_PROXY_ACME_ADDRESS`. The UBI image sets it to `:8081`, because a non-root container under `restricted-v2` cannot bind ports below 1024. **Public port 80 must reach container port 8081.** If only TCP 443 is exposed, as in the rest of this guide, every validation fails and services stay in `certificate_pending`.
|
||||
|
||||
1. In the Deployment, change the challenge type, set the challenge address explicitly, and expose the port:
|
||||
|
||||
```yaml
|
||||
env:
|
||||
- name: NB_PROXY_ACME_CHALLENGE_TYPE
|
||||
value: http-01
|
||||
- name: NB_PROXY_ACME_ADDRESS
|
||||
value: :8081
|
||||
ports:
|
||||
- name: acme-http
|
||||
containerPort: 8081
|
||||
```
|
||||
|
||||
2. Add port 80 to the `netbird-proxy` Service, next to the existing `https` port:
|
||||
|
||||
```yaml
|
||||
ports:
|
||||
- name: https
|
||||
port: 443
|
||||
targetPort: https
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: acme-http
|
||||
```
|
||||
|
||||
3. Re-apply the manifest and confirm the load balancer forwards both TCP 80 and TCP 443 from the Internet.
|
||||
|
||||
The challenge listener answers only ACME requests and redirects all other HTTP requests to HTTPS, so opening port 80 does not expose services over plain HTTP.
|
||||
|
||||
<Warning>
|
||||
`http-01` requires the [TCP LoadBalancer](#preferred-exposure-a-tcp-load-balancer) exposure. It does not work with the TLS-passthrough Route fallback: a passthrough Route only carries TLS traffic on port 443, and the router does not forward plain HTTP on port 80 to the proxy for that hostname. Use `tls-alpn-01` with Routes.
|
||||
</Warning>
|
||||
|
||||
### Removing the deployment
|
||||
|
||||
Delete the NetBird services that use this cluster in the dashboard first, then remove the OpenShift resources:
|
||||
|
||||
```bash
|
||||
oc delete namespace "$NAMESPACE"
|
||||
```
|
||||
|
||||
This also deletes the certificate PVC. If you plan to redeploy with the same hostnames soon, delete the Deployment, Service, and Routes instead and keep the PVC to avoid re-issuing certificates.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
The rows follow the order in which things happen: pod start, management connection, public exposure, certificate issuance, and backend traffic.
|
||||
|
||||
| Symptom | What to check |
|
||||
|---------|---------------|
|
||||
| Pod fails with a permissions error under `/certs` or `/var/lib/netbird` | Confirm the pod runs under `restricted-v2` with no fixed `runAsUser` or `fsGroup`, that the PVC is `Bound` and mounted read-write at `/certs`, and that the StorageClass supports pod volume permissions. Do not add privileged init containers or relax the SCC to work around it. |
|
||||
| Proxy logs `Unauthenticated` right after start | Wrong or revoked token, or a token from a different management server than the `management-address` in the Secret. Update the Secret and restart the Deployment. |
|
||||
| Rollout times out; management gRPC logs HTML `404`/`502` responses | The management server's reverse proxy is not routing the `ProxyService` gRPC path. See [Prepare the management server for cross-host proxies](/selfhosted/maintenance/scaling/multiple-proxy-instances#prepare-the-management-server-for-cross-host-proxies). |
|
||||
| Logs repeat `management connection failed ... PROTOCOL_ERROR` followed by `Initial mapping sync complete`, about once a minute | The rollout succeeds, but the management server's reverse proxy or load balancer cuts long-lived HTTP/2 streams after an idle timeout. Disable or raise that timeout for the `ProxyService` gRPC path. See [Prepare the management server for cross-host proxies](/selfhosted/maintenance/scaling/multiple-proxy-instances#prepare-the-management-server-for-cross-host-proxies). |
|
||||
| Rollout times out with no management errors | Check egress from the namespace to the management server, signal, and relays, including any NetworkPolicy or egress firewall. |
|
||||
| `EXTERNAL-IP` stays `<pending>` on the LoadBalancer Service | The cluster has no working LoadBalancer provider. Use the [Route fallback](#fallback-without-a-load-balancer-tls-passthrough-routes) instead, and switch the Service back to `ClusterIP` first. |
|
||||
| The cluster's wildcard certificate is returned for the service hostname | Traffic reaches the router but not the proxy. Check the exact Route `host`, its admission status, the target Service and port, and that termination is `passthrough`. |
|
||||
| Service stays in `certificate_pending`, or ACME reports `no viable challenge type found` | Validation failed; the error does not only mean an unsupported challenge type. Check the public `A`/`AAAA`/`CNAME` records, end-to-end TCP 443 to the proxy, and, for the Route fallback, that a Route exists for this exact hostname. Also confirm the hostname is under the cluster domain or a verified [custom domain](/manage/reverse-proxy/custom-domains) attached to this cluster, and that any [CAA records](/manage/reverse-proxy/custom-domains#check-your-caa-records) allow `letsencrypt.org`. |
|
||||
| With `http-01`, services stay in `certificate_pending` | Let's Encrypt could not reach public port 80. Check that the Service maps port 80 to container port 8081, that the load balancer forwards TCP 80, and that you are not using the Route fallback. If the logs show `ACME HTTP-01 challenge server failed` with a permission error, `NB_PROXY_ACME_ADDRESS` is a port below 1024; set it to `:8081`. See [Using http-01 instead of tls-alpn-01](#using-http-01-instead-of-tls-alpn-01). |
|
||||
| Cannot write certificates or acquire a lock | Check that the PVC is bound and mounted read-write at `/certs`, and that `NB_PROXY_CERT_LOCK_METHOD` is `flock`. |
|
||||
| HTTPS works but nginx is unreachable, or the service stays in `tunnel_not_created` | Check the routing peer is connected, the access policy covers the proxy's embedded peer, the resource address resolves from the routing peer, and the target uses HTTP on port 80. See [Reverse Proxy Troubleshooting](/manage/reverse-proxy/troubleshooting). |
|
||||
|
||||
Do not repeatedly delete the certificate PVC or restart the proxy to retry issuance before fixing DNS or public routing. Each attempt counts against Let's Encrypt rate limits.
|
||||
|
||||
## Related pages
|
||||
|
||||
- [OpenShift Installation](/get-started/install/openshift) - run the rootless UBI NetBird client on OpenShift, including as a routing peer
|
||||
- [Bring Your Own Proxy](/manage/reverse-proxy/bring-your-own-proxy) - account-scoped proxy tokens and clusters on NetBird Cloud
|
||||
- [Enable Reverse Proxy](/selfhosted/migration/enable-reverse-proxy) - management-wide proxy tokens and the full environment variable reference
|
||||
- [Running Multiple Proxy Instances](/selfhosted/maintenance/scaling/multiple-proxy-instances) - management server preparation for remote proxies and HA patterns
|
||||
- [Custom Domains](/manage/reverse-proxy/custom-domains) - use your own domain for service hostnames
|
||||
- [Reverse Proxy Troubleshooting](/manage/reverse-proxy/troubleshooting) - diagnose backend connectivity problems
|
||||
@@ -308,6 +308,20 @@ Once you regain access to the dashboard, you can re-enable geo-restrictions on t
|
||||
- Leave geo-restrictions disabled on services that the management server needs to reach internally (such as your IdP).
|
||||
- Use a separate network path for the IdP that doesn't pass through the reverse proxy's geo-restriction layer.
|
||||
|
||||
### Issue 4: TCP service is active but connections get no response
|
||||
|
||||
**Symptoms**:
|
||||
- A TCP service shows as active and its listen port accepts connections
|
||||
- Clients connect but receive no response
|
||||
|
||||
**Cause**:
|
||||
|
||||
A service's status reflects the proxy's listener, not the backend: a TCP service stays active and keeps accepting connections even when nothing listens on its target port.
|
||||
|
||||
**Solution**:
|
||||
|
||||
Check the backend, not the service status: on the target host, confirm that the application is listening on the target port and binds to an interface the proxy can reach (step 6 of the [Quick Diagnostics Checklist](#quick-diagnostics-checklist), and Issue 2).
|
||||
|
||||
## Advanced Debugging with Packet Capture
|
||||
|
||||
If the checks above don't reveal the issue, you can use packet capture tools to verify whether traffic is actually arriving on the routing peer or target machine. This is especially useful for diagnosing routing, firewall, or NAT issues.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const description =
|
||||
'Configure account-wide client behavior: automatic updates, lazy connections, and exposing services from the CLI.'
|
||||
'Configure account-wide client behavior: automatic updates, lazy connections, exposing services from the CLI, and unique peer hostnames.'
|
||||
|
||||
# Client Settings
|
||||
|
||||
@@ -29,6 +29,24 @@ Instead of maintaining always-on connections, NetBird can activate peer connecti
|
||||
|
||||
See [Lazy Connections](/manage/peers/lazy-connection) for how activation works, inactivity thresholds, and per-client overrides.
|
||||
|
||||
## Unique Peer Hostnames
|
||||
|
||||
Two machines in one account can register with the same hostname, for example similar VMs that a provisioning tool all named `laptop`. Every peer still needs its own DNS name, so by default NetBird keeps the registration and adds the last two octets of the peer's NetBird IP to the DNS label: a second `laptop` that gets the IP `100.64.12.7` becomes `laptop-12-7.netbird.cloud`. Renaming a peer to a name another peer already uses gets the same suffix.
|
||||
|
||||
The suffix keeps every peer reachable, but it also hides the duplicate. If you want a registration with a duplicate hostname to fail loudly instead, enable the `Reject Hostname Collisions` toggle. With the toggle on:
|
||||
|
||||
- A peer registering with a hostname that another peer in the account already uses is rejected. The client reports `peer with hostname "laptop" is already registered`, stops retrying, and stays disconnected until it logs in again with a different hostname.
|
||||
- Renaming a peer in the dashboard or through the API to a name another peer already uses fails with the same error.
|
||||
- Ephemeral peers are treated exactly like other peers.
|
||||
|
||||
Collisions are matched on the DNS label, not the raw hostname. The label is the hostname lowercased with characters that are not valid in DNS replaced by `-`, so `Laptop` and `laptop` collide, and so do `my_laptop` and `my-laptop`.
|
||||
|
||||
Enabling the toggle does not touch existing peers: labels that already carry a suffix keep it, and a peer logging in again under its own name is not a collision. Deleting a peer frees its name for the next registration. To let a rejected peer join, give the machine a unique hostname or start the client with one: `netbird up --hostname <name>`.
|
||||
|
||||
Keep the toggle off if your fleet registers machines with identical hostnames on purpose, such as autoscaled VMs or containers that join through a [setup key](/manage/peers/register-machines-using-setup-keys). The default suffix exists for that case, and with the toggle on every such machine after the first fails to join.
|
||||
|
||||
This setting is available on NetBird Cloud and on self-hosted management servers running v0.81.0 or later. In the API it is the `peer_hostname_collision_rejected` field of the [account settings](/ipa/resources/accounts).
|
||||
|
||||
## Agent Network-focused view
|
||||
|
||||
Accounts using [Agent Network](/agent-network) see an additional `Agent Network focused view` toggle. When enabled, the dashboard shows only the Agent Network-related sections and hides parts that are not relevant for it, such as Networks, DNS, and Reverse Proxy. Disable it to bring back the full dashboard.
|
||||
|
||||
@@ -19,7 +19,7 @@ To delete your NetBird organization account, you must be a user with the [owner
|
||||
|
||||
After you delete your account, your session will end, and you will be redirected to the login page.
|
||||
<Note>
|
||||
If you log in again, a new account will be created.
|
||||
If you log in again, a new account will be created. The exception on NetBird Cloud is an email domain that already belongs to another account, either as its signup domain or as a verified [sign-in domain](/manage/team/sign-in-domains). In that case you join that account instead, and may need an admin to approve you.
|
||||
</Note>
|
||||
## Get started
|
||||
<div>
|
||||
|
||||
@@ -11,6 +11,10 @@ It features peer-to-peer connections and encryption, access control, routing, an
|
||||
|
||||
- **Business Plan:** At **€12 per user per month**, the Business Plan offers enhanced network security with a Zero Trust approach. It supports unlimited users and includes features like device approvals and integrations (MDM and EDR) for comprehensive control, device posture checks and activity events streaming, making it an excellent choice for organizations seeking advanced security solutions.
|
||||
|
||||
<Note>
|
||||
These plans apply to NetBird Cloud. If you self-host NetBird, the paid features (such as IdP provisioning, device approvals, MDM and EDR integrations, traffic events logging, and event streaming) come from an [Enterprise Commercial License](/selfhosted/enterprise) instead. See the [on-prem pricing page](https://netbird.io/pricing#on-prem).
|
||||
</Note>
|
||||
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/settings/pricing-overview.png" alt="pricing-overview" className="imagewrapper-big"/>
|
||||
|
||||
@@ -24,6 +24,8 @@ The domain can fall into one of the following categories - `public`, `private`,
|
||||
The domains of the private category are the ones that are automatically grouped under the same account.
|
||||
Public domains are the ones of the public email providers like Gmail.
|
||||
|
||||
Users on any other domain your organization uses, such as `company.co.uk` or a subsidiary's domain, don't join automatically. Add and verify those domains as [sign-in domains](/manage/team/sign-in-domains) so their users join your account too.
|
||||
|
||||
<Note>
|
||||
It might happen (unlikely) that the domain classification system didn't classify your company's domain as private.
|
||||
Our system was unsure about your domain and assigned an unclassified or public category to be on the safe side.
|
||||
@@ -43,8 +45,9 @@ After logging in to the system, they will join your network automatically.
|
||||
</p>
|
||||
|
||||
<Note>
|
||||
If a user already has a NetBird account, you can't invite them.
|
||||
This is a limitation that is likely to be removed in future versions.
|
||||
On NetBird Cloud, you can't invite someone who already has a NetBird account, for example one they created by signing in before you invited them. The invite fails with a `409` error. See [User already belongs to another NetBird account](/help/troubleshooting-user-in-another-account) to move them into your account.
|
||||
|
||||
On self-hosted NetBird with the embedded identity provider, a `409` means the email address already belongs to a user in your account. Look for them under **Team → Users** instead.
|
||||
</Note>
|
||||
|
||||
## Identity Provider (IdP) Sync
|
||||
|
||||
@@ -4,6 +4,10 @@ import {Note} from "@/components/mdx";
|
||||
The user approval feature enhances security by requiring manual administrator approval before a user can join your NetBird organization via domain matching.
|
||||
New NetBird Cloud accounts have user approval enabled by default.
|
||||
|
||||
<Note>
|
||||
Signed in and waiting for approval yourself? See [Pending approval and account invitations](/help/troubleshooting-account-access) for what the Waiting for Approval screen means and what to do next.
|
||||
</Note>
|
||||
|
||||
## Require user approval
|
||||
Navigate to the Dashboard's `Settings` page and the `Authentication` tab and enable or disable `User Approval Required`.
|
||||
|
||||
@@ -17,6 +21,8 @@ Navigate to the Dashboard's `Settings` page and the `Authentication` tab and ena
|
||||
## Approve or reject user
|
||||
To approve a user, navigate to the [Users Page](https://app.netbird.io/team/users) and click the `Approve` or `Reject` button on the right side of the users table.
|
||||
|
||||
While they wait, the user sees a **Waiting for Approval** screen that names the account owner's email address, partly hidden, when NetBird can look it up. Otherwise it just asks them to contact the account owner. Any admin can approve them, not only the owner. Once approved, the user clicks **Refresh** on that screen to join the account.
|
||||
|
||||
<p>
|
||||
<img src="/docs-static/img/manage/team/approve-users/netbird-user-approval.png" alt="netbird-user-approval" className="imagewrapper-big"/>
|
||||
</p>
|
||||
|
||||
@@ -20,7 +20,11 @@ or update network configurations like [DNS](/manage/dns#distribution-groups),
|
||||
eliminating the need for manual grouping.
|
||||
|
||||
<Note>
|
||||
This feature is only available in the cloud version of NetBird in the [Team plan](https://netbird.io/pricing) and above.
|
||||
This feature is available in NetBird Cloud on the [Team plan](https://netbird.io/pricing) and above, and in self-hosted deployments with an [Enterprise Commercial License](/selfhosted/enterprise). The open source Community Edition does not include it.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**Before you roll out on NetBird Cloud**: IdP sync can't add a user who already belongs to another NetBird account, for example because they signed in early with an email domain your account doesn't know yet. Add every domain your users sign in with as a [sign-in domain](/manage/team/sign-in-domains) first. If a user still doesn't sync, see [User already belongs to another NetBird account](/help/troubleshooting-user-in-another-account).
|
||||
</Note>
|
||||
|
||||
This video guide walks you through an example integration with Microsoft Entra ID, covering both user onboarding and
|
||||
|
||||