diff --git a/.github/spell-ignore-words.txt b/.github/spell-ignore-words.txt index 2819b95..5e79552 100644 --- a/.github/spell-ignore-words.txt +++ b/.github/spell-ignore-words.txt @@ -16,3 +16,9 @@ YAML secret.secretName configMap.name Lifecycle +SecretStore +ExternalSecret +secretKey +1h +1m +1s diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index c8e2ede..c1d5e8c 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -82,6 +82,10 @@ jobs: run: | curl https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/main/jsonnet/prometheus-operator/podmonitors-crd.json --output /tmp/podmonitors-crd.json kubectl apply -f /tmp/podmonitors-crd.json + - name: Install external-secret CRD + run: | + curl https://raw.githubusercontent.com/external-secrets/external-secrets/main/deploy/crds/bundle.yaml --output /tmp/external-secrets-crd.yaml + kubectl apply -f /tmp/external-secrets-crd.yaml - name: Apply run: kubectl apply -f tests/expected.yaml diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 89847d2..e459fc8 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -17,12 +17,16 @@ Create the name of the service account to use - name: {{ .name | quote }} valueFrom: {{ .value.type }}KeyRef: - {{ if and (hasKey .value "name" ) ( eq .value.name "self" ) -}} + {{ if and ( hasKey .value "name" ) ( eq .value.name "self" ) -}} {{ if .value.type | eq "configMap" -}} name: {{ include "common.fullname" ( dict "root" .root "service" .root.Values.configMaps ) }} {{ else -}} name: {{ include "common.fullname" ( dict "root" .root "service" .root.Values.secrets ) }} {{ end -}} + {{ else if hasPrefix "self-external-secret-" .value.name -}} + {{- $name := substr 21 -1 .value.name }} + {{- $definition := get .root.Values.externalSecrets $name }} + name: {{ include "common.fullname" ( dict "root" .root "service" $definition "serviceName" $name ) }} {{ else if and (hasKey .value "name" ) ( eq .value.name "self-metadata" ) -}} name: {{ include "common.fullname" ( dict "root" .root "service" .root.Values "serviceName" "metadata" ) }} {{ else -}} @@ -182,13 +186,13 @@ volumes: secret: {{- if eq ( default "self" $value.secret.secretName ) "self" }} secretName: {{ include "common.fullname" ( dict "root" $root "service" $root.Values.secrets ) }} - {{- else }} - {{- if eq ( default "self" $value.secret.secretName ) "self-metadata" }} - secretName: {{ include "common.fullname" ( dict "root" $root "service" $root.Values "serviceName" "metadata" ) }} + {{- else if hasPrefix "self-external-secret-" $value.secret.secretName }} + {{- $name := substr 21 -1 $value.secret.secretName }} + {{- $definition := get $root.Values.externalSecrets $name }} + secretName: {{ include "common.fullname" ( dict "root" $root "service" $definition "serviceName" $name ) }} {{- else }} secretName: {{ $value.secret.secretName }} {{- end }} - {{- end }} {{- with $value.secret.items }} items: {{- . | toYaml | nindent 6 }} {{- end }} @@ -197,6 +201,8 @@ volumes: configMap: {{- if eq ( default "self" $value.configMap.name ) "self" }} name: {{ include "common.fullname" ( dict "root" $root "service" $root.Values.configMaps ) }} + {{- else if eq ( default "self" $value.configMap.name ) "self-metadata" }} + name: {{ include "common.fullname" ( dict "root" $root "service" $root.Values "serviceName" "metadata" ) }} {{- else }} name: {{ $value.configMap.name }} {{- end }} diff --git a/templates/external-secret.yaml b/templates/external-secret.yaml new file mode 100644 index 0000000..625fc60 --- /dev/null +++ b/templates/external-secret.yaml @@ -0,0 +1,32 @@ +{{- range $name, $definition := .Values.externalSecrets }} +{{- if eq $definition.enabled true }} +{{- if ( or $definition.data $definition.dataFrom ) }} +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ include "common.fullname" ( dict "root" $ "service" $definition "serviceName" $name ) }} + {{- include "common.metadata" ( dict "root" $ "service" $definition "serviceName" $name ) | nindent 2 }} +spec: + {{- with $definition.refreshInterval }} + refreshInterval: {{ . }} + {{- end }} + {{- with $definition.secretStoreRef }} + secretStoreRef: {{- toYaml . | nindent 4 }} + {{- end }} + target: + {{- if not ( hasKey ( default ( dict ) .target ) "name" ) }} + name: {{ include "common.fullname" ( dict "root" $ "service" $definition "serviceName" "external-secret" ) }} + {{- end }} + {{- with $definition.target }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $definition.dataFrom }} + dataFrom: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $definition.data }} + data: {{- include "common.dictToList" ( dict "keyName" "secretKey" "contents" . ) | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/secret.yaml b/templates/secret.yaml index f6f5fd7..baab14c 100644 --- a/templates/secret.yaml +++ b/templates/secret.yaml @@ -1,4 +1,6 @@ {{- with .Values.secrets }} +{{- if .enabled }} +{{- if .content }} --- apiVersion: v1 kind: Secret @@ -17,3 +19,5 @@ data: {{- end }} {{- end }} {{- end }} +{{- end }} +{{- end }} diff --git a/tests/expected.yaml b/tests/expected.yaml index 47a4bd0..376f8e5 100644 --- a/tests/expected.yaml +++ b/tests/expected.yaml @@ -658,6 +658,18 @@ spec: items: - key: testYaml path: test.yaml + - name: self-configmap-metadata + configMap: + name: custom-custom-pod-metadata + items: + - key: testYaml + path: test.yaml + - name: self-external-secret + secret: + secretName: custom-custom-pod-app + items: + - key: hostname + path: hostname.txt - name: self-secret secret: secretName: custom-custom-pod @@ -1032,6 +1044,12 @@ spec: secretKeyRef: name: custom-custom-pod key: "test" + - name: "SELF_VAULT" + valueFrom: + secretKeyRef: + + name: custom-custom-pod-app + key: "hostname" terminationMessagePolicy: FallbackToLogsOnError --- # Source: custom-pod/templates/statefulset.yaml @@ -1398,6 +1416,54 @@ spec: port: number: 8080 --- +# Source: custom-pod/templates/external-secret.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: custom-custom-pod-app + labels: + helm.sh/chart: custom-pod + app.kubernetes.io/version: "1.0" + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: custom-pod + app.kubernetes.io/instance: custom + app.kubernetes.io/component: app +spec: + refreshInterval: 1h + secretStoreRef: + kind: SecretStore + name: my-secret-store + target: + name: custom-custom-pod-external-secret + data: + - secretKey: hostname + remoteRef: + key: hostname +--- +# Source: custom-pod/templates/external-secret.yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: custom-custom-pod-global + labels: + helm.sh/chart: custom-pod + app.kubernetes.io/version: "1.0" + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: custom-pod + app.kubernetes.io/instance: custom + app.kubernetes.io/component: global +spec: + refreshInterval: 1h + secretStoreRef: + kind: SecretStore + name: my-global-secret-store + target: + name: custom-custom-pod-external-secret + data: + - secretKey: pass + remoteRef: + key: pass +--- # Source: custom-pod/templates/podmonitor.yaml apiVersion: monitoring.coreos.com/v1 kind: PodMonitor diff --git a/tests/values.yaml b/tests/values.yaml index c5c24d9..5e72cd9 100644 --- a/tests/values.yaml +++ b/tests/values.yaml @@ -41,6 +41,7 @@ dockerregistry: password: '1234' secrets: + enabled: true annotations: testAnnotation: annotation value for secrets content: @@ -53,6 +54,28 @@ secrets: user: toto password: toto +externalSecrets: + app: + enabled: true + refreshInterval: 1h + secretStoreRef: + name: my-secret-store + kind: SecretStore + data: + hostname: + remoteRef: + key: hostname + global: + enabled: true + refreshInterval: 1h + secretStoreRef: + name: my-global-secret-store + kind: SecretStore + data: + pass: + remoteRef: + key: pass + configMaps: annotations: testAnnotation: annotation value for configmaps @@ -198,6 +221,18 @@ services: path: test.yaml self-secret: secret: {} + self-configmap-metadata: + configMap: + name: self-metadata + items: + - key: testYaml + path: test.yaml + self-external-secret: + secret: + secretName: self-external-secret-app + items: + - key: hostname + path: hostname.txt service: &servicedefinition type: ClusterIP @@ -508,3 +543,7 @@ services: type: configMap name: self-metadata key: CHART_NAME + SELF_VAULT: + type: secret + name: self-external-secret-app + key: hostname diff --git a/values.md b/values.md index b924c4f..9f2457f 100644 --- a/values.md +++ b/values.md @@ -54,6 +54,7 @@ - **`password`** _(string, required)_: Password. - **`email`** _(string)_: Email. - **`secrets`** _(object)_: Cannot contain additional properties. + - **`enabled`** _(boolean)_: Enable the Secret. Default: `true`. - **`nameOverride`**: Refer to _[#/definitions/nameOverride](#definitions/nameOverride)_. - **`fullnameOverride`**: Refer to _[#/definitions/fullnameOverride](#definitions/fullnameOverride)_. - **`serviceName`**: Refer to _[#/definitions/serviceName](#definitions/serviceName)_. @@ -61,7 +62,7 @@ - **`prefixTrunc`**: Refer to _[#/definitions/prefixTrunc](#definitions/prefixTrunc)_. - **`labels`**: Refer to _[#/definitions/labels](#definitions/labels)_. - **`annotations`**: Refer to _[#/definitions/annotations](#definitions/annotations)_. - - **`content`** _(object, required)_: Secrets configuration. Can contain additional properties. + - **`content`** _(object)_: Secrets configuration. Can contain additional properties. - **Additional properties** - **One of** - _object_: Secret from a direct value. Cannot contain additional properties. @@ -71,6 +72,22 @@ - **`type`** _(string)_: Type of the secret. Must be one of: `["basicAuth"]`. - **`user`** _(string)_: Username. - **`password`** _(string)_: Password. +- **`externalSecrets`** _(object)_: Can contain additional properties. + - **Additional properties** _(object)_: Cannot contain additional properties. + - **`enabled`** _(boolean)_: Enable the ExternalSecret. Default: `true`. + - **`nameOverride`**: Refer to _[#/definitions/nameOverride](#definitions/nameOverride)_. + - **`fullnameOverride`**: Refer to _[#/definitions/fullnameOverride](#definitions/fullnameOverride)_. + - **`serviceName`**: Refer to _[#/definitions/serviceName](#definitions/serviceName)_. + - **`releaseTrunc`**: Refer to _[#/definitions/releaseTrunc](#definitions/releaseTrunc)_. + - **`prefixTrunc`**: Refer to _[#/definitions/prefixTrunc](#definitions/prefixTrunc)_. + - **`labels`**: Refer to _[#/definitions/labels](#definitions/labels)_. + - **`annotations`**: Refer to _[#/definitions/annotations](#definitions/annotations)_. + - **`refreshInterval`** _(string)_: The refresh interval like 1h, 1m, 1s. + - **`secretStoreRef`** _(object)_: defines which SecretStore to fetch the ExternalSecret data. + - **`target`** _(object)_: defines the Kubernetes Secret to be created. + - **`dataFrom`** _(array)_: used to fetch all properties from a specific Provider data. + - **`data`** _(object)_: Data defines the connection between the Kubernetes Secret keys and the Provider data. Can contain additional properties. + - **Additional properties** _(object)_: defines the connection between the Kubernetes Secret key and the Provider data. The map key became the secretKey. - **`configMaps`** _(object)_: Cannot contain additional properties. - **`nameOverride`**: Refer to _[#/definitions/nameOverride](#definitions/nameOverride)_. - **`fullnameOverride`**: Refer to _[#/definitions/fullnameOverride](#definitions/fullnameOverride)_. @@ -137,7 +154,7 @@ - **`failedJobsHistoryLimit`** _(integer)_: CronJob - failedJobsHistoryLimit. - **`concurrencyPolicy`** _(string)_: CronJob - concurrencyPolicy. Must be one of: `["Allow", "Forbid", "Replace"]`. - **`volumeClaimTemplates`** _(array)_: The volume claim templates, the key is the name of the volume claim template. - - **`volumes`** _(object)_: The volumes configuration, the key is the name of the volume, if `secret.secretName` or `configMap.name` is not defined, or defined to 'self' the internal one will be used, use 'self-metadata' for the metadata ConfigMap. + - **`volumes`** _(object)_: The volumes configuration, the key is the name of the volume, if `secret.secretName` or `configMap.name` is not defined, or defined to 'self' the internal one will be used, use 'self-metadata' for the metadata ConfigMap, 'self-external-secret' for the external secret. - **`pdb`** _(object)_: The Pod disruption budget configuration. Cannot contain additional properties. Default: `{"enabled": true, "maxUnavailable": 1}`. - **`enabled`** _(boolean)_: Enable the Pod disruption budget. Default: `true`. - **`minAvailable`** _(integer)_: The minimum number of Pods available. diff --git a/values.schema.json b/values.schema.json index 290e02c..20b2015 100644 --- a/values.schema.json +++ b/values.schema.json @@ -468,6 +468,11 @@ "type": "object", "additionalProperties": false, "properties": { + "enabled": { + "type": "boolean", + "description": "Enable the Secret", + "default": true + }, "nameOverride": { "$ref": "#/definitions/nameOverride" }, @@ -534,8 +539,66 @@ ] } } - }, - "required": ["content"] + } + }, + "externalSecrets": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable the ExternalSecret", + "default": true + }, + "nameOverride": { + "$ref": "#/definitions/nameOverride" + }, + "fullnameOverride": { + "$ref": "#/definitions/fullnameOverride" + }, + "serviceName": { + "$ref": "#/definitions/serviceName" + }, + "releaseTrunc": { + "$ref": "#/definitions/releaseTrunc" + }, + "prefixTrunc": { + "$ref": "#/definitions/prefixTrunc" + }, + "labels": { + "$ref": "#/definitions/labels" + }, + "annotations": { + "$ref": "#/definitions/annotations" + }, + "refreshInterval": { + "type": "string", + "description": "The refresh interval like 1h, 1m, 1s" + }, + "secretStoreRef": { + "type": "object", + "description": "defines which SecretStore to fetch the ExternalSecret data." + }, + "target": { + "type": "object", + "description": "defines the Kubernetes Secret to be created." + }, + "dataFrom": { + "type": "array", + "description": "used to fetch all properties from a specific Provider data." + }, + "data": { + "type": "object", + "description": "Data defines the connection between the Kubernetes Secret keys and the Provider data.", + "additionalProperties": { + "type": "object", + "description": "defines the connection between the Kubernetes Secret key and the Provider data. The map key became the secretKey" + } + } + } + } }, "configMaps": { "type": "object", @@ -803,7 +866,7 @@ }, "volumes": { "type": "object", - "description": "The volumes configuration, the key is the name of the volume, if `secret.secretName` or `configMap.name` is not defined, or defined to 'self' the internal one will be used, use 'self-metadata' for the metadata ConfigMap" + "description": "The volumes configuration, the key is the name of the volume, if `secret.secretName` or `configMap.name` is not defined, or defined to 'self' the internal one will be used, use 'self-metadata' for the metadata ConfigMap, 'self-external-secret' for the external secret" }, "pdb": { "type": "object",