GOAL: Keep secrets out of plaintext (esp when syncing dockerfile and docker-compose.yaml to repo)
- Each secret needs to be a single text file with a clear name
- Edit
docker-entrypoint.sh
to run per environmental variable - (Re)Build docker image with
docker-entrypoint.sh
as entrypoint - Create
secrets
in docker-compose - Call secrets with "_FILE" appended to the name when specifying environmental variable
use if secrets are passed as environmental variables
#!/bin/sh
# logic cribbed from linuxserver.io:
# https://github.com/linuxserver/docker-baseimage-ubuntu/blob/bionic/root/etc/cont-init.d/01-envfile
# iterate over environmental variables
# if variable ends in "__FILE"
for FULLVAR in $(env | grep "^.*__FILE="); do
# trim "=..." from variable name
VARNAME=$(echo $FULLVAR | sed "s/=.*//g")
echo "[secret-init] Evaluating ${VARNAME}"
# set SECRETFILE to the contents of the variable
# Use 'eval hack' for indirect expansion in sh: https://unix.stackexchange.com/questions/111618/indirect-variable-expansion-in-posix-as-done-in-bash
# WARNING: It's not foolproof is an arbitrary command injection vulnerability
eval SECRETFILE="\$${VARNAME}"
echo "[secret-init] Setting SECRETFILE to ${SECRETFILE} ..." # DEBUG - rm for prod!
# if SECRETFILE exists
if [[ -f ${SECRETFILE} ]]; then
# strip the appended "__FILE" from environmental variable name
STRIPVAR=$(echo $VARNAME | sed "s/__FILE//g")
echo "[secret-init] Set STRIPVAR to ${STRIPVAR}" # DEBUG - rm for prod!
# set value to contents of secretfile
eval ${STRIPVAR}=$(cat "${SECRETFILE}")
echo "[secret_init] Set ${STRIPVAR} to $(eval echo \$${STRIPVAR})" # DEBUG - rm for prod!
export "${STRIPVAR}"
echo "[secret-init] Success! ${STRIPFILE} set from ${VARNAME}"
else
echo "[secret-init] ERROR: Cannot find secret in ${VARNAME}"
fi
done
COPY secret-init.sh /usr/local/bin/
ENTRYPOINT [ "docker-entrypoint.sh" ]
version: '3.3'
secrets:
NAMED_SECRET_ONE:
file: ../.secrets/secret_one.txt
NAMED_SECRET_TWO:
file: ../.secrets/secret_two.txt
services:
db:
image: mysql:5.7
secrets:
- NAMED_SECRET_ONE
- NAMED_SECRET_TWO
environment:
SECRET_ONE_ENV__FILE: /run/secrets/NAMED_SECRET_ONE
SECRET_TWO_ENV__FILE: /run/secrets/NAMED_SECRET_TWO
use if container uses s6 initiation
#! /bin/bash
# ref: https://github.com/linuxserver/docker-baseimage-alpine/blob/master/root/etc/cont-init.d/01-envfile
# in s6, environmental variables are written as text files for s6 to monitor
for FILENAME in $(find /var/run/s6/container_environment/ | grep "^.*__FILE"); do
echo "[secret-init] Evaluating ${FILENAME}"
# set SECRETFILE to the contents of the variable
SECRETFILE=$(cat ${FILENAME})
# SECRETFILE=${FILENAME}
echo "[secret-init] Setting SECRETFILE to ${SECRETFILE}..." # DEBUG - rm for prod!
# if SECRETFILE exists / is not null
if [[ -f ${SECRETFILE} ]]; then
# strip the appended "__FILE" from environmental variable name ...
STRIPFILE=$(echo $FILENAME | sed "s/__FILE//g")
echo "[secret-init] Set STRIPFILE to ${STRIPFILE}" # DEBUG - rm for prod!
# ... and set value to contents of secretfile
# since s6 uses text files, this is effectively "export ..."
cat ${SECRETFILE} > ${STRIPFILE}
echo "[secret-init] Set ${STRIPFILE} to $(cat ${STRIPFILE})" # DEBUG - rm for prod!"
echo "[secret-init] Success! ${STRIPFILE##*/} set from ${FILENAME##*/}"
else
echo "[secret-init] cannot find secret in ${FILENAME##*/}"
fi
done
COPY 01_envfile.sh /etc/cont-init.d/
Ref:
- https://medium.com/@adrian.gheorghe.dev/using-docker-secrets-in-your-environment-variables-7a0609659aab
- https://stackoverflow.com/questions/48094850/docker-stack-setting-environment-variable-from-secrets
- https://github.com/linuxserver/docker-baseimage-ubuntu/blob/bionic/root/etc/cont-init.d/01-envfile
- https://github.com/just-containers/s6-overlay#quickstart
- https://github.com/docker-library/mariadb/blob/master/docker-entrypoint.sh