pythondockerkuberneteskubernetes-secretsdocker-secrets

Reading secrets from Kubernetes within Python based app


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?


Solution

  • 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()