6

I’m packaging a Python app for use within a Kubernetes cluster. In the code base this method exists :

   def get_pymongo_client(self):
        username = test;
        password = 'test';
        url = ‘test
        conn_str = "mongodb+srv://" + username + ":" + password + “/”+ url

        return pymongo.MongoClient(conn_str)

I’m attempting to secure the username, password & URL fields so that they are not viewable within the src code. For this, I plan to use secrets.

The URL https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/ details how to create a secret. But I’m not sure how to read the secret from the Python app.

.Dockerfile for my app:

#https://docs.docker.com/language/python/build-images/

FROM python:3.8-slim-buster

WORKDIR /app

COPY requirements.txt requirements.txt

RUN pip3 install -r requirements.txt

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]

Reading Python flask application access to docker secrets in a swarm details the use of secrets in a docker-compose file, is this also required for Kubernetes? What steps are involved in order to read secret parameters from the Python src code file?

blue-sky
  • 51,962
  • 152
  • 427
  • 752
  • 1
    [Distribute Credentials Securely Using Secrets](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/) in the Kubernetes documentation describes both how to translate the Secret to mounted files and environment variables. – David Maze Apr 01 '21 at 16:03

1 Answers1

12

The traditional way is via environment variable

spec:
  containers:
  - name: your-app
    # ...
    env:
    - name: PYMONGO_USERNAME
      valueFrom:
        secretKeyRef:
           name: your-secret-name-here
           key: PYMONGO_USERNAME

Or you can make that yaml less chatty by using a well-formed Secret and the "envFrom:" field

kind: Secret
metadata:
  name: pymongo
stringData:
  PYMONGO_USERNAME: test
  PYMONGO_PASSWORD: sekrit
---
spec:
  containers:
  - name: your-app
    envFrom:
    - secretRef:
        name: pymongo
    # and now the pod has all environment variables matching the keys in the Secret

and then your code would just read it from its environment as normal

   def get_pymongo_client(self):
        username = os.getenv('PYMONGO_USERNAME')
        password = os.getenv('PYMONGO_PASSWORD')
        # etc

An alternative, but similar idea, is to mount the Secret onto the filesystem, and then read in the values as if they were files

spec:
  containers:
  - name: your-app
    env:
    # this part is 100% optional, but allows for easier local development
    - name: SECRETS_PATH
      value: /secrets
    volumeMounts:
    - name: pymongo
      mountPath: /secrets 
  volumes:
  - name: pymongo
    secret:
      secretName: your-secret-name-here

then:

   def get_pymongo_client(self):
        sec_path = os.getenv('SECRETS_PATH', './secrets')
        with open(os.path.join(sec_path, 'PYMONGO_USERNAME')) as fh:
            username = fh.read()
mdaniel
  • 31,240
  • 5
  • 55
  • 58
  • 1
    thanks, just to claify, the yaml is a deployment yaml configuration - as in deployment.yaml referred to here: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/ – blue-sky Apr 01 '21 at 19:15
  • Question? Pros and cons of using filesystem vs environment variable? Do I have to worry about special characters mangling when using environment variables? – Scott Boston Apr 19 '23 at 19:31
  • 1
    There's a healthy debate about not using env-vars for secrets since they are prone to leaks in certain circumstances, but my experience has been that if your threat model includes an actor able to open `/proc/self/environ` the game is already over. One can tell that the filesystem approach is also a lot more code. As far as the special characters, for the most part not with kubernetes, since only `$()` sequences are special and I believe even those are only interpolated in the `env:[]` values and not `envFrom:` (but I'd have to test it to know for sure) – mdaniel Apr 20 '23 at 01:29