UI behavior conflicts relaxation

UI sends full config snapshot with all values. It doesn't
make sense to block it if the values are aligned with the
values constrained by the MDM policy. It's just simplier
to allow values that are compliant. (this goes for the CLI
as well at this point)
This commit is contained in:
riccardom
2026-06-08 18:06:02 +02:00
parent 1100cea6a4
commit aab1a3e150
2 changed files with 60 additions and 52 deletions
+58 -51
View File
@@ -152,65 +152,72 @@ func (s *Server) restartEngineForMDM() error {
return nil return nil
} }
// requestedMDMManagedKeys returns the names of MDM-managed keys whose // mdmManagedFieldConflicts returns the names of MDM-managed keys whose
// corresponding field is set in the SetConfigRequest. Only keys with an // requested value in the SetConfigRequest differs from the MDM-enforced
// MDM mapping are considered; other fields are ignored. // value. A field set to the same value the policy already enforces is
func requestedMDMManagedKeys(msg *proto.SetConfigRequest) []string { // treated as a no-op echo (the GUI tray sends a full Config snapshot on
if msg == nil { // every toggle, so most fields in a typical request match the policy
return nil // exactly and must NOT be flagged as conflicts).
} //
var keys []string // The redacted PreSharedKey sentinel ("**********") that GetConfig
if msg.ManagementUrl != "" { // returns is recognised and treated as no-op so the UI can safely round-
keys = append(keys, mdm.KeyManagementURL) // trip it without tripping the gate.
} func mdmManagedFieldConflicts(msg *proto.SetConfigRequest, policy *mdm.Policy) []string {
if msg.OptionalPreSharedKey != nil { if msg == nil || policy.IsEmpty() {
keys = append(keys, mdm.KeyPreSharedKey)
}
if msg.RosenpassEnabled != nil {
keys = append(keys, mdm.KeyRosenpassEnabled)
}
if msg.RosenpassPermissive != nil {
keys = append(keys, mdm.KeyRosenpassPermissive)
}
if msg.DisableAutoConnect != nil {
keys = append(keys, mdm.KeyDisableAutoConnect)
}
if msg.ServerSSHAllowed != nil {
keys = append(keys, mdm.KeyAllowServerSSH)
}
if msg.DisableClientRoutes != nil {
keys = append(keys, mdm.KeyDisableClientRoutes)
}
if msg.DisableServerRoutes != nil {
keys = append(keys, mdm.KeyDisableServerRoutes)
}
if msg.BlockInbound != nil {
keys = append(keys, mdm.KeyBlockInbound)
}
if msg.WireguardPort != nil {
keys = append(keys, mdm.KeyWireguardPort)
}
return keys
}
// rejectMDMManagedFieldConflicts returns a FailedPrecondition gRPC error
// with an MDMManagedFieldsViolation detail when any of the requested keys
// is MDM-enforced, and nil otherwise. The whole request is rejected on
// any conflict; non-conflicting fields in the same request are not
// applied either (no partial apply).
func rejectMDMManagedFieldConflicts(policy *mdm.Policy, requested []string) error {
if policy.IsEmpty() || len(requested) == 0 {
return nil return nil
} }
var conflicts []string var conflicts []string
for _, k := range requested { mark := func(key string) { conflicts = append(conflicts, key) }
if policy.HasKey(k) {
conflicts = append(conflicts, k) if msg.ManagementUrl != "" && policy.HasKey(mdm.KeyManagementURL) {
if want, ok := policy.GetString(mdm.KeyManagementURL); !ok || want != msg.ManagementUrl {
mark(mdm.KeyManagementURL)
} }
} }
if msg.OptionalPreSharedKey != nil && policy.HasKey(mdm.KeyPreSharedKey) {
// "**********" is the redacted echo from GetConfig — never a real
// override attempt regardless of what the policy holds.
if *msg.OptionalPreSharedKey != "**********" {
if want, ok := policy.GetString(mdm.KeyPreSharedKey); !ok || want != *msg.OptionalPreSharedKey {
mark(mdm.KeyPreSharedKey)
}
}
}
checkBool := func(key string, p *bool) {
if p == nil || !policy.HasKey(key) {
return
}
if want, ok := policy.GetBool(key); !ok || want != *p {
mark(key)
}
}
checkBool(mdm.KeyRosenpassEnabled, msg.RosenpassEnabled)
checkBool(mdm.KeyRosenpassPermissive, msg.RosenpassPermissive)
checkBool(mdm.KeyDisableAutoConnect, msg.DisableAutoConnect)
checkBool(mdm.KeyAllowServerSSH, msg.ServerSSHAllowed)
checkBool(mdm.KeyDisableClientRoutes, msg.DisableClientRoutes)
checkBool(mdm.KeyDisableServerRoutes, msg.DisableServerRoutes)
checkBool(mdm.KeyBlockInbound, msg.BlockInbound)
if msg.WireguardPort != nil && policy.HasKey(mdm.KeyWireguardPort) {
if want, ok := policy.GetInt(mdm.KeyWireguardPort); !ok || want != *msg.WireguardPort {
mark(mdm.KeyWireguardPort)
}
}
return conflicts
}
// rejectMDMManagedFieldConflicts returns a FailedPrecondition gRPC error
// with an MDMManagedFieldsViolation detail when any of the requested
// fields tries to change an MDM-enforced value to something else, and
// nil otherwise. The whole request is rejected on any conflict; non-
// conflicting fields in the same request are not applied either (no
// partial apply).
func rejectMDMManagedFieldConflicts(policy *mdm.Policy, conflicts []string) error {
if len(conflicts) == 0 { if len(conflicts) == 0 {
return nil return nil
} }
_ = policy
log.Warnf("MDM rejected request: tried to modify %d managed key(s): %v", log.Warnf("MDM rejected request: tried to modify %d managed key(s): %v",
len(conflicts), conflicts) len(conflicts), conflicts)
st := gstatus.New( st := gstatus.New(
+2 -1
View File
@@ -330,7 +330,8 @@ func (s *Server) SetConfig(callerCtx context.Context, msg *proto.SetConfigReques
// by the active MDM policy. The error carries an MDMManagedFields- // by the active MDM policy. The error carries an MDMManagedFields-
// Violation detail listing the offending key names. Non-conflicting // Violation detail listing the offending key names. Non-conflicting
// fields in the same request are not applied either. // fields in the same request are not applied either.
if err := rejectMDMManagedFieldConflicts(loadMDMPolicy(), requestedMDMManagedKeys(msg)); err != nil { policy := loadMDMPolicy()
if err := rejectMDMManagedFieldConflicts(policy, mdmManagedFieldConflicts(msg, policy)); err != nil {
return nil, err return nil, err
} }