diff --git a/config/configStruct.go b/config/configStruct.go index c587eb6c8..e5640c200 100644 --- a/config/configStruct.go +++ b/config/configStruct.go @@ -102,23 +102,21 @@ func CreateDefaultConfig() ConfigStruct { }, }, Auth: configStructs.AuthConfig{ - Saml: configStructs.SamlConfig{ - RoleAttribute: "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: "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, }, }, }, diff --git a/config/configStructs/tapConfig.go b/config/configStructs/tapConfig.go index 6cd8164bf..fcd6cca46 100644 --- a/config/configStructs/tapConfig.go +++ b/config/configStructs/tapConfig.go @@ -173,17 +173,29 @@ type Role struct { } type SamlConfig struct { - IdpMetadataUrl string `yaml:"idpMetadataUrl" json:"idpMetadataUrl"` - X509crt string `yaml:"x509crt" json:"x509crt"` - X509key string `yaml:"x509key" json:"x509key"` - RoleAttribute string `yaml:"roleAttribute" json:"roleAttribute"` - Roles map[string]Role `yaml:"roles" json:"roles"` + IdpMetadataUrl string `yaml:"idpMetadataUrl" json:"idpMetadataUrl"` + X509crt string `yaml:"x509crt" json:"x509crt"` + X509key string `yaml:"x509key" json:"x509key"` } type AuthConfig struct { - Enabled bool `yaml:"enabled" json:"enabled" default:"false"` - Type string `yaml:"type" json:"type" default:"saml"` - Saml SamlConfig `yaml:"saml" json:"saml"` + Enabled bool `yaml:"enabled" json:"enabled" default:"false"` + // Type selects the authentication backend. Valid values: + // saml — SAML 2.0 SSO + // oidc — generic OIDC (Dex, Okta, Auth0, Keycloak, Azure AD, …) + // dex — permanent alias of oidc (kept for back-compat) + // descope — Descope SDK + // default — also routes to Descope (kept, not deprecated) + // + // 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 IngressConfig struct { diff --git a/helm-chart/README.md b/helm-chart/README.md index 1c03373c6..ba6636d59 100644 --- a/helm-chart/README.md +++ b/helm-chart/README.md @@ -212,14 +212,15 @@ Example for overriding image names: | `tap.tolerations.hub` | Tolerations for hub component | `[]` | | `tap.tolerations.front` | Tolerations for front-end component | `[]` | | `tap.auth.enabled` | Enable authentication | `false` | -| `tap.auth.type` | Authentication type (1 option available: `saml`) | `saml` | +| `tap.auth.type` | Authentication backend. Valid values: `saml`, `oidc` (generic OIDC — Dex, Okta, Auth0, Keycloak, Azure AD, Google, …), `dex` (permanent alias of `oidc`), `descope`, `default` (also routes to Descope). **Breaking**: prior releases routed `oidc` to Descope — if you were using it for Descope, switch to `descope` or `default`. | `saml` | | `tap.auth.approvedEmails` | List of approved email addresses for authentication | `[]` | | `tap.auth.approvedDomains` | List of approved email domains for authentication | `[]` | -| `tap.auth.saml.idpMetadataUrl` | SAML IDP metadata URL
(effective, if `tap.auth.type = saml`) | `` | +| `tap.auth.rolesClaim` | Name of the JWT claim (OIDC) or SAML attribute carrying role memberships. | `role` | +| `tap.auth.defaultRole` | Optional role name inside `tap.auth.roles` applied as fallback when an authenticated user has no matching role. Empty string = no fallback, zero-valued permissions. | `""` | +| `tap.auth.roles` | Backend-neutral role map shared by SAML and OIDC. Each role's `namespaces` is a comma-separated list controlling which Kubernetes namespaces the role's users see traffic for: `""` = deny all, `"*"` = allow all, `"foo"` = literal namespace, `"foo,bar"` = OR over literals, `"foo-*"` = glob expansion against the cluster's known namespaces. Empty/unset `tap.auth.roles` grants nothing — admins opt into elevated access by populating this map. | `{"admin":{"namespaces":"*","canDownloadPCAP":true,"canUpdateTargetedPods":true,"canUseScripting":true,"scriptingPermissions":{"canSave":true,"canActivate":true,"canDelete":true},"canStopTrafficCapturing":true,"canControlDissection":true,"showAdminConsoleLink":true}}` | +| `tap.auth.saml.idpMetadataUrl` | SAML IDP metadata URL
(effective, if `tap.auth.type = saml`) | `` | | `tap.auth.saml.x509crt` | A self-signed X.509 `.cert` contents
(effective, if `tap.auth.type = saml`) | `` | | `tap.auth.saml.x509key` | A self-signed X.509 `.key` contents
(effective, if `tap.auth.type = saml`) | `` | -| `tap.auth.saml.roleAttribute` | A SAML attribute name corresponding to user's authorization role
(effective, if `tap.auth.type = saml`) | `role` | -| `tap.auth.saml.roles` | A list of SAML authorization roles and their permissions
(effective, if `tap.auth.type = saml`) | `{"admin":{"canDownloadPCAP":true,"canUpdateTargetedPods":true,"canUseScripting":true, "scriptingPermissions":{"canSave":true, "canActivate":true, "canDelete":true}, "canStopTrafficCapturing":true, "canControlDissection":true, "filter":"","showAdminConsoleLink":true}}` | | `tap.ingress.enabled` | Enable `Ingress` | `false` | | `tap.ingress.className` | Ingress class name | `""` | | `tap.ingress.host` | Host of the `Ingress` | `ks.svc.cluster.local` | @@ -377,8 +378,8 @@ Add these helm values to set up OIDC authentication powered by your Dex IdP: tap: auth: enabled: true - type: dex - dexOidc: + type: oidc # canonical; `dex` is accepted as a permanent alias + oidc: issuer: clientId: kubeshark clientSecret: create your own client password @@ -390,7 +391,7 @@ tap: --- **Note:**
-Set `tap.auth.dexOidc.bypassSslCaCheck: true` +Set `tap.auth.oidc.bypassSslCaCheck: true` to allow Kubeshark communication with Dex IdP having an unknown SSL Certificate Authority. This setting allows you to prevent such SSL CA-related errors:
@@ -429,7 +430,7 @@ The following Dex settings will have these values: | Setting | Value | |-------------------------------------------------------|----------------------------------------------| -| `tap.auth.dexOidc.issuer` | `https://ks.example.com/dex` | +| `tap.auth.oidc.issuer` | `https://ks.example.com/dex` | | `tap.auth.dexConfig.issuer` | `https://ks.example.com/dex` | | `tap.auth.dexConfig.staticClients -> redirectURIs` | `https://ks.example.com/api/oauth2/callback` | | `tap.auth.dexConfig.connectors -> config.redirectURI` | `https://ks.example.com/dex/callback` | @@ -447,16 +448,16 @@ Please, make sure to prepare the following things first. - You will need to specify storage settings in `tap.auth.dexConfig.storage` - default: `memory` 3. Decide on the OAuth2 `?state=` param expiration time: - - field: `tap.auth.dexOidc.oauth2StateParamExpiry` + - field: `tap.auth.oidc.oauth2StateParamExpiry` - default: `10m` (10 minutes) - valid time units are `s`, `m`, `h` 4. Decide on the refresh token expiration: - - field 1: `tap.auth.dexOidc.expiry.refreshTokenLifetime` + - field 1: `tap.auth.oidc.expiry.refreshTokenLifetime` - field 2: `tap.auth.dexConfig.expiry.refreshTokens.absoluteLifetime` - default: `3960h` (165 days) - valid time units are `s`, `m`, `h` 5. Create a unique & secure password to set in these fields: - - field 1: `tap.auth.dexOidc.clientSecret` + - field 1: `tap.auth.oidc.clientSecret` - field 2: `tap.auth.dexConfig.staticClients -> secret` - password must be the same for these 2 fields 6. Discover more possibilities of **[Dex Configuration](https://dexidp.io/docs/configuration/)** @@ -478,8 +479,8 @@ Helm `values.yaml`: tap: auth: enabled: true - type: dex - dexOidc: + type: oidc # canonical; `dex` is accepted as a permanent alias + oidc: issuer: https:///dex # Client ID/secret must be taken from `tap.auth.dexConfig.staticClients -> id/secret` diff --git a/helm-chart/templates/06-front-deployment.yaml b/helm-chart/templates/06-front-deployment.yaml index ab30f22a6..9d94ca7cf 100644 --- a/helm-chart/templates/06-front-deployment.yaml +++ b/helm-chart/templates/06-front-deployment.yaml @@ -26,12 +26,12 @@ spec: - env: - name: REACT_APP_AUTH_ENABLED value: '{{- if or (and .Values.cloudLicenseEnabled (not (empty .Values.license))) (not .Values.internetConnectivity) -}} - {{ (default false .Values.demoModeEnabled) | ternary true ((and .Values.tap.auth.enabled (eq .Values.tap.auth.type "dex")) | ternary true false) }} + {{ (default false .Values.demoModeEnabled) | ternary true ((and .Values.tap.auth.enabled (or (eq .Values.tap.auth.type "oidc") (eq .Values.tap.auth.type "dex"))) | ternary true false) }} {{- else -}} {{ .Values.cloudLicenseEnabled | ternary "true" ((default false .Values.demoModeEnabled) | ternary "true" .Values.tap.auth.enabled) }} {{- end }}' - name: REACT_APP_AUTH_TYPE - value: '{{- if and .Values.cloudLicenseEnabled (not (eq .Values.tap.auth.type "dex")) -}} + value: '{{- if and .Values.cloudLicenseEnabled (not (or (eq .Values.tap.auth.type "oidc") (eq .Values.tap.auth.type "dex"))) -}} default {{- else -}} {{ (default false .Values.demoModeEnabled) | ternary "default" .Values.tap.auth.type }} diff --git a/helm-chart/templates/12-config-map.yaml b/helm-chart/templates/12-config-map.yaml index bdb40f90b..dc7f15641 100644 --- a/helm-chart/templates/12-config-map.yaml +++ b/helm-chart/templates/12-config-map.yaml @@ -19,27 +19,28 @@ data: INGRESS_HOST: '{{ .Values.tap.ingress.host }}' PROXY_FRONT_PORT: '{{ .Values.tap.proxy.front.port }}' AUTH_ENABLED: '{{- if and .Values.cloudLicenseEnabled (not (empty .Values.license)) -}} - {{ (default false .Values.demoModeEnabled) | ternary true ((and .Values.tap.auth.enabled (eq .Values.tap.auth.type "dex")) | ternary true false) }} + {{ (default false .Values.demoModeEnabled) | ternary true ((and .Values.tap.auth.enabled (or (eq .Values.tap.auth.type "oidc") (eq .Values.tap.auth.type "dex"))) | ternary true false) }} {{- else -}} {{ .Values.cloudLicenseEnabled | ternary "true" ((default false .Values.demoModeEnabled) | ternary "true" .Values.tap.auth.enabled) }} {{- end }}' - AUTH_TYPE: '{{- if and .Values.cloudLicenseEnabled (not (eq .Values.tap.auth.type "dex")) -}} + AUTH_TYPE: '{{- if and .Values.cloudLicenseEnabled (not (or (eq .Values.tap.auth.type "oidc") (eq .Values.tap.auth.type "dex"))) -}} default {{- else -}} {{ (default false .Values.demoModeEnabled) | ternary "default" .Values.tap.auth.type }} {{- end }}' AUTH_SAML_IDP_METADATA_URL: '{{ .Values.tap.auth.saml.idpMetadataUrl }}' - AUTH_SAML_ROLE_ATTRIBUTE: '{{ .Values.tap.auth.saml.roleAttribute }}' - AUTH_SAML_ROLES: '{{ .Values.tap.auth.saml.roles | toJson }}' - AUTH_OIDC_ISSUER: '{{ default "not set" (((.Values.tap).auth).dexOidc).issuer }}' - AUTH_OIDC_REFRESH_TOKEN_LIFETIME: '{{ default "3960h" (((.Values.tap).auth).dexOidc).refreshTokenLifetime }}' - AUTH_OIDC_STATE_PARAM_EXPIRY: '{{ default "10m" (((.Values.tap).auth).dexOidc).oauth2StateParamExpiry }}' + AUTH_ROLES: '{{ .Values.tap.auth.roles | toJson }}' + AUTH_ROLES_CLAIM: '{{ .Values.tap.auth.rolesClaim }}' + AUTH_DEFAULT_ROLE: '{{ default "" .Values.tap.auth.defaultRole }}' + 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 }}' AUTH_OIDC_BYPASS_SSL_CA_CHECK: '{{- if and (hasKey .Values.tap "auth") - (hasKey .Values.tap.auth "dexOidc") - (hasKey .Values.tap.auth.dexOidc "bypassSslCaCheck") + (hasKey .Values.tap.auth "oidc") + (hasKey .Values.tap.auth.oidc "bypassSslCaCheck") -}} - {{ eq .Values.tap.auth.dexOidc.bypassSslCaCheck true | ternary "true" "false" }} + {{ eq .Values.tap.auth.oidc.bypassSslCaCheck true | ternary "true" "false" }} {{- else -}} false {{- end }}' diff --git a/helm-chart/templates/13-secret.yaml b/helm-chart/templates/13-secret.yaml index cf6f92c15..059732fd0 100644 --- a/helm-chart/templates/13-secret.yaml +++ b/helm-chart/templates/13-secret.yaml @@ -9,8 +9,8 @@ metadata: stringData: LICENSE: '{{ .Values.license }}' SCRIPTING_ENV: '{{ .Values.scripting.env | toJson }}' - OIDC_CLIENT_ID: '{{ default "not set" (((.Values.tap).auth).dexOidc).clientId }}' - OIDC_CLIENT_SECRET: '{{ default "not set" (((.Values.tap).auth).dexOidc).clientSecret }}' + OIDC_CLIENT_ID: '{{ default "not set" (((.Values.tap).auth).oidc).clientId }}' + OIDC_CLIENT_SECRET: '{{ default "not set" (((.Values.tap).auth).oidc).clientSecret }}' --- diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index 302bafc2b..9fe77e5f5 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -152,25 +152,44 @@ tap: front: [] auth: enabled: false + # Valid values: saml, oidc (generic OIDC — Dex, Okta, Auth0, Keycloak, + # Azure AD, Google, …), dex (permanent alias of oidc), descope, default + # (also Descope). Was previously misnamed: prior releases routed `oidc` + # to Descope. See release notes for migration. type: saml + # Name of the JWT claim (OIDC) or SAML attribute carrying role memberships. + rolesClaim: role + # Optional: role name inside `roles` applied as a fallback when an + # authenticated user has no matching role in their token/assertion. + # Empty string means no fallback (authenticated but no elevated permissions). + defaultRole: "" + # Backend-neutral role map shared by SAML and OIDC. Empty/unset grants + # nothing — admins opt into elevated access by populating this map. + # + # Per-role `namespaces` controls which Kubernetes namespaces the role's + # users are allowed to see traffic for. Comma-separated list: + # "" — deny all (explicit deny-default for this role). + # "*" — allow every namespace (no scope filter applied). + # "foo" — only the literal namespace "foo" (src or dst). + # "foo,bar" — OR over both literal namespaces. + # "foo-*" — glob expansion against the cluster's known namespaces. + roles: + admin: + namespaces: "*" + canDownloadPCAP: true + canUseScripting: true + scriptingPermissions: + canSave: true + canActivate: true + canDelete: true + canUpdateTargetedPods: true + canStopTrafficCapturing: true + canControlDissection: true + showAdminConsoleLink: true saml: idpMetadataUrl: "" x509crt: "" x509key: "" - roleAttribute: role - roles: - admin: - filter: "" - canDownloadPCAP: true - canUseScripting: true - scriptingPermissions: - canSave: true - canActivate: true - canDelete: true - canUpdateTargetedPods: true - canStopTrafficCapturing: true - canControlDissection: true - showAdminConsoleLink: true ingress: enabled: false className: ""