Reputation: 79
I want to deploy a flask+gunicorn project and I am newbie to Docker. So far, I have a Dockerfile as following.
# Pull official base image
FROM python:3.7-slim-buster
# Set work directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Set environment variables
ENV REDIS_HOST [...omit here...]
ENV REDIS_PORT [...omit here...]
ENV REDIS_DB_WHITELIST [...omit here...]
ENV MYSQL_HOST [...omit here...]
ENV MYSQL_PORT [...omit here...]
ENV MYSQL_DB_DUMMY [...omit here...]
# Copy project
COPY . /usr/src/app/
# Install dependencies
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
RUN pip install gunicorn
EXPOSE 5000
RUN chmod +x ./entrypoint.sh
ENTRYPOINT ["sh", "entrypoint.sh"]
And a docker-compose.yml as following.
version: "3.9"
secrets:
FLASK_SECRET_KEY:
external: true
MYSQL_USER:
external: true
MYSQL_PASSWORD:
external: true
services:
web:
image: flask-app:v0.1.0
environment:
FLASK_SECRET_KEY_FILE: /run/secrets/FLASK_SECRET_KEY
MYSQL_USER_FILE: /run/secrets/MYSQL_USER
MYSQL_PASSWORD_FILE: /run/secrets/MYSQL_PASSWORD
ports:
- "5000:5000"
secrets:
- FLASK_SECRET_KEY
- MYSQL_USER
- MYSQL_PASSWORD
After I googled through, it seems the only way of accessing docker secrets is to use docker stack deploy --compose-file=docker-compose.yml flask-app
command. Obviously, I have three sensitive data FLASK_SECRET_KEY, MYSQL_USER, MYSQL_PASSWORD needed to storing in Docker secrets. It turns out that app keeps failing to run, and I assume that mysql_user = os.environ['MYSQL_USER']
etc. in python script fails to access environment variable.
I have no idea of right way to access sensitive data from Docker secrets, via Dockerfile or docker-compose.yml, and please correct me if I get something wrong.
Upvotes: 1
Views: 6184
Reputation: 79
I did find an approach of accessing sensitive data from docker secret, using python-dotenv. Here's some snippet of my project-level config.py
module.
import os
from dotenv import load_dotenv
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
if os.path.exists(dotenv_path):
load_dotenv(dotenv_path=dotenv_path)
def manage_sensitive(name):
v1 = os.getenv(name)
secret_fpath = f'/run/secrets/{name}'
existence = os.path.exists(secret_fpath)
if v1 is not None:
return v1
if existence:
v2 = open(secret_fpath).read().rstrip('\n')
return v2
if all([v1 is None, not existence]):
return KeyError(f'{name}')
class ConfigRabbitMQ:
AMQP_USER = manage_sensitive(name='amqp_user')
AMQP_PASSWORD = manage_sensitive(name='amqp_password')
AMQP_HOST = manage_sensitive(name='amqp_host')
AMQP_PORT = manage_sensitive(name='amqp_port')
So there's a .env
file at the same directory with this config.py
module. This module can access sensitive data from Docker secret and .env
file, as it is a common practice to list .env
in a .dockerignore
file. Thus, for example, docker-compose.yml
is as following.
version: "3.9"
services:
web:
...
secrets:
- amqp_user
- amqp_password
...
secrets:
amqp_user:
external: true
amqp_password:
external: true
Is there's any advice for a better practice ?
Upvotes: 1
Reputation: 741
You used the short syntax to declare the secrets on the service. By default such secrets will be mounted in /run/secrets/{secretname} inside the container.
The long syntax even allows to specify the target location (even though the v3 compose reference claims it would only mount to /run/secrets/):
Create a secret:
echo "mysecret" | docker secret create mysecret -
Note: the secret could have been created within a docker-compose.yml with a file reference as well. For simplicity I chose to create it manualy in my example.
Consume the secret in a swarm stack:
version: '3.8'
services:
testsecret:
image: ubuntu
deploy:
replicas: 1
tty: true
secrets:
- source: mysecret
target: /path/in/container/mysecret
mode: 0444
secrets:
mysecret:
external: true
Then read the file /path/in/container/mysecret within your application to get its content.
Bare in mind that secrets are always mounted as read-only. The added security is that the secret is destributed encrypted amongst swarm nodes and stored encrypted in the raft log (~=the cluster state). Once a secret is mounted in a container, it will be an unencrypted file on a tempfs. Another advantage is that the details are not exposed as environment variables, thus can less likey by accidentaly leaked.
It seems that docker-compose has a definition gap with the v3.x version of the compose file specs that allows to use secrets with docker-compose deployments as well. But: many low level functionality won't be available then.
Since a coouple of days, the v3 compose file reference page seem to be broken: appart from headers and footers, the whole body of the description is missing...
Upvotes: 0