Docker Community Forums

Share and learn in the Docker community.

'unset' environment variables

docker

(Vir2l) #1

Hi,
I hope I’m in the right place.
I would like to delete the environment variables set by docker-compose.

This is also possible via command line but not via script. Even if I had “sourced” the script (source ./myscript.sh)

More details:
I am using a modified alpine-image with mariadb.
A “run.sh” - Script is used as entrypoint. This script checks wether the DB is already set up properly or not. If not all necessary DBs are created. Therefore I submit some environment variables (such as DB-name, DB-user and DB-pass) via docker-compose.

So far so good, all works fine. But I would like to remove all these environment variables after DB-creation (or in case no creation was needed) for security reason. But nothing seems to work as suspected :frowning:

I tried:

  • modified the “run.sh” entrypoint to ‘unset’ the variables … no success (because it runs in a child-process of bash)
  • created a script that launched once at docker-boot (using supervisord) … no success
  • even with ‘source /path/to/script.sh’ it doesn’t remove/unset any variables :frowning:

So what else can I do to get those vars deleted?

Thanks in Advance
David


(Sam) #2

i don’t think there is a way to accomplish this with env vars… I suggest put those in a separate script that you check and call if present, then erase the file when you are done.


(David Maze) #3

That should totally be possible. Can you say more about your container script setup?

The pattern I cribbed from the standard containers is to have a single ENTRYPOINT script (like this one) that does a bunch of setup and then actually launches the CMD process. In very short they tend to look like

#!/bin/sh
if "$1" == "mariadb"; then
  # do database setup here
  unset MARIADB_FOO INITIAL_SETUP_BAR
fi
exec "$@"

This all is subject to the usual rules on environment variables (a process can never affect its parent’s environment or children that have already started, only its own current environment and its future children).

How are you checking that the variables are/aren’t set?

I’ve done very little with supervisord, but if you’re using it, it looks like you can set environment=FOO="bar",BAZ="quux" for individual processes, and you could at least force these variables to empty strings.


(Vir2l) #4

dockerfile

FROM alpine:latest
RUN apk update && apk upgrade \
  && apk add --update --no-cache mariadb \
  && rm -f /var/cache/apk/*
COPY inc/run.sh /usr/bin/run.sh
RUN chmod +x /usr/bin/run.sh
VOLUME ["/var/lib/mysql", "/run/mysqld"]
EXPOSE 3306/tcp
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

supervisord.conf

...snip
[program:mariadb]
command=/usr/bin/run.sh
...snap

run.sh

#!/bin/bash

if [ "$(ls -A /var/lib/mysql/)" ]; then
  echo "[i] MySQL directory already present, skipping creation"
else
  echo "[i] MySQL data directory not found, creating initial DBs"
  mysql_install_db --user=mysql --skip-name-resolve > /dev/null

  if [ "$MYSQL_ROOT_PASSWORD" = "" ]; then
    MYSQL_ROOT_PASSWORD=<db_rootpass>
    echo "[i] MySQL root Password: $MYSQL_ROOT_PASSWORD"
  fi

  MYSQL_DATABASE=${MYSQL_DATABASE:-""}
  MYSQL_USER=${MYSQL_USER:-""}
  MYSQL_PASSWORD=${MYSQL_PASSWORD:-""}

  if [ ! -d "/run/mysqld" ]; then
    mkdir -p /run/mysqld
  fi

  tfile=`mktemp`
  if [ ! -f "$tfile" ]; then
      return 1
  fi

  cat << EOF > $tfile
USE mysql;
FLUSH PRIVILEGES;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY "$MYSQL_ROOT_PASSWORD" WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY "$MYSQL_ROOT_PASSWORD" WITH GRANT OPTION;
DELETE FROM user WHERE user='';
DELETE FROM user WHERE password='';
EOF

  if [ $MYSQL_DATABASE != "" ]; then
    echo "[i] Creating database: $MYSQL_DATABASE"
    echo "CREATE DATABASE IF NOT EXISTS $MYSQL_DATABASE CHARACTER SET utf8 COLLATE utf8_general_ci;" >> $tfile
    if [ $MYSQL_USER != "" ]; then
      echo "[i] Creating user: $MYSQL_USER with password $MYSQL_PASSWORD"
      echo "GRANT ALL ON $MYSQL_DATABASE.* to '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD';" >> $tfile
      echo "GRANT ALL ON $MYSQL_DATABASE.* to '$MYSQL_USER'@'localhost' IDENTIFIED BY '$MYSQL_PASSWORD';" >> $tfile
    fi
  fi
  /usr/bin/mysqld --user=root --bootstrap --verbose=0 < $tfile
  rm -f $tfile
fi

exec /usr/bin/mysqld --user=root --console

docker-compose.yml

...snip
mariadb:
  container_name: mariadb
  image: <image>
  volumes:
    - ./data/db/:/var/lib/mysql/
  environment:
    MYSQL_ROOT_PASSWORD: <db_rootpass>
    MYSQL_DATABASE: <db_name>
    MYSQL_USER: <db_user>
    MYSQL_PASSWORD: <db_userpass>
...snap

Simple ‘unset’-script

#!/bin/bash
unset -v MYSQL_ROOT_PASSWORD
unset -v MYSQL_DATABASE
unset -v MYSQL_USER
unset -v MYSQL_PASSWORD

I launched this ‘unset’-script via supervisord with all three method (one at a time)

[program:unset]
command=/usr/bin/unset.sh

[program:unset]
command=source /usr/bin/unset.sh

[program:unset]
command=/bin/bash -c 'source /usr/bin/unset.sh'

That’s it :slight_smile:


(David Maze) #5

I’m mildly curious what else your database server container is doing, that you need to run it under supervisord and that you’re worried about processes potentially seeing the password. (The mysqld already knows the database name and root password, it needs them to run.)

You need to invoke it from within your run.sh script with a line like

. /usr/bin/unset.sh

It does not need to be executable.

In practice I’d probably use the standard mariadb image over rolling my own; there’s a standard pattern for doing startup-time tasks like creating database tables. If you kept with this image, I probably wouldn’t make /run/mysqld an implicit VOLUME; it seems likely to get the database engine confused over whether or not it’s running (because an old pid file got left over from a container that’s been since deleted).


(Vir2l) #6

Its just a simplified snippet. I use supervisord for two reasons… One is for logging purpose and the other to double restart capabilities (inside using supervisor, outside using docker-compose/restart). I am pretty happy with this, its solid and fits my needs. Another aspect is, that all my other images works the same way. So its easier for me to handle :wink:

Can you explain this a bit deeper?


(David Maze) #7

VOLUME effectively does two things. The subtle thing is that it freezes the directory named in the image, and any later changes to that directory are silently lost. The more interesting thing here is that it causes Docker to automatically create a default named volume to store that data when it’s run. But, again, the behavior is different depending on how it’s run (--mount is newer and has similar effects to -v):

(*) If you launch the container with -v and a host path for that directory, whatever was in that host path (which could be nothing) appears in container space and hides what was in the image.

(*) If you launch the container with -v and the name of a volume that already exists, the same thing.

(*) If you launch the container with -v and the name of a volume that doesn’t exist, then Docker creates a volume (which is normal) and copies the contents of the target directory from the image into the new volume (again, assuming the target directory is a declared VOLUME).

(*) If you launch the container without -v for the target directory, then Docker automatically creates a volume for you.

It used to be that the implicit volume creation in the last step used a consistent name, so if you started a container, stopped it, deleted it, and started a new container, it would reuse the old volume. I can’t find that in the current Docker documentation (indeed, starting from the Dockerfile VOLUME write-up, I can’t find anything about its runtime effects). It might be based on the container’s --name.

So, the quoted comment: usually what gets stored in /run is small temporary files that ensure that not more than one copy of a daemon is running and tell *ctl commands what process ID it is. If it’s recorded that mysqld is running and is pid 2, and then the container stops and is recreated, you could run into trouble where the next container sees the previous container’s /run directory and pid files and says “but the daemon is already running, I’m not starting myself”.


(Vir2l) #8

You are totally right :slight_smile: I was wondering about VOLUME in my Dockerfile for long, and if its useless for my case.

I’m honest: The sense of VOLUME has not really opened up to me yet. I am completely satisfied with the easy integration by -v /host/path: /container/path.

VOLUME may make sense if you work on the stack with a lot of people. In the sense of a guideline or if you run bigger swarms with network-storage systems. But for me alone? What do I include? Folder to store the data I want to keep (Webroot, databases) or configs I want to keep. And of course, i would also prefer to keep /run in its original place and leave it untouched.

I will remove VOLUME and go on with the run-script to clear it out there, too. Than I come back to the inital problem of unsetting env vars :wink:

A big Thank You and :+1: