diff --git a/helm-chart/README.md b/helm-chart/README.md index f3ec71252..4d0a16ffe 100644 --- a/helm-chart/README.md +++ b/helm-chart/README.md @@ -228,7 +228,7 @@ KernelMapping pairs kernel versions with a DriverContainer image. Kernel versions can be matched literally or using a regular expression -## Installing with SAML enabled +# Installing with SAML enabled ### Prerequisites: @@ -293,3 +293,226 @@ tap: UaV5sbRtTzYLxpOSQyi8CEFA+A== -----END PRIVATE KEY----- ``` + +# Installing with Dex OIDC authentication + +[**Click here to see full docs**](https://docs.kubeshark.co/en/saml#installing-with-oidc-enabled-dex-idp). + +Choose this option, if **you already have a running instance** of Dex in your cluster & +you want to set up Dex OIDC authentication for Kubeshark users. + +Kubeshark supports authentication using [Dex - A Federated OpenID Connect Provider](https://dexidp.io/). +Dex is an abstraction layer designed for integrating a wide variety of Identity Providers. + +**Requirement:** +Your Dex IdP must have a publicly accessible URL. + +### Pre-requisites: + +**1. If you configured Ingress for Kubeshark:** + +(see section: "Installing with Ingress (EKS) enabled") + +OAuth2 callback URL is:
+`https:///api/oauth2/callback` + +**2. If you did not configure Ingress for Kubeshark:** + +OAuth2 callback URL is:
+`http://0.0.0.0:8899/api/oauth2/callback` + +Use chosen OAuth2 callback URL to replace `` in Step 3. + +**3. Add this static client to your Dex IdP configuration (`config.yaml`):** +```yaml +staticClients: + - id: kubeshark + secret: create your own client password + name: Kubeshark + redirectURIs: + - https:///api/oauth2/callback +``` + +**Final step:** + +Add these helm values to set up OIDC authentication powered by your Dex IdP: + +```yaml +# values.yaml + +tap: + auth: + enabled: true + type: dex + dexOidc: + issuer: + clientId: kubeshark + clientSecret: create your own client password + refreshTokenLifetime: "3960h" # 165 days + oauth2StateParamExpiry: "10m" +``` + +Once you run `helm install kubeshark kubeshark/kubeshark -f ./values.yaml`, Kubeshark will be installed with (Dex) OIDC authentication enabled. + +--- + +# Installing your own Dex IdP along with Kubeshark + +Choose this option, if **you need to deploy an instance of Dex IdP** along with Kubeshark & +set up Dex OIDC authentication for Kubeshark users. + +Depending on Ingress enabled/disabled, your Dex configuration might differ. + +**Requirement:** +Please, configure Ingress using `tap.ingress` for your Kubeshark installation. For example: + +```yaml +tap: + ingress: + enabled: true + className: "alb" + host: ks.example.com + tls: [] + annotations: + alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:7..8:certificate/b...65c + alb.ingress.kubernetes.io/target-type: ip + alb.ingress.kubernetes.io/scheme: internet-facing +``` + +The following Dex settings will have these values: + +| Setting | Value | +|-------------------------------------------------------|----------------------------------------------| +| `tap.auth.dexOidc.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` | + +--- + +### Before proceeding with Dex IdP installation: + +Please, make sure to prepare the following things first. + +1. Choose **[Connectors](https://dexidp.io/docs/connectors/)** to enable in Dex IdP. + - i.e. how many kind of "Log in with ..." options you'd like to offer your users + - You will need to specify connectors in `tap.auth.dexConfig.connectors` +2. Choose type of **[Storage](https://dexidp.io/docs/configuration/storage/)** to use in Dex IdP. + - 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` + - 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 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 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/)** + - if you decide to include more configuration options, make sure to add them into `tap.auth.dexConfig` +--- + +### Once you are ready with all the points described above: + +Use these helm `values.yaml` fields to: +- Deploy your own instance of Dex IdP along with Kubeshark +- Enable OIDC authentication for Kubeshark users + +Make sure to: +- Replace `` with a correct Kubeshark Ingress host (`tap.auth.ingress.host`). + - refer to section **Installing with Ingress (EKS) enabled** to find out how you can configure Ingress host. + +Helm `values.yaml`: +```yaml +tap: + auth: + enabled: true + type: dex + dexOidc: + issuer: https:///dex + + # Client ID/secret must be taken from `tap.auth.dexConfig.staticClients -> id/secret` + clientId: kubeshark + clientSecret: create your own client password + + refreshTokenLifetime: "3960h" # 165 days + oauth2StateParamExpiry: "10m" + dexConfig: + # This field is REQUIRED! + # + # The base path of Dex and the external name of the OpenID Connect service. + # This is the canonical URL that all clients MUST use to refer to Dex. If a + # path is provided, Dex's HTTP service will listen at a non-root URL. + issuer: https:///dex + + # Expiration configuration for tokens, signing keys, etc. + expiry: + refreshTokens: + validIfNotUsedFor: "2160h" # 90 days + absoluteLifetime: "3960h" # 165 days + + # This field is REQUIRED! + # + # The storage configuration determines where Dex stores its state. + # See the documentation (https://dexidp.io/docs/storage/) for further information. + storage: + type: memory + + # This field is REQUIRED! + # + # Attention: + # Do not change this field and its values. + # This field is required for internal Kubeshark-to-Dex communication. + # + # HTTP service configuration + web: + http: 0.0.0.0:5556 + + # This field is REQUIRED! + # + # Attention: + # Do not change this field and its values. + # This field is required for internal Kubeshark-to-Dex communication. + # + # Telemetry configuration + telemetry: + http: 0.0.0.0:5558 + + # This field is REQUIRED! + # + # Static clients registered in Dex by default. + staticClients: + - id: kubeshark + secret: create your own client password + name: Kubeshark + redirectURIs: + - https:///api/oauth2/callback + + # Enable the password database. + # It's a "virtual" connector (identity provider) that stores + # login credentials in Dex's store. + enablePasswordDB: true + + # Connectors are used to authenticate users against upstream identity providers. + # See the documentation (https://dexidp.io/docs/connectors/) for further information. + # + # Attention: + # When you define a new connector, `config.redirectURI` must be: + # https:///dex/callback + # + # Example with Google connector: + # connectors: + # - type: google + # id: google + # name: Google + # config: + # clientID: your Google Cloud Auth app client ID + # clientSecret: your Google Auth app client ID + # redirectURI: https:///dex/callback + connectors: [] +``` diff --git a/helm-chart/templates/06-front-deployment.yaml b/helm-chart/templates/06-front-deployment.yaml index d8586d8be..1644bf450 100644 --- a/helm-chart/templates/06-front-deployment.yaml +++ b/helm-chart/templates/06-front-deployment.yaml @@ -26,12 +26,16 @@ spec: - env: - name: REACT_APP_AUTH_ENABLED value: '{{- if or (and .Values.cloudLicenseEnabled (not (empty .Values.license))) (not .Values.internetConnectivity) -}} - "false" - {{- else -}} - {{ .Values.cloudLicenseEnabled | ternary "true" .Values.tap.auth.enabled }} - {{- end }}' + {{ (and .Values.tap.auth.enabled (eq .Values.tap.auth.type "dex")) | ternary true false }} + {{- else -}} + {{ .Values.cloudLicenseEnabled | ternary "true" .Values.tap.auth.enabled }} + {{- end }}' - name: REACT_APP_AUTH_TYPE - value: '{{ not (eq .Values.tap.auth.type "") | ternary (.Values.cloudLicenseEnabled | ternary "oidc" .Values.tap.auth.type) " " }}' + value: '{{- if and .Values.cloudLicenseEnabled (not (eq .Values.tap.auth.type "dex")) -}} + default + {{- else -}} + {{ .Values.tap.auth.type }} + {{- end }}' - name: REACT_APP_AUTH_SAML_IDP_METADATA_URL value: '{{ not (eq .Values.tap.auth.saml.idpMetadataUrl "") | ternary .Values.tap.auth.saml.idpMetadataUrl " " }}' - name: REACT_APP_TIMEZONE diff --git a/helm-chart/templates/11-nginx-config-map.yaml b/helm-chart/templates/11-nginx-config-map.yaml index 22e085059..70a5cecd5 100644 --- a/helm-chart/templates/11-nginx-config-map.yaml +++ b/helm-chart/templates/11-nginx-config-map.yaml @@ -45,7 +45,22 @@ data: proxy_send_timeout 12s; proxy_pass_request_headers on; } - +{{- if .Values.tap.auth.dexConfig }} + location /dex { + rewrite ^/dex(.*)$ /dex$1 break; + proxy_pass http://kubeshark-dex; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header Upgrade websocket; + proxy_set_header Connection Upgrade; + proxy_set_header Authorization $http_authorization; + proxy_pass_header Authorization; + proxy_connect_timeout 4s; + proxy_read_timeout 120s; + proxy_send_timeout 12s; + proxy_pass_request_headers on; + } +{{- end }} location / { root /usr/share/nginx/html; index index.html index.htm; diff --git a/helm-chart/templates/12-config-map.yaml b/helm-chart/templates/12-config-map.yaml index 175a1b03b..3cf3d7144 100644 --- a/helm-chart/templates/12-config-map.yaml +++ b/helm-chart/templates/12-config-map.yaml @@ -18,14 +18,21 @@ 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)) -}} - "false" + {{ and .Values.tap.auth.enabled (eq .Values.tap.auth.type "dex") | ternary true false }} {{- else -}} {{ .Values.cloudLicenseEnabled | ternary "true" (.Values.tap.auth.enabled | ternary "true" "") }} {{- end }}' - AUTH_TYPE: '{{ .Values.cloudLicenseEnabled | ternary "oidc" (.Values.tap.auth.type) }}' + AUTH_TYPE: '{{- if and .Values.cloudLicenseEnabled (not (eq .Values.tap.auth.type "dex")) -}} + default + {{- else -}} + {{ .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 }}' TELEMETRY_DISABLED: '{{ not .Values.internetConnectivity | ternary "true" (not .Values.tap.telemetry.enabled | ternary "true" "false") }}' SCRIPTING_DISABLED: '{{- if .Values.tap.liveConfigMapChangesDisabled -}} {{- if .Values.demoModeEnabled -}} diff --git a/helm-chart/templates/13-secret.yaml b/helm-chart/templates/13-secret.yaml index d1c431c18..026567ed2 100644 --- a/helm-chart/templates/13-secret.yaml +++ b/helm-chart/templates/13-secret.yaml @@ -9,6 +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 }}' --- diff --git a/helm-chart/templates/17-network-policies.yaml b/helm-chart/templates/17-network-policies.yaml index 276acd2db..9235daf75 100644 --- a/helm-chart/templates/17-network-policies.yaml +++ b/helm-chart/templates/17-network-policies.yaml @@ -53,6 +53,31 @@ spec: --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy +metadata: + labels: + {{- include "kubeshark.labels" . | nindent 4 }} + annotations: + {{- if .Values.tap.annotations }} + {{- toYaml .Values.tap.annotations | nindent 4 }} + {{- end }} + name: kubeshark-dex-network-policy + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app.kubeshark.co/app: dex + policyTypes: + - Ingress + - Egress + ingress: + - ports: + - protocol: TCP + port: 5556 + egress: + - {} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy metadata: labels: {{- include "kubeshark.labels" . | nindent 4 }} diff --git a/helm-chart/templates/18-dex-deployment.yaml b/helm-chart/templates/18-dex-deployment.yaml new file mode 100644 index 000000000..ea2d07f73 --- /dev/null +++ b/helm-chart/templates/18-dex-deployment.yaml @@ -0,0 +1,116 @@ +{{- if .Values.tap.auth.dexConfig }} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubeshark.co/app: dex + {{- include "kubeshark.labels" . | nindent 4 }} + annotations: + {{- if .Values.tap.annotations }} + {{- toYaml .Values.tap.annotations | nindent 4 }} + {{- end }} + name: {{ include "kubeshark.name" . }}-dex + namespace: {{ .Release.Namespace }} +spec: + replicas: 1 # Set the desired number of replicas + selector: + matchLabels: + app.kubeshark.co/app: dex + {{- include "kubeshark.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + app.kubeshark.co/app: dex + {{- include "kubeshark.labels" . | nindent 8 }} + spec: + containers: + - name: kubeshark-dex + image: 'dexidp/dex:v2.42.0-alpine' + ports: + - name: http + containerPort: 5556 + protocol: TCP + - name: telemetry + containerPort: 5558 + protocol: TCP + args: + - dex + - serve + - /etc/dex/dex-config.yaml + imagePullPolicy: {{ .Values.tap.docker.imagePullPolicy }} + volumeMounts: + - name: dex-secret-conf-volume + mountPath: /etc/dex/dex-config.yaml + subPath: dex-config.yaml + readOnly: true + livenessProbe: + httpGet: + path: /healthz/live + port: 5558 + periodSeconds: 1 + failureThreshold: 3 + successThreshold: 1 + initialDelaySeconds: 3 + readinessProbe: + httpGet: + path: /healthz/ready + port: 5558 + periodSeconds: 1 + failureThreshold: 3 + successThreshold: 1 + initialDelaySeconds: 3 + timeoutSeconds: 1 + resources: + limits: + cpu: 750m + memory: 1Gi + requests: + cpu: 50m + memory: 50Mi + {{- if .Values.tap.docker.imagePullSecrets }} + imagePullSecrets: + {{- range .Values.tap.docker.imagePullSecrets }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- if gt (len .Values.tap.nodeSelectorTerms.dex) 0}} + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + {{- toYaml .Values.tap.nodeSelectorTerms.dex | nindent 12 }} +{{- end }} + {{- if or .Values.tap.dns.nameservers .Values.tap.dns.searches .Values.tap.dns.options }} + dnsConfig: + {{- if .Values.tap.dns.nameservers }} + nameservers: + {{- range .Values.tap.dns.nameservers }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.tap.dns.searches }} + searches: + {{- range .Values.tap.dns.searches }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.tap.dns.options }} + options: + {{- range .Values.tap.dns.options }} + - name: {{ .name | quote }} + {{- if .value }} + value: {{ .value | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + volumes: + - name: dex-secret-conf-volume + secret: + secretName: kubeshark-dex-conf-secret + dnsPolicy: ClusterFirstWithHostNet + serviceAccountName: {{ include "kubeshark.serviceAccountName" . }} + +{{- end }} diff --git a/helm-chart/templates/19-dex-service.yaml b/helm-chart/templates/19-dex-service.yaml new file mode 100644 index 000000000..f10db2423 --- /dev/null +++ b/helm-chart/templates/19-dex-service.yaml @@ -0,0 +1,25 @@ +{{- if .Values.tap.auth.dexConfig }} + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubeshark.co/app: dex + {{- include "kubeshark.labels" . | nindent 4 }} + annotations: + {{- if .Values.tap.annotations }} + {{- toYaml .Values.tap.annotations | nindent 4 }} + {{- end }} + name: kubeshark-dex + namespace: {{ .Release.Namespace }} +spec: + ports: + - name: kubeshark-dex + port: 80 + targetPort: 5556 + selector: + app.kubeshark.co/app: dex + type: ClusterIP + +{{- end }} diff --git a/helm-chart/templates/20-dex-secret.yaml b/helm-chart/templates/20-dex-secret.yaml new file mode 100644 index 000000000..6355b57d4 --- /dev/null +++ b/helm-chart/templates/20-dex-secret.yaml @@ -0,0 +1,14 @@ +{{- if .Values.tap.auth.dexConfig }} + +kind: Secret +apiVersion: v1 +metadata: + name: kubeshark-dex-conf-secret + namespace: {{ .Release.Namespace }} + labels: + app.kubeshark.co/app: hub + {{- include "kubeshark.labels" . | nindent 4 }} +data: + dex-config.yaml: {{ .Values.tap.auth.dexConfig | toYaml | b64enc | quote }} + +{{- end }} diff --git a/helm-chart/templates/_helpers.tpl b/helm-chart/templates/_helpers.tpl index 887a6cc7c..0230d06f6 100644 --- a/helm-chart/templates/_helpers.tpl +++ b/helm-chart/templates/_helpers.tpl @@ -86,3 +86,15 @@ Set sentry based on internet connectivity and telemetry {{- end -}} {{- $sentryEnabledVal -}} {{- end -}} + +{{/* +Dex IdP: retrieve a secret for static client with a specific ID +*/}} +{{- define "getDexKubesharkStaticClientSecret" -}} + {{- $clientId := .clientId -}} + {{- range .clients }} + {{- if eq .id $clientId }} + {{- .secret }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index 1c68a9cde..11a3a7c98 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -99,6 +99,12 @@ tap: operator: In values: - linux + dex: + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux tolerations: hub: [] workers: