29

I'm using a kubernetes ConfigMap that contains database configurations for an app and there is a secret that has the database password. I need to use this secret in the ConfigMap so when I try to add environment variable in the ConfigMap and specify the value in the pod deployment from the secret I'm not able to connect to mysql with password as the values in the ConfigMap took the exact string of the variable.

apiVersion: v1
kind: ConfigMap
metadata:
  name: config
data:
  APP_CONFIG: |
    port: 8080
    databases:
      default:
        connector: mysql
        host: "mysql"
        port: "3306"
        user: "root"
        password: "$DB_PASSWORD"

and the deployment.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: app
  labels:
    app: backend
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: app
        image: simple-app-image
        ports:
          - name: "8080"
            containerPort: 8080
        env:
          - name: APP_CONFIG
            valueFrom:
              configMapKeyRef:
                name: config
                key: APP_CONFIG
          - name: DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: "mysql-secret"
                key: "mysql-root-password"

Note: the secret exist and I'm able to get "mysql-root-password" value and use to login to the database

yara mohamed
  • 371
  • 1
  • 7
  • 12

4 Answers4

16

Kubernetes can't make that substitution for you, you should do it with shell in the entrypoint of the container.

This is a working example. I modify the default entrypoint to create a new variable with that substitution. After this command you should add the desired entrypoint.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: app
  labels:
    app: backend
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: app
        image: simple-app-image
        command:
          - /bin/bash
          - -c
        args:
          - "NEW_APP_CONFIG=$(echo $APP_CONFIG | envsubst) && echo $NEW_APP_CONFIG && <INSERT IMAGE ENTRYPOINT HERE>"
        ports:
          - name: "app"
            containerPort: 8080
        env:
          - name: APP_CONFIG
            valueFrom:
              configMapKeyRef:
                name: config
                key: APP_CONFIG
          - name: DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: "mysql-secret"
                key: "mysql-root-password"
Ignacio Millán
  • 7,480
  • 1
  • 25
  • 28
2

You could do something like this in HELM:

{{- define "getValueFromSecret" }}
{{- $len := (default 16 .Length) | int -}}
{{- $obj := (lookup "v1" "Secret" .Namespace .Name).data -}}
{{- if $obj }}
{{- index $obj .Key | b64dec -}}
{{- else -}}
{{- randAlphaNum $len -}}
{{- end -}}
{{- end }}

Then you could do something like this in configmap:

{{- include "getValueFromSecret" (dict "Namespace" .Release.Namespace "Name" "<secret_name>" "Length" 10 "Key" "<key>")  -}}

The secret should be already present while deploying; or you can control the order of deployment using https://github.com/vmware-tanzu/carvel-kapp-controller

Vishrant
  • 15,456
  • 11
  • 71
  • 120
0

For me, something like this worked (sharing just the relevant example snippet for simplicity):

example-configmap

data:
  example.yaml: |
    user: ${USER}

example-secret

data:
  user: <some base64 encoded value>

example-deployment

containers:
  - name: example-container
    image: example-image
    env:
      - name: USER
        valueFrom:
          secretKeyRef:
            key: user
            name: example-secret
    volumeMounts:
      - mountPath: /some/path/in/container
        name: config
        readOnly: true
volumes:  
  - name: config
    configMap:
      name: example-configmap

Explanation: In the above example, say,
I need to substitute ${USER} in the configmap with the decoded value of the user: key from the secret.
Then, as done above, in the deployment/pod definition, I need to:

  • mount the configmap as a volume (volumes.configMap) under volumeMounts in the container, and
  • expose the secret value as an environment variable (env.valueFrom.secretKeyRef) in the container.

Result: The ${USER} in the configmap will have been substituted with the decoded value of the user: key from the secret.

Reason: This is because the configmap is now mounted as a file at the mountPath inside the container, and thus, can access any env variable exposed within the container.

Note: namespace for deployment/pod, configmap, and secret should be the same.

-2

I would transform the whole configMap into a secret and deploy the database password directly in there. Then you can mount the secret as a file to a volume and use it like a regular config file in the container.

peedee
  • 3,257
  • 3
  • 24
  • 42
  • You won't "able" to SVC your file in git, unless you allow your secret to get visible in your git repo.. I used confd as a workaround. I used sceret as env var in my deployment, confd at container startup substitues values for me in my config files. – Med Ali Difallah Feb 07 '20 at 13:10