diff --git a/cmd/tapRunner.go b/cmd/tapRunner.go index 7d1678603..19fbd91a3 100644 --- a/cmd/tapRunner.go +++ b/cmd/tapRunner.go @@ -444,12 +444,25 @@ func updateConfig(kubernetesProvider *kubernetes.Provider) { _, _ = kubernetes.SetConfig(kubernetesProvider, kubernetes.CONFIG_SCRIPTING_ENV, string(data)) } + ingressEnabled := "" + if config.Config.Tap.Ingress.Enabled { + ingressEnabled = "true" + } + authEnabled := "" if config.Config.Tap.Auth.Enabled { authEnabled = "true" } + + _, _ = kubernetes.SetConfig(kubernetesProvider, kubernetes.CONFIG_INGRESS_ENABLED, ingressEnabled) + _, _ = kubernetes.SetConfig(kubernetesProvider, kubernetes.CONFIG_INGRESS_HOST, config.Config.Tap.Ingress.Host) + + _, _ = kubernetes.SetConfig(kubernetesProvider, kubernetes.CONFIG_PROXY_FRONT_PORT, fmt.Sprint(config.Config.Tap.Proxy.Front.Port)) + _, _ = kubernetes.SetConfig(kubernetesProvider, kubernetes.CONFIG_AUTH_ENABLED, authEnabled) + _, _ = kubernetes.SetConfig(kubernetesProvider, kubernetes.CONFIG_AUTH_TYPE, config.Config.Tap.Auth.Type) _, _ = kubernetes.SetConfig(kubernetesProvider, kubernetes.CONFIG_AUTH_APPROVED_EMAILS, strings.Join(config.Config.Tap.Auth.ApprovedEmails, ",")) _, _ = kubernetes.SetConfig(kubernetesProvider, kubernetes.CONFIG_AUTH_APPROVED_DOMAINS, strings.Join(config.Config.Tap.Auth.ApprovedDomains, ",")) _, _ = kubernetes.SetConfig(kubernetesProvider, kubernetes.CONFIG_AUTH_APPROVED_TENANTS, strings.Join(config.Config.Tap.Auth.ApprovedTenants, ",")) + _, _ = kubernetes.SetConfig(kubernetesProvider, kubernetes.CONFIG_AUTH_SAML_IDP_METADATA_URL, config.Config.Tap.Auth.Saml.IdpMetadataUrl) } diff --git a/config/configStructs/tapConfig.go b/config/configStructs/tapConfig.go index 50ef1e317..4fa455c7f 100644 --- a/config/configStructs/tapConfig.go +++ b/config/configStructs/tapConfig.go @@ -82,11 +82,19 @@ type ResourcesConfig struct { Tracer ResourceRequirements `yaml:"tracer" json:"tracer"` } +type SamlConfig struct { + 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"` - ApprovedEmails []string `yaml:"approvedEmails" json:"approvedEmails" default:"[]"` - ApprovedDomains []string `yaml:"approvedDomains" json:"approvedDomains" default:"[]"` - ApprovedTenants []string `yaml:"approvedTenants" json:"approvedTenants" default:"[]"` + Enabled bool `yaml:"enabled" json:"enabled" default:"false"` + Type string `yaml:"type" json:"type" default:"saml"` + ApprovedEmails []string `yaml:"approvedEmails" json:"approvedEmails" default:"[]"` + ApprovedDomains []string `yaml:"approvedDomains" json:"approvedDomains" default:"[]"` + ApprovedTenants []string `yaml:"approvedTenants" json:"approvedTenants" default:"[]"` + Saml SamlConfig `yaml:"saml" json:"saml"` } type IngressConfig struct { diff --git a/helm-chart/README.md b/helm-chart/README.md index 26babfa47..a62cf07b8 100644 --- a/helm-chart/README.md +++ b/helm-chart/README.md @@ -51,7 +51,61 @@ kubectl port-forward service/kubeshark-front 8899:80 Visit [localhost:8899](http://localhost:8899) -## Installing with Ingress (EKS) and enable Auth +## Installing with Ingress (EKS) enabled + +```shell +helm install kubeshark kubeshark/kubeshark -f values.yaml +``` + +Set this `value.yaml`: +```shell +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 +``` + +## Installing with SAML enabled + +### Prerequisites: + +##### 1. Generate X.509 certificate & key (TL;DR: https://ubuntu.com/server/docs/security-certificates) + +**Example:** +``` +openssl genrsa -out mykey.key 2048 +openssl req -new -key mykey.key -out mycsr.csr +openssl x509 -signkey mykey.key -in mycsr.csr -req -days 365 -out mycert.crt +``` + +**What you get:** +- `mycert.crt` - use it for `tap.auth.saml.x509crt` +- `mykey.key` - use it for `tap.auth.saml.x509crt` + +##### 2. Prepare your SAML IDP + +You should set up the required SAML IDP (Google, Auth0, your custom IDP, etc.) + +During setup, an IDP provider will typically request to enter: +- Metadata URL +- ACS URL (Assertion Consumer Service URL, aka Callback URL) +- SLO URL (Single Logout URL) + +Correspondingly, you will enter these (if you run the most default Kubeshark setup): +- [http://localhost:8899/saml/metadata](http://localhost:8899/saml/metadata) +- [http://localhost:8899/saml/acs](http://localhost:8899/saml/acs) +- [http://localhost:8899/saml/slo](http://localhost:8899/saml/slo) + +Otherwise, if you have `tap.ingress.enabled == true`, change protocol & domain respectively - showing example domain: +- [https://kubeshark.example.com/saml/metadata](https://kubeshark.example.com/saml/metadata) +- [https://kubeshark.example.com/saml/acs](https://kubeshark.example.com/saml/acs) +- [https://kubeshark.example.com/saml/slo](https://kubeshark.example.com/saml/slo) ```shell helm install kubeshark kubeshark/kubeshark -f values.yaml @@ -62,19 +116,28 @@ Set this `value.yaml`: tap: auth: enabled: true - approvedEmails: - - john.doe@example.com + type: saml + approvedEmails: [] approvedDomains: [] approvedTenants: [] - 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 + saml: + idpMetadataUrl: "https://tiptophelmet.us.auth0.com/samlp/metadata/MpWiDCMMB5ShU1HRnhdb1sHM6VWqdnDG" + x509crt: | + -----BEGIN CERTIFICATE----- + MIIDlTCCAn0CFFRUzMh+dZvp+FvWd4gRaiBVN8EvMA0GCSqGSIb3DQEBCwUAMIGG + MSQwIgYJKoZIhvcNAQkBFhV3ZWJtYXN0ZXJAZXhhbXBsZS5jb20wHhcNMjMxMjI4 + ................ + ZMzM7YscqZwoVhTOhrD4/5nIfOD/hTWG/MBe2Um1V1IYF8aVEllotTKTgsF6ZblA + miCOgl6lIlZy + -----END CERTIFICATE----- + x509key: | + -----BEGIN PRIVATE KEY----- + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDlgDFKsRHj+mok + euOF0IpwToOEpQGtafB75ytv3psD/tQAzEIug+rkDriVvsfcvafj0qcaTeYvnCoz + ................. + sUpBCu0E3nRJM/QB2ui5KhNR7uvPSL+kSsaEq19/mXqsL+mRi9aqy2wMEvUSU/kt + UaV5sbRtTzYLxpOSQyi8CEFA+A== + -----END PRIVATE KEY----- ``` ## Add a License @@ -147,8 +210,12 @@ Please refer to [metrics](./metrics.md) documentation for details. | `tap.annotations` | Kubernetes annotations to apply to all Kubeshark resources | `{}` | | `tap.nodeSelectorTerms` | Node selector terms | `[{"matchExpressions":[{"key":"kubernetes.io/os","operator":"In","values":["linux"]}]}]` | | `tap.auth.enabled` | Enable authentication | `false` | +| `tap.auth.type` | Authentication type (1 option available: `saml`) | `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.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.ingress.enabled` | Enable `Ingress` | `false` | | `tap.ingress.className` | Ingress class name | `""` | | `tap.ingress.host` | Host of the `Ingress` | `ks.svc.cluster.local` | diff --git a/helm-chart/templates/04-hub-deployment.yaml b/helm-chart/templates/04-hub-deployment.yaml index e83de9a2c..acb0652b5 100644 --- a/helm-chart/templates/04-hub-deployment.yaml +++ b/helm-chart/templates/04-hub-deployment.yaml @@ -64,3 +64,21 @@ spec: requests: cpu: {{ .Values.tap.resources.hub.requests.cpu }} memory: {{ .Values.tap.resources.hub.requests.memory }} + volumeMounts: + - name: saml-x509-volume + mountPath: "/etc/saml/x509" + readOnly: true + volumes: + - name: saml-x509-volume + projected: + sources: + - secret: + name: kubeshark-saml-x509-crt-secret + items: + - key: AUTH_SAML_X509_CRT + path: kubeshark.crt + - secret: + name: kubeshark-saml-x509-key-secret + items: + - key: AUTH_SAML_X509_KEY + path: kubeshark.key diff --git a/helm-chart/templates/06-front-deployment.yaml b/helm-chart/templates/06-front-deployment.yaml index d40225a86..b2c109373 100644 --- a/helm-chart/templates/06-front-deployment.yaml +++ b/helm-chart/templates/06-front-deployment.yaml @@ -28,6 +28,10 @@ spec: value: '{{ not (eq .Values.tap.defaultFilter "") | ternary .Values.tap.defaultFilter " " }}' - name: REACT_APP_AUTH_ENABLED value: '{{ .Values.tap.auth.enabled }}' + - name: REACT_APP_AUTH_TYPE + value: '{{ .Values.tap.auth.type }}' + - name: REACT_APP_AUTH_SAML_IDP_METADATA_URL + value: '{{ .Values.tap.auth.saml.idpMetadataUrl }}' - name: REACT_APP_REPLAY_DISABLED value: '{{ .Values.tap.replayDisabled }}' image: '{{ .Values.tap.docker.registry }}/front:{{ not (eq .Values.tap.docker.tag "") | ternary .Values.tap.docker.tag (printf "v%s" .Chart.Version) }}' diff --git a/helm-chart/templates/11-nginx-config-map.yaml b/helm-chart/templates/11-nginx-config-map.yaml index 1cb8ca08d..d9aca1332 100644 --- a/helm-chart/templates/11-nginx-config-map.yaml +++ b/helm-chart/templates/11-nginx-config-map.yaml @@ -16,6 +16,10 @@ data: access_log /dev/stdout; error_log /dev/stdout; + client_body_buffer_size 64k; + client_header_buffer_size 32k; + large_client_header_buffers 8 64k; + location /api { rewrite ^/api(.*)$ $1 break; proxy_pass http://kubeshark-hub; @@ -31,6 +35,17 @@ data: proxy_pass_request_headers on; } + location /saml { + rewrite ^/saml(.*)$ /saml$1 break; + proxy_pass http://kubeshark-hub; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Host $http_host; + proxy_connect_timeout 4s; + proxy_read_timeout 120s; + proxy_send_timeout 12s; + proxy_pass_request_headers on; + } + 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 b0f8e57c1..24cfcf00c 100644 --- a/helm-chart/templates/12-config-map.yaml +++ b/helm-chart/templates/12-config-map.yaml @@ -10,10 +10,15 @@ data: POD_REGEX: '{{ .Values.tap.regex }}' NAMESPACES: '{{ gt (len .Values.tap.namespaces) 0 | ternary (join "," .Values.tap.namespaces) "" }}' SCRIPTING_SCRIPTS: '{}' + INGRESS_ENABLED: '{{ .Values.tap.ingress.enabled }}' + INGRESS_HOST: '{{ .Values.tap.ingress.host }}' + PROXY_FRONT_PORT: '{{ .Values.tap.proxy.front.port }}' AUTH_ENABLED: '{{ .Values.tap.auth.enabled | ternary "true" "" }}' + AUTH_TYPE: '{{ .Values.tap.auth.type }}' AUTH_APPROVED_EMAILS: '{{ gt (len .Values.tap.auth.approvedEmails) 0 | ternary (join "," .Values.tap.auth.approvedEmails) "" }}' AUTH_APPROVED_DOMAINS: '{{ gt (len .Values.tap.auth.approvedDomains) 0 | ternary (join "," .Values.tap.auth.approvedDomains) "" }}' AUTH_APPROVED_TENANTS: '{{ gt (len .Values.tap.auth.approvedTenants) 0 | ternary (join "," .Values.tap.auth.approvedTenants) "" }}' + AUTH_SAML_IDP_METADATA_URL: '{{ .Values.tap.auth.saml.idpMetadataUrl }}' TELEMETRY_DISABLED: '{{ not .Values.tap.telemetry.enabled | ternary "true" "" }}' REPLAY_DISABLED: '{{ .Values.tap.replayDisabled | ternary "true" "" }}' GLOBAL_FILTER: {{ include "kubeshark.escapeDoubleQuotes" .Values.tap.globalFilter | quote }} diff --git a/helm-chart/templates/13-secret.yaml b/helm-chart/templates/13-secret.yaml index 8a97c85d3..d1c431c18 100644 --- a/helm-chart/templates/13-secret.yaml +++ b/helm-chart/templates/13-secret.yaml @@ -9,3 +9,33 @@ metadata: stringData: LICENSE: '{{ .Values.license }}' SCRIPTING_ENV: '{{ .Values.scripting.env | toJson }}' + +--- + +kind: Secret +apiVersion: v1 +metadata: + name: kubeshark-saml-x509-crt-secret + namespace: {{ .Release.Namespace }} + labels: + app.kubeshark.co/app: hub + {{- include "kubeshark.labels" . | nindent 4 }} +stringData: + AUTH_SAML_X509_CRT: | + {{ .Values.tap.auth.saml.x509crt | nindent 4 }} + +--- + +kind: Secret +apiVersion: v1 +metadata: + name: kubeshark-saml-x509-key-secret + namespace: {{ .Release.Namespace }} + labels: + app.kubeshark.co/app: hub + {{- include "kubeshark.labels" . | nindent 4 }} +stringData: + AUTH_SAML_X509_KEY: | + {{ .Values.tap.auth.saml.x509key | nindent 4 }} + +--- diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index 258ad4a5e..9bfabb719 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -59,9 +59,14 @@ tap: - linux auth: enabled: false + type: saml approvedEmails: [] approvedDomains: [] approvedTenants: [] + saml: + idpMetadataUrl: "" + x509crt: "" + x509key: "" ingress: enabled: false className: "" diff --git a/kubernetes/config.go b/kubernetes/config.go index 09e127c4e..587f5e91a 100644 --- a/kubernetes/config.go +++ b/kubernetes/config.go @@ -10,16 +10,21 @@ import ( ) const ( - SUFFIX_SECRET = "secret" - SUFFIX_CONFIG_MAP = "config-map" - SECRET_LICENSE = "LICENSE" - CONFIG_POD_REGEX = "POD_REGEX" - CONFIG_NAMESPACES = "NAMESPACES" - CONFIG_SCRIPTING_ENV = "SCRIPTING_ENV" - CONFIG_AUTH_ENABLED = "AUTH_ENABLED" - CONFIG_AUTH_APPROVED_EMAILS = "AUTH_APPROVED_EMAILS" - CONFIG_AUTH_APPROVED_DOMAINS = "AUTH_APPROVED_DOMAINS" - CONFIG_AUTH_APPROVED_TENANTS = "AUTH_APPROVED_TENANTS" + SUFFIX_SECRET = "secret" + SUFFIX_CONFIG_MAP = "config-map" + SECRET_LICENSE = "LICENSE" + CONFIG_POD_REGEX = "POD_REGEX" + CONFIG_NAMESPACES = "NAMESPACES" + CONFIG_SCRIPTING_ENV = "SCRIPTING_ENV" + CONFIG_INGRESS_ENABLED = "INGRESS_ENABLED" + CONFIG_INGRESS_HOST = "INGRESS_HOST" + CONFIG_PROXY_FRONT_PORT = "PROXY_FRONT_PORT" + CONFIG_AUTH_ENABLED = "AUTH_ENABLED" + CONFIG_AUTH_TYPE = "AUTH_TYPE" + CONFIG_AUTH_APPROVED_EMAILS = "AUTH_APPROVED_EMAILS" + CONFIG_AUTH_APPROVED_DOMAINS = "AUTH_APPROVED_DOMAINS" + CONFIG_AUTH_APPROVED_TENANTS = "AUTH_APPROVED_TENANTS" + CONFIG_AUTH_SAML_IDP_METADATA_URL = "AUTH_SAML_IDP_METADATA_URL" ) func SetSecret(provider *Provider, key string, value string) (updated bool, err error) {