2
votes

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?

2
Distribute Credentials Securely Using Secrets in the Kubernetes documentation describes both how to translate the Secret to mounted files and environment variables. - David Maze

2 Answers

3
votes

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()
0
votes

if possible, use pydantic

Pydantic settings got support for docker secrets, k8s secrets work the same way. https://pydantic-docs.helpmanual.io/usage/settings/#use-case-docker-secrets

example from docs:

from pydantic import BaseSettings

class Settings(BaseSettings):
    my_secret_data: str

    class Config:
        secrets_dir = '/run/secrets'

If you have more than 1 k8s secrets containers pydantic settings also read from env vars, but seems to only support 1 secrets folder.