From 2fdf66a41f9569f44c8d54c29ca8e1b46b36613d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B4nio=20Franco?= <13881523+antoniomrfranco@users.noreply.github.com> Date: Wed, 26 Jun 2024 17:05:25 -0300 Subject: [PATCH] ENG-13649: Add initial Helm template (#1) * Add initial Helm template * Update ports to use map instead of list of int * Update Snowflake values * Some fixes after testing * Update README and add pre-commit script (#2) * Add deployment properties (#3) * Bump sidecar version & fix typo * Add Role and RoleBinding * Delete templates/ingress.yaml * Set CYRAL_SIDECAR_DEPLOYMENT_PROPERTIES env * Add license to missing files * Fix typo on error msg * Keep existing certs on updates * Use same index pattern for all ports * Place Cyral parameters docs close to helm values * Remove support for old K8s versions * Add permissions to get/watch secrets * Add newline at end of files * Update default registry and repository * Update docs with required secret keys * Simplify Snowflake envs --------- Co-authored-by: Wilson de Carvalho <796900+wcmjunior@users.noreply.github.com> --- .pre-commit-config.yaml | 13 + Chart.yaml | 20 ++ README.md | 168 +++++++++-- templates/NOTES.txt | 14 + templates/_helpers.tpl | 84 ++++++ templates/ca-cert-secret.yaml | 23 ++ templates/common/_affinities.tpl | 137 +++++++++ templates/common/_images.tpl | 64 +++++ templates/common/_labels.tpl | 44 +++ templates/common/_names.tpl | 43 +++ templates/common/_secrets.tpl | 30 ++ templates/common/_tplvalues.tpl | 37 +++ templates/credentials-secret.yaml | 20 ++ templates/deployment.yaml | 139 ++++++++++ templates/rbac/role.yaml | 33 +++ templates/rbac/rolebinding.yaml | 24 ++ templates/rbac/serviceaccount.yaml | 18 ++ templates/selfsigned-cert-secret.yaml | 23 ++ templates/service.yaml | 50 ++++ values.yaml | 383 ++++++++++++++++++++++++++ 20 files changed, 1337 insertions(+), 30 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 Chart.yaml create mode 100644 templates/NOTES.txt create mode 100644 templates/_helpers.tpl create mode 100644 templates/ca-cert-secret.yaml create mode 100644 templates/common/_affinities.tpl create mode 100644 templates/common/_images.tpl create mode 100644 templates/common/_labels.tpl create mode 100644 templates/common/_names.tpl create mode 100644 templates/common/_secrets.tpl create mode 100644 templates/common/_tplvalues.tpl create mode 100644 templates/credentials-secret.yaml create mode 100644 templates/deployment.yaml create mode 100644 templates/rbac/role.yaml create mode 100644 templates/rbac/rolebinding.yaml create mode 100644 templates/rbac/serviceaccount.yaml create mode 100644 templates/selfsigned-cert-secret.yaml create mode 100644 templates/service.yaml create mode 100644 values.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c1079ef --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,13 @@ +# Copyright Cyral, Inc. +# SPDX-License-Identifier: APACHE-2.0 + +repos: +- repo: https://github.com/bitnami/readme-generator-for-helm + rev: "main" + hooks: + - id: helm-readme-generator + # in order to run helm-readme-generator only once + pass_filenames: false + always_run: true + # default args are [--readme=chart/README.md, --values=chart/values.yaml] + args: [--readme=README.md, --values=values.yaml] diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 0000000..fbeae68 --- /dev/null +++ b/Chart.yaml @@ -0,0 +1,20 @@ +# Copyright Cyral, Inc. +# SPDX-License-Identifier: APACHE-2.0 + +annotations: + licenses: Apache-2.0 +apiVersion: v2 +appVersion: 4.13.9 +description: Cyral Sidecar. +home: https://cyral.com +keywords: +- cyral +- sidecar +- database security +maintainers: +- name: Cyral, Inc. + url: https://github.com/cyralinc/helm-sidecar +name: cyral-sidecar +sources: +- https://github.com/cyralinc/helm-sidecar +version: 4.13.9 diff --git a/README.md b/README.md index fa25ab0..ad2d4bf 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,146 @@ -# template-repo + -Use this template to create new repositories in our organization. **After** creating the new repo, follow the steps below: +# Cyral Sidecar -* [Create a personal access token in your GitHub account](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) and enable all `repo` scope permissions; -* Assign the access token value and the repository name to the following script and run it: +## TL;DR +```console +helm install cyral-sidecar oci://public.ecr.aws/cyral/helm/sidecar ``` -ACCESS_TOKEN=your_access_token -REPO=your_repo_name - -echo "Defining repo configuration settings..." -curl -X PATCH \ - -H "Authorization: token $ACCESS_TOKEN" \ - -H "Accept: application/vnd.github.v3+json" \ - -d '{"has_wiki":false,"has_projects":false,"has_issues":false,"allow_squash_merge":true,"allow_merge_commit":false,"allow_rebase_merge":false,"delete_branch_on_merge":true,"allow_auto_merge":true,"auto_merge":false}' \ - https://api.github.com/repos/cyralinc/${REPO} - -echo "Defining protection rules for 'main' branch..." -curl -X PUT \ - -H "Authorization: token $ACCESS_TOKEN" \ - -H "Accept: application/vnd.github.luke-cage-preview+json" \ - -d '{"required_status_checks":null,"enforce_admins":true,"required_pull_request_reviews":{"required_approving_review_count":1},"restrictions":null}' \ - https://api.github.com/repos/cyralinc/${REPO}/branches/main/protection + +## Introduction + +Helm chart to deploy Cyral Sidecar. + +## Prerequisites + +- Kubernetes 1.23+ +- Helm 3.8.0+ + +## Installing the Chart + +To install the chart with the release name `cyral-sidecar`: + +```console +helm install cyral-sidecar oci://REGISTRY_NAME/REPOSITORY_NAME/%%CHART_NAME%% ``` -* Update this file and define a `README.md` that suits your new repository. +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Cyral, you need to use `REGISTRY_NAME=cyralinc.docker.io` and `REPOSITORY_NAME=cyralcharts`. + +The command deploys Cyral Sidecar on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `cyral-sidecar` deployment: + +```console +helm delete cyral-sidecar +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Parameters + +### Global parameters + +| Name | Description | Value | +| ------------------------- | ----------------------------------------------- | ----- | +| `global.imageRegistry` | Global Docker image registry | `""` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | + +### Common parameters + +| Name | Description | Value | +| ------------------- | ----------------------------------------------------------------------------------------------------------------- | --------------- | +| `kubeVersion` | Force target Kubernetes version (using Helm capabilities if not set) | `""` | +| `nameOverride` | String to partially override common.names.fullname template with a string (will prepend the release name) | `""` | +| `fullnameOverride` | String to fully override common.names.fullname template with a string | `""` | +| `commonAnnotations` | Common annotations to add to all Cyral Sidecar resources (sub-charts are not considered). Evaluated as a template | `{}` | +| `commonLabels` | Common labels to add to all Cyral Sidecar resources (sub-charts are not considered). Evaluated as a template | `{}` | +| `clusterDomain` | Kubernetes cluster domain | `cluster.local` | + +### Cyral Sidecar deployment parameters + +| Name | Description | Value | +| --------------------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------- | +| `podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `hard` | +| `podAffinityPreset` | Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `nodeAffinityPreset.type` | Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `nodeAffinityPreset.key` | Node label key to match Ignored if `affinity` is set. | `""` | +| `nodeAffinityPreset.values` | Node label values to match. Ignored if `affinity` is set. | `[]` | +| `affinity` | Affinity for pod assignment | `{}` | +| `image.registry` | Cyral Sidecar image registry | `public.ecr.aws/cyral` | +| `image.repository` | Cyral Sidecar image repository | `cyral-sidecar` | +| `image.digest` | Cyral Sidecar image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `image.pullPolicy` | Cyral Sidecar image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Cyral Sidecar image pull secrets | `[]` | +| `image.debug` | Enable image debug mode | `false` | +| `replicaCount` | Number of Cyral Sidecar replicas to deploy | `1` | +| `extraEnvVars` | Extra environment variables to be set on Cyral Sidecar containers | `[]` | +| `extraEnvVarsCM` | ConfigMap with extra environment variables | `""` | +| `extraEnvVarsSecret` | Secret with extra environment variables | `""` | + +### Cyral Sidecar deployment parameters + +| Name | Description | Value | +| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ---------------- | +| `resources` | Set container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` | +| `nodeSelector` | Node labels for pod assignment. Evaluated as a template. | `{}` | +| `tolerations` | Tolerations for pod assignment. Evaluated as a template. | `[]` | +| `extraVolumes` | Array of extra volumes to be added to the Cyral Sidecar deployment (evaluated as template). Requires setting `extraVolumeMounts` | `[]` | +| `serviceAccount.create` | Enable creation of ServiceAccount for Cyral Sidecar pod | `true` | +| `serviceAccount.name` | The name of the ServiceAccount to use. | `""` | +| `serviceAccount.annotations` | Annotations for service account. Evaluated as a template. | `{}` | +| `serviceAccount.automountServiceAccountToken` | Auto-mount the service account token in the pod | `false` | +| `rbac.create` | Create Role and RoleBinding | `true` | +| `rbac.rules` | Custom RBAC rules to set | `[]` | +| `podSecurityContext.enabled` | Enabled Cyral Sidecar pods' Security Context | `false` | +| `podSecurityContext.fsGroupChangePolicy` | Set filesystem group change policy | `Always` | +| `podSecurityContext.sysctls` | Set kernel settings using the sysctl interface | `[]` | +| `podSecurityContext.supplementalGroups` | Set filesystem extra groups | `[]` | +| `podSecurityContext.fsGroup` | Set Cyral Sidecar pod's Security Context fsGroup | `1001` | +| `containerSecurityContext.enabled` | Enabled containers' Security Context | `false` | +| `containerSecurityContext.seLinuxOptions` | Set SELinux options in container | `nil` | +| `containerSecurityContext.runAsUser` | Set containers' Security Context runAsUser | `1001` | +| `containerSecurityContext.runAsNonRoot` | Set container's Security Context runAsNonRoot | `true` | +| `containerSecurityContext.privileged` | Set container's Security Context privileged | `false` | +| `containerSecurityContext.readOnlyRootFilesystem` | Set container's Security Context readOnlyRootFilesystem | `false` | +| `containerSecurityContext.allowPrivilegeEscalation` | Set container's Security Context allowPrivilegeEscalation | `false` | +| `containerSecurityContext.capabilities.drop` | List of capabilities to be dropped | `["ALL"]` | +| `containerSecurityContext.seccompProfile.type` | Set container's Security Context seccomp profile | `RuntimeDefault` | +| `containerPorts` | Map of all ports inside Cyral Sidecar container | `{}` | +| `extraContainerPorts` | Array of additional container ports for the Cyral Sidecar container | `[]` | +| `service.type` | Service type | `LoadBalancer` | +| `service.ports` | Map of Cyral Sidecar service ports | `{}` | +| `service.nodePorts` | Specify the nodePort(s) value(s) for the LoadBalancer and NodePort service types. | `{}` | +| `service.targetPort` | Target port reference value for the Loadbalancer service types can be specified explicitly. | `{}` | +| `service.clusterIP` | Cyral Sidecar service Cluster IP | `""` | +| `service.loadBalancerIP` | LoadBalancer service IP address | `""` | +| `service.loadBalancerSourceRanges` | Cyral Sidecar service Load Balancer sources | `[]` | +| `service.loadBalancerClass` | service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) | `""` | +| `service.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | +| `service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `service.annotations` | Service annotations | `{}` | +| `service.externalTrafficPolicy` | Enable client source IP preservation | `Cluster` | -## Setting up your CI +### Cyral configuration parameters -We use GitHub Actions for CI. The `.github/workflows` folder in this template -repo contains the minimal set of workflows for any repository. However, it is -likely that you will need to use other workflows if you need, for instance, a -docker image build as part of the CI. Please check out the README in -[devops-github-workflows](https://github.com/cyralinc/devops-github-workflows) -and the folder -[client-workflows](https://github.com/cyralinc/devops-github-workflows/tree/main/client-workflows) -for more information. +| Name | Description | Value | +| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | +| `cyral.sidecarId` | Sidecar identifier | `""` | +| `cyral.controlPlane` | Address of the control plane - .cyral.com | `""` | +| `cyral.credentials.existingSecret` | Name of an existing Kubernetes secret containing client ID and client secret. The secret must contain the `clientId` and `clientSecret` keys. | `""` | +| `cyral.credentials.clientId` | The client ID assigned to the sidecar. Optional - required only if existingSecret is not provided. | `""` | +| `cyral.credentials.clientSecret` | The client secret assigned to the sidecar. Optional - required only if existingSecret is not provided. | `""` | +| `cyral.sidecar.dnsName` | Fully qualified domain name that will be used to access the Cyral Sidecar | `""` | +| `cyral.sidecar.certificates.tls.existingSecret` | Name of an existing Kubernetes secret containing a private key and a certificate to terminate TLS connections. | `""` | +| `cyral.sidecar.certificates.ca.existingSecret` | Name of an existing Kubernetes secret containing a private key and a certificate for the internal CA. | `""` | +| `cyral.sidecar.snowflake.SSOLoginURL` | The IdP SSO URL for the IdP being used with Snowflake. | `""` | +| `cyral.sidecar.snowflake.idpCertificate` | The certificate used to verify SAML assertions from the IdP being used with Snowflake. Enter this value as a one-line string with literal new line characters (\n) specifying the line breaks. | `""` | +| `cyral.sidecar.snowflake.sidecarIdpCertificate` | The public certificate used to verify signatures for SAML Assertions generated by the sidecar. Required if using SSO with Snowflake. | `""` | +| `cyral.sidecar.snowflake.sidecarIdpPrivateKey` | The private key used to sign SAML Assertions generated by the sidecar. Required if using SSO with Snowflake. | `""` | +| `cyral.deploymentProperties.cloud` | Cloud provider where the Cyral Sidecar is hosted. | `""` | +| `cyral.deploymentProperties.endpoint` | Fully qualified domain name that will be used to access the Cyral Sidecar. | `""` | +| `cyral.deploymentProperties.deploymentType` | Deployment type choosen to deploy the Cyral Sidecar. Defaults to `helm-kubernetes`. | `helm-kubernetes` | diff --git a/templates/NOTES.txt b/templates/NOTES.txt new file mode 100644 index 0000000..fd435e7 --- /dev/null +++ b/templates/NOTES.txt @@ -0,0 +1,14 @@ +{{- /* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/ -}} + +CHART NAME: {{ .Chart.Name }} +CHART VERSION: {{ .Chart.Version }} +APP VERSION: {{ .Chart.AppVersion }} + +** Please be patient while the chart is being deployed ** + +Cyral Sidecar can be accessed through the following DNS name from within your cluster: + + {{ include "common.names.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl new file mode 100644 index 0000000..e4e528b --- /dev/null +++ b/templates/_helpers.tpl @@ -0,0 +1,84 @@ +{{/* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* +Return the proper Cyral Sidecar image name +*/}} +{{- define "cyral.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "cyral.imagePullSecrets" -}} +{{- include "common.images.pullSecrets" (dict "images" (list .Values.image) "global" .Values.global) -}} +{{- end -}} + +{{/* + Create the name of the service account to use + */}} +{{- define "cyral.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "common.names.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Return true if a secret for Cyral Sidecar credentials should be created +*/}} +{{- define "cyral.createCredentialsSecret" -}} +{{- if and (not .Values.cyral.credentials.existingSecret) .Values.cyral.credentials.clientId .Values.cyral.credentials.clientSecret -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Get Cyral Sidecar credentials secret +*/}} +{{- define "cyral.credentials.secretName" -}} +{{- if (include "cyral.createCredentialsSecret" .) -}} + {{- printf "%s-credentials-secret" (include "common.names.fullname" .) -}} +{{- else if not (empty .Values.cyral.credentials.existingSecret) -}} + {{- tpl .Values.cyral.credentials.existingSecret $ -}} +{{- else -}} + {{- fail "cyral.credentials.clientId and cyral.credentials.clientSecret are required if cyral.credentials.existingSecret is empty." -}} +{{- end -}} +{{- end -}} + +{{/* +Get Cyral Sidecar TLS cert secret +*/}} +{{- define "cyral.certificates.tls.secretName" -}} +{{- if not .Values.cyral.sidecar.certificates.tls.existingSecret -}} + {{- printf "%s-selfsigned-certificate" (include "common.names.fullname" .) -}} +{{- else -}} + {{- tpl .Values.cyral.sidecar.certificates.tls.existingSecret $ -}} +{{- end -}} +{{- end -}} + +{{/* +Get Cyral Sidecar CA cert secret +*/}} +{{- define "cyral.certificates.ca.secretName" -}} +{{- if not .Values.cyral.sidecar.certificates.ca.existingSecret -}} + {{- printf "%s-ca-certificate" (include "common.names.fullname" .) -}} +{{- else -}} + {{- tpl .Values.cyral.sidecar.certificates.ca.existingSecret $ -}} +{{- end -}} +{{- end -}} + +{{/* +Get the DNS name to access Cyral Sidecar +*/}} +{{- define "cyral.dnsName" -}} +{{- if .Values.cyral.sidecar.dnsName -}} + {{- tpl .Values.cyral.sidecar.dnsName $ -}} +{{- else -}} + {{- include "common.names.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain -}} +{{- end -}} +{{- end -}} diff --git a/templates/ca-cert-secret.yaml b/templates/ca-cert-secret.yaml new file mode 100644 index 0000000..764546b --- /dev/null +++ b/templates/ca-cert-secret.yaml @@ -0,0 +1,23 @@ +{{/* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if not .Values.cyral.sidecar.certificates.ca.existingSecret -}} +{{- $secretName := printf "%s-ca-certificate" (include "common.names.fullname" .) }} +{{- $hostname := .Values.cyral.sidecar.dnsName | default "sidecar.app.cyral.com" }} +{{- $ca := genCA $hostname 3650 }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ $.Release.Namespace }} + labels: {{- include "common.labels.standard" ( dict "customLabels" $.Values.commonLabels "context" $ ) | nindent 4 }} + {{- if $.Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $ca.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $ca.Key "context" $) }} +{{- end -}} diff --git a/templates/common/_affinities.tpl b/templates/common/_affinities.tpl new file mode 100644 index 0000000..1031af4 --- /dev/null +++ b/templates/common/_affinities.tpl @@ -0,0 +1,137 @@ +{{/* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* +Return a soft nodeAffinity definition +{{ include "common.affinities.nodes.soft" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.soft" -}} +preferredDuringSchedulingIgnoredDuringExecution: + - preference: + matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} + weight: 1 +{{- end -}} + +{{/* +Return a hard nodeAffinity definition +{{ include "common.affinities.nodes.hard" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.hard" -}} +requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} +{{- end -}} + +{{/* +Return a nodeAffinity definition +{{ include "common.affinities.nodes" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.nodes.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.nodes.hard" . -}} + {{- end -}} +{{- end -}} + +{{/* +Return a topologyKey definition +{{ include "common.affinities.topologyKey" (dict "topologyKey" "BAR") -}} +*/}} +{{- define "common.affinities.topologyKey" -}} +{{ .topologyKey | default "kubernetes.io/hostname" -}} +{{- end -}} + +{{/* +Return a soft podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.soft" (dict "component" "FOO" "customLabels" .Values.podLabels "extraMatchLabels" .Values.extraMatchLabels "topologyKey" "BAR" "extraPodAffinityTerms" .Values.extraPodAffinityTerms "context" $) -}} +*/}} +{{- define "common.affinities.pods.soft" -}} +{{- $component := default "" .component -}} +{{- $customLabels := default (dict) .customLabels -}} +{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} +{{- $extraPodAffinityTerms := default (list) .extraPodAffinityTerms -}} +preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" ( dict "customLabels" $customLabels "context" .context )) | nindent 10 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := $extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + weight: 1 + {{- range $extraPodAffinityTerms }} + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" ( dict "customLabels" $customLabels "context" $.context )) | nindent 10 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := .extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + weight: {{ .weight | default 1 -}} + {{- end -}} +{{- end -}} + +{{/* +Return a hard podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.hard" (dict "component" "FOO" "customLabels" .Values.podLabels "extraMatchLabels" .Values.extraMatchLabels "topologyKey" "BAR" "extraPodAffinityTerms" .Values.extraPodAffinityTerms "context" $) -}} +*/}} +{{- define "common.affinities.pods.hard" -}} +{{- $component := default "" .component -}} +{{- $customLabels := default (dict) .customLabels -}} +{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} +{{- $extraPodAffinityTerms := default (list) .extraPodAffinityTerms -}} +requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" ( dict "customLabels" $customLabels "context" .context )) | nindent 8 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := $extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + {{- range $extraPodAffinityTerms }} + - labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" ( dict "customLabels" $customLabels "context" $.context )) | nindent 8 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := .extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + {{- end -}} +{{- end -}} + +{{/* +Return a podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.pods" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.pods.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.pods.hard" . -}} + {{- end -}} +{{- end -}} diff --git a/templates/common/_images.tpl b/templates/common/_images.tpl new file mode 100644 index 0000000..84240a4 --- /dev/null +++ b/templates/common/_images.tpl @@ -0,0 +1,64 @@ +{{/* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* +Return the proper image name +{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" .Values.global ) }} +*/}} +{{- define "common.images.image" -}} +{{- $registryName := .imageRoot.registry -}} +{{- $repositoryName := .imageRoot.repository -}} +{{- $separator := ":" -}} +{{- $termination := .imageRoot.tag | toString -}} +{{- if .global }} + {{- if .global.imageRegistry }} + {{- $registryName = .global.imageRegistry -}} + {{- end -}} +{{- end -}} +{{- if .imageRoot.digest }} + {{- $separator = "@" -}} + {{- $termination = .imageRoot.digest | toString -}} +{{- end -}} +{{- if $registryName }} + {{- printf "%s/%s%s%s" $registryName $repositoryName $separator $termination -}} +{{- else -}} + {{- printf "%s%s%s" $repositoryName $separator $termination -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead) +{{ include "common.images.pullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global) }} +*/}} +{{- define "common.images.pullSecrets" -}} + {{- $pullSecrets := list }} + + {{- if .global }} + {{- range .global.imagePullSecrets -}} + {{- if kindIs "map" . -}} + {{- $pullSecrets = append $pullSecrets .name -}} + {{- else -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end }} + {{- end -}} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- if kindIs "map" . -}} + {{- $pullSecrets = append $pullSecrets .name -}} + {{- else -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) }} +imagePullSecrets: + {{- range $pullSecrets | uniq }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/templates/common/_labels.tpl b/templates/common/_labels.tpl new file mode 100644 index 0000000..9aab55f --- /dev/null +++ b/templates/common/_labels.tpl @@ -0,0 +1,44 @@ +{{/* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* +Kubernetes standard labels +{{ include "common.labels.standard" (dict "customLabels" .Values.commonLabels "context" $) -}} +*/}} +{{- define "common.labels.standard" -}} +{{- if and (hasKey . "customLabels") (hasKey . "context") -}} +{{- $default := dict "app.kubernetes.io/name" (include "common.names.name" .context) "helm.sh/chart" (include "common.names.chart" .context) "app.kubernetes.io/instance" .context.Release.Name "app.kubernetes.io/managed-by" .context.Release.Service -}} +{{- with .context.Chart.AppVersion -}} +{{- $_ := set $default "app.kubernetes.io/version" . -}} +{{- end -}} +{{ template "common.tplvalues.merge" (dict "values" (list .customLabels $default) "context" .context) }} +{{- else -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +helm.sh/chart: {{ include "common.names.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with .Chart.AppVersion }} +app.kubernetes.io/version: {{ . | quote }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Labels used on immutable fields such as deploy.spec.selector.matchLabels or svc.spec.selector +{{ include "common.labels.matchLabels" (dict "customLabels" .Values.podLabels "context" $) -}} + +We don't want to loop over custom labels appending them to the selector +since it's very likely that it will break deployments, services, etc. +However, it's important to overwrite the standard labels if the user +overwrote them on metadata.labels fields. +*/}} +{{- define "common.labels.matchLabels" -}} +{{- if and (hasKey . "customLabels") (hasKey . "context") -}} +{{ merge (pick (include "common.tplvalues.render" (dict "value" .customLabels "context" .context) | fromYaml) "app.kubernetes.io/name" "app.kubernetes.io/instance") (dict "app.kubernetes.io/name" (include "common.names.name" .context) "app.kubernetes.io/instance" .context.Release.Name ) | toYaml }} +{{- else -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} +{{- end -}} diff --git a/templates/common/_names.tpl b/templates/common/_names.tpl new file mode 100644 index 0000000..d4997ed --- /dev/null +++ b/templates/common/_names.tpl @@ -0,0 +1,43 @@ +{{/* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "common.names.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "common.names.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "common.names.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts. +*/}} +{{- define "common.names.namespace" -}} +{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/templates/common/_secrets.tpl b/templates/common/_secrets.tpl new file mode 100644 index 0000000..2d564ae --- /dev/null +++ b/templates/common/_secrets.tpl @@ -0,0 +1,30 @@ +{{/* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* +Reuses the value from an existing secret, otherwise sets its value to a default value. + +Usage: +{{ include "common.secrets.lookup" (dict "secret" "secret-name" "key" "keyName" "defaultValue" .Values.myValue "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - defaultValue - String - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - context - Context - Required - Parent context. + +*/}} +{{- define "common.secrets.lookup" -}} +{{- $value := "" -}} +{{- $secretData := (lookup "v1" "Secret" (include "common.names.namespace" .context) .secret).data -}} +{{- if and $secretData (hasKey $secretData .key) -}} + {{- $value = index $secretData .key -}} +{{- else if .defaultValue -}} + {{- $value = .defaultValue | toString | b64enc -}} +{{- end -}} +{{- if $value -}} +{{- printf "%s" $value -}} +{{- end -}} +{{- end -}} diff --git a/templates/common/_tplvalues.tpl b/templates/common/_tplvalues.tpl new file mode 100644 index 0000000..2ab8f99 --- /dev/null +++ b/templates/common/_tplvalues.tpl @@ -0,0 +1,37 @@ +{{/* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* +Renders a value that contains template perhaps with scope if the scope is present. +Usage: +{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $ ) }} +{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $ "scope" $app ) }} +*/}} +{{- define "common.tplvalues.render" -}} +{{- $value := typeIs "string" .value | ternary .value (.value | toYaml) }} +{{- if contains "{{" (toJson .value) }} + {{- if .scope }} + {{- tpl (cat "{{- with $.RelativeScope -}}" $value "{{- end }}") (merge (dict "RelativeScope" .scope) .context) }} + {{- else }} + {{- tpl $value .context }} + {{- end }} +{{- else }} + {{- $value }} +{{- end }} +{{- end -}} + +{{/* +Merge a list of values that contains template after rendering them. +Merge precedence is consistent with http://masterminds.github.io/sprig/dicts.html#merge-mustmerge +Usage: +{{ include "common.tplvalues.merge" ( dict "values" (list .Values.path.to.the.Value1 .Values.path.to.the.Value2) "context" $ ) }} +*/}} +{{- define "common.tplvalues.merge" -}} +{{- $dst := dict -}} +{{- range .values -}} +{{- $dst = include "common.tplvalues.render" (dict "value" . "context" $.context "scope" $.scope) | fromYaml | merge $dst -}} +{{- end -}} +{{ $dst | toYaml }} +{{- end -}} diff --git a/templates/credentials-secret.yaml b/templates/credentials-secret.yaml new file mode 100644 index 0000000..2795e6c --- /dev/null +++ b/templates/credentials-secret.yaml @@ -0,0 +1,20 @@ +{{- /* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if (include "cyral.createCredentialsSecret" .) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-credentials-secret" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + clientId: {{ .Values.cyral.credentials.clientId | b64enc | quote }} + clientSecret: {{ .Values.cyral.credentials.clientSecret | b64enc | quote }} +{{- end }} diff --git a/templates/deployment.yaml b/templates/deployment.yaml new file mode 100644 index 0000000..76be3aa --- /dev/null +++ b/templates/deployment.yaml @@ -0,0 +1,139 @@ +{{- /* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replicaCount }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.podLabels .Values.commonLabels ) "context" . ) }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 6 }} + template: + metadata: + labels: {{- include "common.labels.standard" ( dict "customLabels" $podLabels "context" $ ) | nindent 8 }} + annotations: + {{- if .Values.podAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + {{- include "cyral.imagePullSecrets" . | nindent 6 }} + serviceAccountName: {{ template "cyral.serviceAccountName" . }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset "customLabels" $podLabels "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset "customLabels" $podLabels "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.tolerations "context" $) | nindent 8 }} + {{- end }} + volumes: + containers: + - name: cyral-sidecar + image: {{ include "cyral.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.args "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + env: + - name: CYRAL_SIDECAR_DEPLOYMENT_PROPERTIES + value: {{ .Values.cyral.deploymentProperties | toJson | quote }} + - name: CYRAL_SIDECAR_CLOUD_PROVIDER + value: {{ .Values.cyral.deploymentProperties.cloud }} + - name: CYRAL_CONTROL_PLANE + value: {{ .Values.cyral.controlPlane | required "cyral.controlPlane is required." }} + - name: CYRAL_SIDECAR_ID + value: {{ .Values.cyral.sidecarId | required "cyral.sidecarId is required" }} + - name: CYRAL_SIDECAR_ENDPOINT + value: {{ include "cyral.dnsName" $ }} + - name: CYRAL_SIDECAR_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ include "cyral.credentials.secretName" $ }} + key: clientId + - name: CYRAL_SIDECAR_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ include "cyral.credentials.secretName" $ }} + key: clientSecret + - name: CYRAL_CERTIFICATE_MANAGER_TLS_KEY + valueFrom: + secretKeyRef: + name: {{ include "cyral.certificates.tls.secretName" $ }} + key: tls.key + - name: CYRAL_CERTIFICATE_MANAGER_TLS_CERT + valueFrom: + secretKeyRef: + name: {{ include "cyral.certificates.tls.secretName" $ }} + key: tls.crt + - name: CYRAL_CERTIFICATE_MANAGER_CA_KEY + valueFrom: + secretKeyRef: + name: {{ include "cyral.certificates.ca.secretName" $ }} + key: tls.key + - name: CYRAL_CERTIFICATE_MANAGER_CA_CERT + valueFrom: + secretKeyRef: + name: {{ include "cyral.certificates.ca.secretName" $ }} + key: tls.crt + - name: CYRAL_SSO_LOGIN_URL + value: {{ .Values.cyral.sidecar.snowflake.SSOLoginURL | default "" }} + - name: CYRAL_IDP_CERTIFICATE + value: {{ .Values.cyral.sidecar.snowflake.idpCertificate | default "" }} + - name: CYRAL_SIDECAR_IDP_PUBLIC_CERT + value: {{ .Values.cyral.sidecar.snowflake.sidecarIdpCertificate | default "" }} + - name: CYRAL_SIDECAR_IDP_PRIVATE_KEY + value: {{ .Values.cyral.sidecar.snowflake.sidecarIdpPrivateKey | default "" }} + {{- if .Values.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + envFrom: + {{- if .Values.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.extraEnvVarsCM "context" $) }} + {{- end }} + {{- if .Values.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.extraEnvVarsSecret "context" $) }} + {{- end }} + ports: + {{- range $key, $value := .Values.containerPorts }} + - name: {{ $key }} + containerPort: {{ $value }} + {{- end }} + {{- if .Values.extraContainerPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraContainerPorts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.extraVolumeMounts }} + {{- include "common.tplvalues.render" ( dict "value" .Values.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} diff --git a/templates/rbac/role.yaml b/templates/rbac/role.yaml new file mode 100644 index 0000000..988ab92 --- /dev/null +++ b/templates/rbac/role.yaml @@ -0,0 +1,33 @@ +{{- /* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "" + resources: + - "secrets" + verbs: + - "get" + - "watch" + - apiGroups: + - "" + resources: + - "services" + verbs: + - "get" + {{- if .Values.rbac.rules }} + {{- include "common.tplvalues.render" ( dict "value" .Values.rbac.rules "context" $ ) | nindent 2 }} + {{- end }} +{{- end -}} diff --git a/templates/rbac/rolebinding.yaml b/templates/rbac/rolebinding.yaml new file mode 100644 index 0000000..cf8cb4d --- /dev/null +++ b/templates/rbac/rolebinding.yaml @@ -0,0 +1,24 @@ +{{- /* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "common.names.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ include "cyral.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end -}} diff --git a/templates/rbac/serviceaccount.yaml b/templates/rbac/serviceaccount.yaml new file mode 100644 index 0000000..f5b8201 --- /dev/null +++ b/templates/rbac/serviceaccount.yaml @@ -0,0 +1,18 @@ +{{- /* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "cyral.serviceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if or .Values.serviceAccount.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.serviceAccount.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end -}} diff --git a/templates/selfsigned-cert-secret.yaml b/templates/selfsigned-cert-secret.yaml new file mode 100644 index 0000000..8dad477 --- /dev/null +++ b/templates/selfsigned-cert-secret.yaml @@ -0,0 +1,23 @@ +{{/* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if not .Values.cyral.sidecar.certificates.tls.existingSecret -}} +{{- $secretName := printf "%s-selfsigned-certificate" (include "common.names.fullname" .) }} +{{- $hostname := .Values.cyral.sidecar.dnsName | default "sidecar.app.cyral.com" }} +{{- $cert := genSelfSignedCert $hostname nil (list (printf "*.%s" $hostname)) 3650 }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ $.Release.Namespace }} + labels: {{- include "common.labels.standard" ( dict "customLabels" $.Values.commonLabels "context" $ ) | nindent 4 }} + {{- if $.Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} +{{- end -}} diff --git a/templates/service.yaml b/templates/service.yaml new file mode 100644 index 0000000..41387fe --- /dev/null +++ b/templates/service.yaml @@ -0,0 +1,50 @@ +{{- /* +Copyright Cyral, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + annotations: + {{- if or .Values.service.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.service.annotations .Values.commonAnnotations ) "context" . ) }} + {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + {{- if and .Values.service.clusterIP (eq .Values.service.type "ClusterIP") }} + clusterIP: {{ .Values.service.clusterIP }} + {{- end }} + {{- if .Values.service.sessionAffinity }} + sessionAffinity: {{ .Values.service.sessionAffinity }} + {{- end }} + {{- if .Values.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + {{- if or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerSourceRanges)) }} + loadBalancerSourceRanges: {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP)) }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.service.loadBalancerClass }} + {{- end }} + ports: + {{- range $key, $value := .Values.containerPorts }} + - name: {{ $key }} + port: {{ $value }} + targetPort: {{ get $.Values.service.targetPort $key }} + {{- if and (or (eq $.Values.service.type "NodePort") (eq $.Values.service.type "LoadBalancer")) (not (empty (get $.Values.service.nodePorts $key))) }} + nodePort: {{ get $.Values.service.nodePorts $key }} + {{- end }} + {{- end }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.podLabels .Values.commonLabels ) "context" . ) }} + selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} diff --git a/values.yaml b/values.yaml new file mode 100644 index 0000000..d8a0297 --- /dev/null +++ b/values.yaml @@ -0,0 +1,383 @@ +# Copyright Cyral, Inc. +# SPDX-License-Identifier: APACHE-2.0 + +## @section Global parameters +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## + +## @param global.imageRegistry Global Docker image registry +## @param global.imagePullSecrets [array] Global Docker registry secret names as an array +## +global: + imageRegistry: "" + ## E.g. + ## imagePullSecrets: + ## - myRegistryKeySecretName + ## + imagePullSecrets: [] +## @section Common parameters +## + +## @param kubeVersion Force target Kubernetes version (using Helm capabilities if not set) +## +kubeVersion: "" +## @param nameOverride String to partially override common.names.fullname template with a string (will prepend the release name) +## +nameOverride: "" +## @param fullnameOverride String to fully override common.names.fullname template with a string +## +fullnameOverride: "" +## @param commonAnnotations Common annotations to add to all Cyral Sidecar resources (sub-charts are not considered). Evaluated as a template +## +commonAnnotations: {} +## @param commonLabels Common labels to add to all Cyral Sidecar resources (sub-charts are not considered). Evaluated as a template +## +commonLabels: {} +## @param clusterDomain Kubernetes cluster domain +## +clusterDomain: cluster.local +## @section Cyral Sidecar deployment parameters + +## @param podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## +podAntiAffinityPreset: hard +## @param podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` +## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## +podAffinityPreset: "" +## Node affinity preset +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity +## +nodeAffinityPreset: + ## @param nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param nodeAffinityPreset.key Node label key to match Ignored if `affinity` is set. + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## @param nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set. + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] +## @param affinity Affinity for pod assignment +## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set +## +affinity: {} +## Cyral Sidecar image +## ref: https://hub.docker.com/r/cyral/sidecar/tags/ +## @param image.registry [default: public.ecr.aws/cyral] Cyral Sidecar image registry +## @param image.repository [default: cyral-sidecar] Cyral Sidecar image repository +## @skip image.tag Cyral Sidecar image tag (immutable tags are recommended) +## @param image.digest Cyral Sidecar image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag +## @param image.pullPolicy Cyral Sidecar image pull policy +## @param image.pullSecrets Cyral Sidecar image pull secrets +## @param image.debug Enable image debug mode +## +image: + registry: public.ecr.aws/cyral + repository: cyral-sidecar + tag: v4.13.9 + digest: "" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: https://kubernetes.io/docs/concepts/containers/images/#pre-pulled-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Enable debug mode + ## + debug: false +## @param replicaCount Number of Cyral Sidecar replicas to deploy +## +## @param extraEnvVars Extra environment variables to be set on Cyral Sidecar containers +## E.g: +## extraEnvVars: +## - name: FOO +## value: BAR +## +extraEnvVars: [] +## @param extraEnvVarsCM ConfigMap with extra environment variables +## +extraEnvVarsCM: "" +## @param extraEnvVarsSecret Secret with extra environment variables +## +extraEnvVarsSecret: "" +## @section Cyral Sidecar deployment parameters +replicaCount: 1 +## @param resources Set container requests and limits for different resources like CPU or memory (essential for production workloads) +## Example: +## resources: +## requests: +## cpu: 2 +## memory: 512Mi +## limits: +## cpu: 3 +## memory: 1024Mi +## +resources: {} +## @param nodeSelector Node labels for pod assignment. Evaluated as a template. +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ +## +nodeSelector: {} +## @param tolerations Tolerations for pod assignment. Evaluated as a template. +## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] +## @param extraVolumes Array of extra volumes to be added to the Cyral Sidecar deployment (evaluated as template). Requires setting `extraVolumeMounts` +## +extraVolumes: [] +## Pods Service Account +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +## +serviceAccount: + ## @param serviceAccount.create Enable creation of ServiceAccount for Cyral Sidecar pod + ## + create: true + ## @param serviceAccount.name The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the `common.names.fullname` template + name: "" + ## @param serviceAccount.annotations Annotations for service account. Evaluated as a template. + ## Only used if `create` is `true`. + ## + annotations: {} + ## @param serviceAccount.automountServiceAccountToken Auto-mount the service account token in the pod + ## + automountServiceAccountToken: false +## Role Based Access +## Ref: https://kubernetes.io/docs/admin/authorization/rbac/ +## @param rbac.create Create Role and RoleBinding +## +rbac: + create: true + ## @param rbac.rules Custom RBAC rules to set + ## e.g: + ## rules: + ## - apiGroups: + ## - "" + ## resources: + ## - pods + ## verbs: + ## - get + ## - list + ## + rules: [] +## Cyral Sidecar pods' Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod +## @param podSecurityContext.enabled Enabled Cyral Sidecar pods' Security Context +## @param podSecurityContext.fsGroupChangePolicy Set filesystem group change policy +## @param podSecurityContext.sysctls Set kernel settings using the sysctl interface +## @param podSecurityContext.supplementalGroups Set filesystem extra groups +## @param podSecurityContext.fsGroup Set Cyral Sidecar pod's Security Context fsGroup +## +podSecurityContext: + enabled: false + fsGroupChangePolicy: Always + sysctls: [] + supplementalGroups: [] + fsGroup: 1001 +## Cyral Sidecar containers' Security Context. +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container +## @param containerSecurityContext.enabled Enabled containers' Security Context +## @param containerSecurityContext.seLinuxOptions [object,nullable] Set SELinux options in container +## @param containerSecurityContext.runAsUser Set containers' Security Context runAsUser +## @param containerSecurityContext.runAsNonRoot Set container's Security Context runAsNonRoot +## @param containerSecurityContext.privileged Set container's Security Context privileged +## @param containerSecurityContext.readOnlyRootFilesystem Set container's Security Context readOnlyRootFilesystem +## @param containerSecurityContext.allowPrivilegeEscalation Set container's Security Context allowPrivilegeEscalation +## @param containerSecurityContext.capabilities.drop List of capabilities to be dropped +## @param containerSecurityContext.seccompProfile.type Set container's Security Context seccomp profile +## +containerSecurityContext: + enabled: false + seLinuxOptions: null + runAsUser: 1001 + runAsNonRoot: true + privileged: false + readOnlyRootFilesystem: false + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + seccompProfile: + type: "RuntimeDefault" +## Configures the ports Cyral Sidecar listens on +## @param containerPorts [object] Map of all ports inside Cyral Sidecar container +## +containerPorts: + metrics: 9000 + denodo0: 9996 + denodo1: 9999 + dremio: 31010 + dynamodb: 463 + sqlserver: 1433 + mongodb0: 27017 + mongodb1: 27018 + mongodb2: 27019 + mysql: 3306 + oracle: 1521 + pg: 5432 + redshift: 5439 + s3: 453 + snowflake: 443 +## @param extraContainerPorts Array of additional container ports for the Cyral Sidecar container +## e.g: +## extraContainerPorts: +## - 4317 +## +extraContainerPorts: [] +## Cyral Sidecar Service properties +## +service: + ## @param service.type Service type + ## + type: LoadBalancer + ## @param service.ports [object] Map of Cyral Sidecar service ports + ## + ports: + metrics: 9000 + denodo0: 9996 + denodo1: 9999 + dremio: 31010 + dynamodb: 463 + sqlserver: 1433 + mongodb0: 27017 + mongodb1: 27018 + mongodb2: 27019 + mysql: 3306 + oracle: 1521 + pg: 5432 + redshift: 5439 + s3: 453 + snowflake: 443 + ## + ## @param service.nodePorts [object] Specify the nodePort(s) value(s) for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + nodePorts: + sidecar: [] + ## @param service.targetPort [object] Target port reference value for the Loadbalancer service types can be specified explicitly. + ## Listeners for the Loadbalancer can be custom mapped to the any Cyral Sidecar service. + ## Example: Mapping the mysql listener to targetPort mysql [mysql: mysql] + ## + targetPort: + denodo0: denodo0 + denodo1: denodo1 + dremio: dremio + dynamodb: dynamodb + sqlserver: sqlserver + mongodb0: mongodb0 + mongodb1: mongodb1 + mongodb2: mongodb2 + mysql: mysql + oracle: oracle + pg: pg + redshift: redshift + s3: s3 + snowflake: snowflake + ## @param service.clusterIP Cyral Sidecar service Cluster IP + ## e.g.: + ## clusterIP: None + ## + clusterIP: "" + ## @param service.loadBalancerIP LoadBalancer service IP address + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + loadBalancerIP: "" + ## @param service.loadBalancerSourceRanges Cyral Sidecar service Load Balancer sources + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param service.loadBalancerClass service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer + ## + loadBalancerClass: "" + ## @param service.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" + ## If "ClientIP", consecutive client requests will be directed to the same Pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## @param service.annotations Service annotations + ## This can be used to set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + ## @param service.externalTrafficPolicy Enable client source IP preservation + ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster +## @section Cyral configuration parameters + +## Cyral parameters +## +cyral: + ## @param cyral.sidecarId Sidecar identifier + sidecarId: "" + ## @param cyral.controlPlane Address of the control plane - .cyral.com + controlPlane: "" + ## Cyral credentials + ## + credentials: + ## @param cyral.credentials.existingSecret Name of an existing Kubernetes secret containing client ID and client secret. The secret must contain the `clientId` and `clientSecret` keys. + existingSecret: "" + ## @param cyral.credentials.clientId The client ID assigned to the sidecar. Optional - required only if existingSecret is not provided. + clientId: "" + ## @param cyral.credentials.clientSecret The client secret assigned to the sidecar. Optional - required only if existingSecret is not provided. + clientSecret: "" + ## Cyral Sidecar parameters + ## + sidecar: + ## @param cyral.sidecar.dnsName Fully qualified domain name that will be used to access the Cyral Sidecar + dnsName: "" + ## Cyral Sidecar certificates parameters + ## + certificates: + ## @param cyral.sidecar.certificates.tls.existingSecret Name of an existing Kubernetes secret containing a private key and a certificate to terminate TLS connections. + tls: + existingSecret: "" + ## @param cyral.sidecar.certificates.ca.existingSecret Name of an existing Kubernetes secret containing a private key and a certificate for the internal CA. + ca: + existingSecret: "" + ## Snowflake parameters + ## + snowflake: + ## @param cyral.sidecar.snowflake.SSOLoginURL The IdP SSO URL for the IdP being used with Snowflake. + SSOLoginURL: "" + ## @param cyral.sidecar.snowflake.idpCertificate The certificate used to verify SAML assertions from the IdP being used with Snowflake. Enter this value as a one-line string with literal new line characters (\n) specifying the line breaks. + idpCertificate: "" + ## @param cyral.sidecar.snowflake.sidecarIdpCertificate The public certificate used to verify signatures for SAML Assertions generated by the sidecar. Required if using SSO with Snowflake. + sidecarIdpCertificate: "" + ## @param cyral.sidecar.snowflake.sidecarIdpPrivateKey The private key used to sign SAML Assertions generated by the sidecar. Required if using SSO with Snowflake. + sidecarIdpPrivateKey: "" + ## Deployment properties + ## + deploymentProperties: + ## @param cyral.deploymentProperties.cloud Cloud provider where the Cyral Sidecar is hosted. + cloud: "" + ## @param cyral.deploymentProperties.endpoint Fully qualified domain name that will be used to access the Cyral Sidecar. + endpoint: "" + ## @param cyral.deploymentProperties.deploymentType Deployment type choosen to deploy the Cyral Sidecar. Defaults to `helm-kubernetes`. + deploymentType: helm-kubernetes