I have a use case where OOB “out-of-the-box” Helm charts are being deployed on a k8s cluster.
In the example I’ll share here, it will be a postgres Helm chart.
The issue I’m currently facing is that the helm chart and many other OOB helm charts don’t cater for using an existing k8s secret to get secret values.
i.e.
making use of param: existingSecret
for example.
The helm chart in this example get’s secrets from the values.yaml
file and parses them in and creates a configMap.
The are also a bunch of OOB helm charts that do something similar and maybe not pass the secret data to a configMap, but the chart itself creates a k8s secret (for example), also reading in values from the values.yaml
file.
The example helm chart (postgres)
folder/file structure:
my-test-postgres-vaules.yaml
postgres
├── charts
├── Chart.yaml
├── templates
│ ├── config.yaml
│ ├── deployment.yaml
│ ├── pvc.yaml
│ └── service.yaml
└── values.yaml
See contents below of the files:
postgres/chart.yaml
:
---
apiVersion: v2
name: postgres
description: A Helm chart for PostgreSQL database
type: application
version: 0.0.1
appVersion: 0.0.1
keywords:
- database
- postgres
postgres/templates/config.yaml
:
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Values.postgres.config.name }}
labels:
group: {{ .Values.postgres.group }}
data:
{{- range .Values.postgres.config.data }}
{{ .key }}: {{ .value }}
{{- end}}
postgres/templates/deployment.yaml
:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.postgres.name }}
labels:
app: {{ .Values.postgres.name }}
group: {{ .Values.postgres.group }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Values.postgres.name }}
template:
metadata:
annotations:
{{- range $key, $value := .Values.podAnnotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
labels:
app: {{ .Values.postgres.name }}
group: {{ .Values.postgres.group }}
spec:
volumes:
- name: {{ .Values.postgres.volume.name }}
persistentVolumeClaim:
claimName: {{ .Values.postgres.volume.pvc.name }}
containers:
- name: {{ .Values.postgres.name }}
image: {{ .Values.postgres.container.image }}
ports:
- containerPort: {{ .Values.postgres.container.port }}
envFrom:
- configMapRef:
name: {{ .Values.postgres.config.name }}
volumeMounts:
- name: {{ .Values.postgres.volume.name }}
mountPath: {{ .Values.postgres.volume.mountPath }}
postgres/templates/pvc.yaml
:
---
apiVersion: v1
kind: {{ .Values.postgres.volume.kind }}
metadata:
name: {{ .Values.postgres.volume.pvc.name }}
spec:
accessModes:
- {{ .Values.postgres.volume.pvc.accessMode }}
resources:
requests:
storage: {{ .Values.postgres.volume.pvc.storage }}
postgres/templates/service.yaml
:
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.postgres.name }}
labels:
group: {{ .Values.postgres.group }}
spec:
type: {{ .Values.postgres.service.type }}
selector:
app: {{ .Values.postgres.name }}
ports:
- port: {{ .Values.postgres.service.port }}
targetPort: {{ .Values.postgres.container.port }}
postgres/values.yaml
:
---
replicaCount: 1
podAnnotations: {}
postgres:
name: postgres
group: db
container:
image: postgres:9.6-alpine
port: 5432
service:
type: ClusterIP
port: 5432
volume:
name: postgres-storage
kind: PersistentVolumeClaim
mountPath: /var/lib/postgresql/data
pvc:
name: postgres-persistent-volume-claim
accessMode: ReadWriteOnce
storage: 4Gi
config:
name: postgres-config
data:
- key: key
value: value
my-test-postgres-vaules.yaml
:
---
podAnnotations:
vault.hashicorp.com/agent-inject: 'true'
vault.hashicorp.com/role: 'test-db'
vault.hashicorp.com/agent-inject-secret-db-config.txt: 'kv-v2/data/database/config'
postgres:
config:
data:
- key: POSTGRES_DB
value: my-test-db
- key: POSTGRES_USER
value: my-test-user
- key: POSTGRES_PASSWORD
value: my-test-password
You will note that the chart DOES cater for podAnnotations
.
Where I am leveraging HashiCorps Vault Agent Injector service by means of using a “side car” to inject my secrets stored in vault to the postgres pod, into a file called: /vault/secrets/db-config.txt
that resides locally on the postgres container.
I followed instructions here to install vault, the vault agent injector as well as set the secrets on my vault server.
I install the postgres helm chart with:
helm install -f my-test-postgres-vaules.yaml postgres ./postgres
which returns successful:
NAME: postgres
LAST DEPLOYED: Wed Jun 5 19:57:42 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
postgres 1/1 1 1 30s
vault-agent-injector 1/1 1 1 3h56m
kubectl get pods
NAME READY STATUS RESTARTS AGE
postgres-f7f59555f-5b9sp 2/2 Running 0 63s
vault-0 1/1 Running 0 3h57m
vault-agent-injector-6d57d65964-9qzjx 1/1 Running 0 3h57m
If you look at the posgres pod, you will see it has two containers, one being postgres itself, and the other the vault-agent injector.
$ kubectl get pods $(kubectl get pod -l app=postgres -o jsonpath="{.items[0].metadata.name}") -o jsonpath='{.spec.containers[*].name}'
postgres vault-agent
The postgres pod has also successfully got the secrets from vault, see below:
kubectl exec $(kubectl get pod -l app=postgres -o jsonpath="{.items[0].metadata.name}") --container postgres -- cat /vault/secrets/db-config.txt
data: map[db-name:my-test-db password:my-test-password username:my-test-user]
metadata: map[created_time:2024-06-05T16:30:44.961447686Z custom_metadata:<nil> deletion_time: destroyed:false version:2]
I know that this does not solve my issue, but perhaps I can use something similar to parse in those secret values into the values.yaml
file that many of these types of OOB charts needs?
I know it’s possible to amend the helm chart itself, such that it doesn’t create a configMap, or a k8s secret of its own, or even better, update the chart to use the param existingSecret
, and then use vault secrets operator and create a VaultStaticSecrets CRD with my secrets.
The problem is that there are many OOB charts like this, and unpacking them, updating them, maintaining them, etc. just becomes a nightmare.
Whereas being able to inject vault secrets straight into the helm values.yaml
file will be super beneficial for us and the vendors supplying these OOB charts that don’t cater for using existing secrets with param: existingSecret
for example.
Something like:
my-test-postgres-vaules.yaml
:
---
postgres:
config:
data:
- key: POSTGRES_DB
value: <my vault secret here>.db-name
- key: POSTGRES_USER
value: <my vault secret here>.username
- key: POSTGRES_PASSWORD
value: <my vault secret here>.password
Thanks in advance for taking the time to review / answer my question 🙂