mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-31 03:51:29 +02:00
fix allowserverssh mdm
This commit is contained in:
@@ -106,7 +106,7 @@ State that crosses screens/windows lives in context, each provider mounted exact
|
||||
- **`useStatus`** (`StatusContext`) — `{ status, error, refresh, isReady, isDaemonAvailable, isDaemonUnavailable }`. Owns the single `DaemonFeed.Get` + `netbird:status` subscription and the daemon gate (see Layouts). `refresh()` after Connect/Disconnect to dodge a few hundred ms of event-stream lag.
|
||||
- **`ProfileContext`** — `username`, `activeProfile`, `profiles`, plus `refresh` / `switchProfile` / `addProfile` / `removeProfile` / `logoutProfile`. `switchProfile` delegates to `ProfileSwitcher.SwitchActive` (the Go-side single source of truth — drives the optimistic-Connecting paint and `Peers` suppression). The other methods are thin wrappers over `Profiles.*` / `Connection.Logout` + a `refresh()`.
|
||||
- **`SettingsContext`** — `setField` / `saveField` / `saveFields` / `saveNow` over `Settings.GetConfig|SetConfig` with 400ms debounce. Renders `<SettingsSkeleton/>` while `config === null`. **PSK mask quirk:** `GetConfig` returns existing PSKs as `"**********"`; sending the mask back round-trips it into storage and `wgtypes.ParseKey` fails on the next connect — `save` drops the field when it equals the mask.
|
||||
- **`MdmContext`** — `useMdm()` returns `config.managedFields` as `Record<string, boolean>`, **keyed by the daemon's `mdm.Key*` names exactly as written in the policy source** (`managementURL`, `allowServerSSH`, `preSharedKey`, `wireguardPort`, `rosenpassEnabled`/`Permissive`, `disableClientRoutes`/`disableServerRoutes`, `disableAutoConnect`, `blockInbound`). No GUI-side renaming — what the Group Policy admin writes is what the lookup key is. Mounted in `AppLayout` (under `ProfileProvider`); fetches `Settings.GetConfig` once, re-fetches on the daemon's `netbird:event` `metadata.type=config_changed` push so policy flips paint live. No second copy of the locked *values* — MDM is a global override, so the active profile's resolved `useSettings().config.<field>` already carries the MDM-enforced value. Consumers: Settings tabs hide individual toggles/sections (both rosenpass keys managed ⇒ whole encryption section hidden); `SettingsNavigation` + `SettingsPage` hide the SSH tab when `managed.allowServerSSH` is set and bounce `active="ssh"` back to General; `ProfileCreationModal` skips the Cloud/self-hosted picker when `managed.managementURL` is set and submits the resolved URL verbatim; `WelcomeDialog` reads `config.managedFields.managementURL` directly (sits outside `AppLayout`) to skip the management step on a fresh install.
|
||||
- **`MdmContext`** — `useMdm()` returns `config.managedFields` as `Record<string, boolean>`, **keyed by the daemon's `mdm.Key*` names exactly as written in the policy source** (`managementURL`, `allowServerSSH`, `preSharedKey`, `wireguardPort`, `rosenpassEnabled`/`Permissive`, `disableClientRoutes`/`disableServerRoutes`, `disableAutoConnect`, `blockInbound`). No GUI-side renaming — what the Group Policy admin writes is what the lookup key is. Mounted in `AppLayout` (under `ProfileProvider`); fetches `Settings.GetConfig` once, re-fetches on the daemon's `netbird:event` `metadata.type=config_changed` push so policy flips paint live. No second copy of the locked *values* — MDM is a global override, so the active profile's resolved `useSettings().config.<field>` already carries the MDM-enforced value. Consumers: Settings tabs hide individual toggles/sections (both rosenpass keys managed ⇒ whole encryption section hidden); `mdm.*` fields are **is-managed flags** — `true` when the field name appears in `MDMManagedFields`, `false` otherwise. Consumers check truthiness (`!mdm.x` to mean "not managed"). The *resolved value* of a managed field is already enforced into `Config`, so consumers read it via `useSettings().config.<field>` and hide the toggle when `mdm.<field>` is true. **Three carve-outs** that carry the resolved value instead, populated outside the reflective is-managed loop in `services/settings.go` `GetRestrictions`: `mdm.managementURL` (string — used by `ProfileCreationModal` / `WelcomeDialog`, which need the URL to render the form); `mdm.allowServerSSH` (`*bool` / `boolean | null` — used by `SettingsNavigation` + `SettingsPage` to gate the SSH tab on `mdm.allowServerSSH ?? !features.disableUpdateSettings`; tri-state needed because the tab gate lives outside `SettingsProvider` and can't read `config.serverSshAllowed`, and MDM-managed-with-value-`false` must hide the tab — falsy fallthrough would leak it); and `mdm.disableAdvancedView` (`bool` — MDM-only on the daemon, no CLI fallback; plumbed via the `GetFeatures` RPC rather than `MDMManagedFields`, so set directly from `featResp.GetDisableAdvancedView()`. Value semantic: `true` iff MDM explicitly disables; the nil/false collapse is fine because both mean "advanced view available"). The three `features.*` gates (`disableProfiles`/`Networks`/`UpdateSettings`) live in `Features` because they accept a CLI-flag fallback in addition to MDM (`--disable-profiles` etc.) — `disableAdvancedView` doesn't, which is why it sits with the MDM-only carve-outs. `ProfileCreationModal` skips the Cloud/self-hosted picker when `managed.managementURL` is set and submits the resolved URL verbatim; `WelcomeDialog` reads `config.managedFields.managementURL` directly (sits outside `AppLayout`) to skip the management step on a fresh install.
|
||||
- **`DebugBundleContext`** — stages `idle → preparing-trace → reconnecting → capturing → restoring-level → bundling → uploading → done`. Cancellable via `AbortController` at any stage; cancel restores the original log level best-effort. Upload URL is the hardcoded `NETBIRD_UPLOAD_URL`.
|
||||
- **`ClientVersionContext`** — seeds from `Update.GetState()`, subscribes to `netbird:update:state`; exposes `{ updateAvailable, updateVersion, enforced, installing, triggerUpdate, updating }`. Three branches:
|
||||
1. `available && !enforced` — download-only; `UpdateVersionCard` → opens GitHub releases.
|
||||
|
||||
@@ -34,7 +34,7 @@ const MainBody = () => {
|
||||
const { viewMode, setViewMode } = useViewMode();
|
||||
const { mdm, features } = useRestrictions();
|
||||
|
||||
// Force flip the view if mdm changed it
|
||||
// Force flip the view if MDM disabled advanced
|
||||
useEffect(() => {
|
||||
if (mdm.disableAdvancedView && viewMode === "advanced") {
|
||||
setViewMode("default");
|
||||
|
||||
@@ -19,7 +19,7 @@ export const SettingsNavigation = () => {
|
||||
const { t } = useTranslation();
|
||||
const { updateAvailable } = useClientVersion();
|
||||
const { mdm, features } = useRestrictions();
|
||||
const showSsh = mdm.allowServerSSH || !features.disableUpdateSettings;
|
||||
const showSsh = mdm.allowServerSSH ?? !features.disableUpdateSettings;
|
||||
|
||||
const aboutAdornment = updateAvailable ? (
|
||||
<Tooltip content={t("settings.tabs.updateAvailable")} side={"right"}>
|
||||
|
||||
@@ -54,7 +54,7 @@ export const SettingsPage = () => {
|
||||
[Tab.Network]: editable,
|
||||
[Tab.Security]: editable,
|
||||
[Tab.Profiles]: !features.disableProfiles,
|
||||
[Tab.SSH]: mdm.allowServerSSH || editable,
|
||||
[Tab.SSH]: mdm.allowServerSSH ?? editable,
|
||||
[Tab.Advanced]: editable,
|
||||
[Tab.Troubleshooting]: true,
|
||||
[Tab.About]: true,
|
||||
|
||||
@@ -18,13 +18,13 @@ type MDMFields struct {
|
||||
RosenpassPermissive bool `json:"rosenpassPermissive"`
|
||||
DisableClientRoutes bool `json:"disableClientRoutes"`
|
||||
DisableServerRoutes bool `json:"disableServerRoutes"`
|
||||
AllowServerSSH bool `json:"allowServerSSH"`
|
||||
AllowServerSSH *bool `json:"allowServerSSH"`
|
||||
DisableAutoConnect bool `json:"disableAutoConnect"`
|
||||
BlockInbound bool `json:"blockInbound"`
|
||||
DisableMetricsCollection bool `json:"disableMetricsCollection"`
|
||||
SplitTunnelMode bool `json:"splitTunnelMode"`
|
||||
SplitTunnelApps bool `json:"splitTunnelApps"`
|
||||
DisableAdvancedView bool `json:"disableAdvancedView"`
|
||||
DisableAdvancedView bool `json:"disableAdvancedView"`
|
||||
}
|
||||
|
||||
type Features struct {
|
||||
@@ -222,6 +222,9 @@ func (s *Settings) GetRestrictions(ctx context.Context) (Restrictions, error) {
|
||||
DisableNetworks: featResp.GetDisableNetworks(),
|
||||
DisableUpdateSettings: featResp.GetDisableUpdateSettings(),
|
||||
},
|
||||
MDM: MDMFields{
|
||||
DisableAdvancedView: featResp.GetDisableAdvancedView(),
|
||||
},
|
||||
}
|
||||
managed := cfgResp.GetMDMManagedFields()
|
||||
if len(managed) > 0 {
|
||||
@@ -235,10 +238,6 @@ func (s *Settings) GetRestrictions(ctx context.Context) (Restrictions, error) {
|
||||
if v.Field(i).Kind() != reflect.Bool {
|
||||
continue
|
||||
}
|
||||
// AllowServerSSH carries the resolved value (like ManagementURL), not the is-managed flag.
|
||||
if t.Field(i).Name == "AllowServerSSH" {
|
||||
continue
|
||||
}
|
||||
if _, ok := set[t.Field(i).Tag.Get("json")]; ok {
|
||||
v.Field(i).SetBool(true)
|
||||
}
|
||||
@@ -247,9 +246,9 @@ func (s *Settings) GetRestrictions(ctx context.Context) (Restrictions, error) {
|
||||
r.MDM.ManagementURL = cfgResp.GetManagementUrl()
|
||||
}
|
||||
if _, ok := set["allowServerSSH"]; ok {
|
||||
r.MDM.AllowServerSSH = cfgResp.GetServerSSHAllowed()
|
||||
allowed := cfgResp.GetServerSSHAllowed()
|
||||
r.MDM.AllowServerSSH = &allowed
|
||||
}
|
||||
}
|
||||
r.MDM.DisableAdvancedView = featResp.GetDisableAdvancedView()
|
||||
return r, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user