From fcceed23ab328f586c839fe59c89c692c827c517 Mon Sep 17 00:00:00 2001 From: Volodymyr Stoiko Date: Tue, 14 Jul 2026 20:19:55 +0300 Subject: [PATCH] Permissions refactor: tap.auth.* roles + groupMapping (#1942) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * auth: drop AUTH_ROLES; add AUTH_GROUP_MAPPING + built-in defaultRole Companion to kubeshark/hub#permissions-refactoring. Aligns the CLI config struct, chart values, and rendered ConfigMap with the post-refactor hub. config/configStructs/tapConfig.go: - Drop AuthConfig.Roles (admin-authored map[string]Role) and the Role + ScriptingPermissions structs they referenced. - Drop AuthConfig.DefaultFilter (no namespace scoping in v1). - Add AuthConfig.GroupMapping (map[string]string) — SSO group name → built-in role translation. - Tighten DefaultRole godoc to reference the four built-in role constants (kubeshark-admin / kubeshark-realtime / kubeshark-snapshot / kubeshark-viewer) and the strict-deny semantics on empty. config/configStruct.go: - Drop the legacy "admin" entry from the AuthConfig default — operators now configure DefaultRole + GroupMapping instead. - Default RolesClaim is now "groups" (Okta/OIDC convention; was "role"), matching the hub's runtime default. helm-chart/templates/12-config-map.yaml: - Drop AUTH_ROLES emission (key no longer read by hub). - Add AUTH_GROUP_MAPPING emission from tap.auth.groupMapping (JSON map; hub validates each value against the built-in role names at sync time). helm-chart/values.yaml: regenerated from the Go config — drops the tap.auth.roles block, adds tap.auth.groupMapping with the new documentation header for DefaultRole. Breaking change: deployments carrying tap.auth.roles in their values will silently lose those role definitions. Migration is to remove the roles: block and either (a) name their SSO groups to match the four built-in role constants, or (b) populate tap.auth.groupMapping with explicit translations. * auth: set helm default role to kubeshark-viewer Per round-2 permissions clarifications: SSO users whose claim doesn't match any built-in role and isn't in AUTH_GROUP_MAPPING should fall back to a read-only baseline instead of strict-deny (""). defaultRole="" causes the dashboard to 403-storm gated endpoints from unmatched users; viewer (snapshot:read only) gives them a sensible read-only UX while still preventing any state change. * auth: add tap.auth.roles operator-defined role catalogue Chart-side companion to hub commit 67162b2e (Phase C of the permissions refactor). Operators can now declare named roles with their own capability set + namespace scope under tap.auth.roles; the 12-config-map renders these as AUTH_ROLES JSON for the hub to consume. - config/configStructs: AuthConfig.Roles map[string]RoleConfig with Capabilities + Namespaces; doc comments updated for groupMapping + defaultRole to reflect that user-defined names are now accepted. - config/configStruct.go: zero-value initializer for Roles so `kubeshark config` renders `roles: {}` consistently. - helm-chart/templates/12-config-map.yaml: AUTH_ROLES emits the full roles map as JSON; hub-side syncAuthRoles validates names (kubeshark-* prefix reserved) and capabilities (unknown caps warn-dropped). - helm-chart/values.yaml: regenerated. Diff is the single `roles: {}` line under tap.auth. Spot-checked the rendered ConfigMap: AUTH_ROLES: '{"payments-viewer":{"capabilities":["snapshot:read", "dissection:live"], "namespaces":"payments"}}' which is exactly the shape the hub parser expects. * auth: emit CHART_VERSION into hub ConfigMap (Phase V) Hub commit 51abc954 reads this key on first SyncConfig and warns when its embedded version.Ver disagrees with the chart at the major component. Provided alongside as the chart-side companion to keep both sides on one PR per repo on the permissions-refactoring branch. * Revert "auth: emit CHART_VERSION into hub ConfigMap (Phase V)" This reverts c21e4c42. Companion to hub revert 61a275b6 — the hub no longer reads CHART_VERSION, so emitting it serves no purpose. * chore: drop internal plan ref from auth config comment Plans live as local working docs, not in the repo — strip the cross-repo plans/permissions-decisions.md reference from the AuthConfig.Roles comment. --- config/configStruct.go | 21 ++------- config/configStructs/tapConfig.go | 57 +++++++++++++++---------- helm-chart/templates/12-config-map.yaml | 3 +- helm-chart/values.yaml | 20 ++------- 4 files changed, 44 insertions(+), 57 deletions(-) diff --git a/config/configStruct.go b/config/configStruct.go index 15b14bedc..131c2bb4d 100644 --- a/config/configStruct.go +++ b/config/configStruct.go @@ -102,23 +102,10 @@ func CreateDefaultConfig() ConfigStruct { }, }, Auth: configStructs.AuthConfig{ - RolesClaim: "role", - Roles: map[string]configStructs.Role{ - "admin": { - Filter: "", - CanDownloadPCAP: true, - CanUseScripting: true, - ScriptingPermissions: configStructs.ScriptingPermissions{ - CanSave: true, - CanActivate: true, - CanDelete: true, - }, - CanUpdateTargetedPods: true, - CanStopTrafficCapturing: true, - CanControlDissection: true, - ShowAdminConsoleLink: true, - }, - }, + RolesClaim: "groups", + DefaultRole: "kubeshark-viewer", + GroupMapping: map[string]string{}, + Roles: map[string]configStructs.RoleConfig{}, }, EnabledDissectors: []string{ "amqp", diff --git a/config/configStructs/tapConfig.go b/config/configStructs/tapConfig.go index fcd6cca46..238a32e00 100644 --- a/config/configStructs/tapConfig.go +++ b/config/configStructs/tapConfig.go @@ -155,23 +155,6 @@ type ProbeConfig struct { FailureThreshold int `yaml:"failureThreshold" json:"failureThreshold" default:"3"` } -type ScriptingPermissions struct { - CanSave bool `yaml:"canSave" json:"canSave" default:"true"` - CanActivate bool `yaml:"canActivate" json:"canActivate" default:"true"` - CanDelete bool `yaml:"canDelete" json:"canDelete" default:"true"` -} - -type Role struct { - Filter string `yaml:"filter" json:"filter" default:""` - CanDownloadPCAP bool `yaml:"canDownloadPCAP" json:"canDownloadPCAP" default:"false"` - CanUseScripting bool `yaml:"canUseScripting" json:"canUseScripting" default:"false"` - ScriptingPermissions ScriptingPermissions `yaml:"scriptingPermissions" json:"scriptingPermissions"` - CanUpdateTargetedPods bool `yaml:"canUpdateTargetedPods" json:"canUpdateTargetedPods" default:"false"` - CanStopTrafficCapturing bool `yaml:"canStopTrafficCapturing" json:"canStopTrafficCapturing" default:"false"` - CanControlDissection bool `yaml:"canControlDissection" json:"canControlDissection" default:"false"` - ShowAdminConsoleLink bool `yaml:"showAdminConsoleLink" json:"showAdminConsoleLink" default:"false"` -} - type SamlConfig struct { IdpMetadataUrl string `yaml:"idpMetadataUrl" json:"idpMetadataUrl"` X509crt string `yaml:"x509crt" json:"x509crt"` @@ -190,12 +173,40 @@ type AuthConfig struct { // NOTE: prior releases routed `oidc` to Descope. If you were using `oidc` // to mean Descope, switch to `descope` (or `default`). The rename is a // breaking change documented in the release notes. - Type string `yaml:"type" json:"type" default:"saml"` - Roles map[string]Role `yaml:"roles" json:"roles"` - RolesClaim string `yaml:"rolesClaim" json:"rolesClaim"` - DefaultRole string `yaml:"defaultRole" json:"defaultRole"` - DefaultFilter string `yaml:"defaultFilter" json:"defaultFilter"` - Saml SamlConfig `yaml:"saml" json:"saml"` + Type string `yaml:"type" json:"type" default:"saml"` + RolesClaim string `yaml:"rolesClaim" json:"rolesClaim"` + // DefaultRole is applied when the authenticated user's SSO claim has no + // recognized group. Must be one of the four built-in roles + // (kubeshark-admin / kubeshark-realtime / kubeshark-snapshot / + // kubeshark-viewer), the name of an operator-defined role under + // `tap.auth.roles`, or empty for strict-deny. + DefaultRole string `yaml:"defaultRole" json:"defaultRole"` + // GroupMapping translates SSO group names into role names (built-in or + // operator-defined). Optional — groups whose name already matches a + // built-in role are identity-matched and don't need an entry here. + // Operator-defined role names MUST appear here to participate in + // resolution (identity-match is built-in-only). + GroupMapping map[string]string `yaml:"groupMapping" json:"groupMapping"` + // Roles is the operator-defined role catalogue, keyed by role name. + // Each role has its own capability set + namespace scope. Names with + // the `kubeshark-` prefix are reserved for built-ins and will be + // rejected at hub startup. Unknown capability strings are dropped + // with a warning; empty / "*" namespace specs mean deny-all-data and + // allow-all respectively. + Roles map[string]RoleConfig `yaml:"roles" json:"roles"` + Saml SamlConfig `yaml:"saml" json:"saml"` +} + +// RoleConfig is an operator-defined role declared under tap.auth.roles. +// Capabilities is the closed vocabulary documented in the hub project +// (snapshot:read / snapshot:write / snapshot:dissection / dissection:live / +// dissection:control / pods:target:write / settings:write); unknown +// capability strings are warn-dropped at hub startup. Namespaces is a +// comma-separated list with `*` (allow-all) and glob (`foo-*`, `*-bar`, +// `*mid*`) support; empty string means deny-all-data. +type RoleConfig struct { + Capabilities []string `yaml:"capabilities" json:"capabilities"` + Namespaces string `yaml:"namespaces" json:"namespaces"` } type IngressConfig struct { diff --git a/helm-chart/templates/12-config-map.yaml b/helm-chart/templates/12-config-map.yaml index dc7f15641..1849af543 100644 --- a/helm-chart/templates/12-config-map.yaml +++ b/helm-chart/templates/12-config-map.yaml @@ -29,9 +29,10 @@ data: {{ (default false .Values.demoModeEnabled) | ternary "default" .Values.tap.auth.type }} {{- end }}' AUTH_SAML_IDP_METADATA_URL: '{{ .Values.tap.auth.saml.idpMetadataUrl }}' - AUTH_ROLES: '{{ .Values.tap.auth.roles | toJson }}' AUTH_ROLES_CLAIM: '{{ .Values.tap.auth.rolesClaim }}' AUTH_DEFAULT_ROLE: '{{ default "" .Values.tap.auth.defaultRole }}' + AUTH_GROUP_MAPPING: '{{ default (dict) .Values.tap.auth.groupMapping | toJson }}' + AUTH_ROLES: '{{ default (dict) .Values.tap.auth.roles | toJson }}' AUTH_OIDC_ISSUER: '{{ default "not set" (((.Values.tap).auth).oidc).issuer }}' AUTH_OIDC_REFRESH_TOKEN_LIFETIME: '{{ default "3960h" (((.Values.tap).auth).oidc).refreshTokenLifetime }}' AUTH_OIDC_STATE_PARAM_EXPIRY: '{{ default "10m" (((.Values.tap).auth).oidc).oauth2StateParamExpiry }}' diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index 93ea01244..41f11b925 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -153,22 +153,10 @@ tap: auth: enabled: false type: saml - roles: - admin: - filter: "" - canDownloadPCAP: true - canUseScripting: true - scriptingPermissions: - canSave: true - canActivate: true - canDelete: true - canUpdateTargetedPods: true - canStopTrafficCapturing: true - canControlDissection: true - showAdminConsoleLink: true - rolesClaim: role - defaultRole: "" - defaultFilter: "" + rolesClaim: groups + defaultRole: kubeshark-viewer + groupMapping: {} + roles: {} saml: idpMetadataUrl: "" x509crt: ""