13

I want to make a postgres database image but don't want to expose password and username which are stored as environment variable when produced using docker-compose.yml file. Basically, I don't want anyone to exec into the container and find out the variables.

One way is to use docker-secrets, but I don't want to to use docker swarm because my containers would be running on a single host.

my docker-compose file -

    version: "3"
    services:
       db:
         image: postgres:10.0-alpine
      environment:
         POSTGRES_USER: 'user'
         POSTGRES_PASSWORD: 'pass'
         POSTGRES_DB: 'db'

Things I have tried -

1) unset the environment variable at the end of entrypoint-entrypoint.sh

        for f in /docker-entrypoint-initdb.d/*; do
            case "$f" in
            *.sh)     echo "$0: running $f"; . "$f" ;;
            *.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
            *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
            *)        echo "$0: ignoring $f" ;;
            esac
            echo
        done
        unset POSTGRES_USER

nothing happened though. :(

2) init.sql inside docker-entrypoint-initdb.d, to create db, user and pass without using env. I shared the volume, as -

```
   volumes:
       - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
```

and, on my host, inside docker-entrypoint-initdb.d, I saved an init.sql as -

CREATE DATABASE docker_db;CREATE USER docker_user with encrypted password 'pass';GRANT ALL PRIVILEGES ON DATABASE docker_db TO docker_user;

I moved inside the running container and this file was there but, no user or database was created as mentioned in the file.

I have been stuck on this for past two days, any help is much appreciated.

Shirish Bajpai
  • 608
  • 1
  • 5
  • 16
  • If you set ENV in docker-compose it will contain the ENV, just try to run `docker exec -it db bash -c "printenv"` , while the unset work as expected. – Adiii Sep 18 '19 at 09:04
  • @Adiii , is there a way to create db and user other than specifying in the environment? Please read my entire question. – Shirish Bajpai Sep 18 '19 at 09:11
  • If the user can execute into the container, then the secrets, exposed to the container, will be accessible as files. So that solution is not entirely secure in that regard either. – Tommy Andersen Mar 31 '23 at 10:23

5 Answers5

10

use args without values to build the image in your Dockerfile:

ARG PASSWORD 

and build it using

export PASSWORD="MYPASS" && docker build ...

in this way the ARG is not there when running the container

here is a complete example:

dockerfile:

FROM postgres:10.0-alpine

ARG my_user
ARG my_pass

Compose:

version: "3"
services:
       db:
         build:
           context: .
           args:
            - my_user
            - my_pass       
         environment:
           - POSTGRES_USER=${my_user}
           - POSTGRES_PASSWORD=${my_pass}
           - POSTGRES_DB=db

run it:

export my_user=test && export my_pass=test1cd && docker-compose up -d --build

now if you login to the container and try echo $my_pass you get an empty string

result :

docker exec -ti 3b631d907153 bash

bash-4.3# psql -U test db
psql (10.0)
Type "help" for help.

db=#
LinPy
  • 16,987
  • 4
  • 43
  • 57
  • WARNING: The my_user variable is not set. Defaulting to a blank string. WARNING: The my_pass variable is not set. Defaulting to a blank string. ERROR: The Compose file './docker-compose.yml' is invalid because: services.db.environment contains {"POSTGRES_USER": ""}, which is an invalid type, it should be a string @LinPy – Shirish Bajpai Sep 18 '19 at 08:25
  • I just copy pasted your code in Dockerfile, compose file and run using your command. – Shirish Bajpai Sep 18 '19 at 08:31
  • The container is running fine(environment variable is updated to empty string) but the user is not created inside postgres. @LinPy – Shirish Bajpai Sep 18 '19 at 08:44
  • try to connect to the DB using your username/pasword wich you use with export, it should works, `docker exec -it db psql -U USER db` – LinPy Sep 18 '19 at 08:46
  • psql: FATAL: role "test" does not exist ```docker exec -it container_id psql -U test``` @LinPy I've used same user and pass as mentioned in you answer. – Shirish Bajpai Sep 18 '19 at 08:59
  • see the update you need to specify the db at the end of the command – LinPy Sep 18 '19 at 09:15
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/199617/discussion-between-shirish-and-linpy). – Shirish Bajpai Sep 18 '19 at 09:23
  • I tried to do the same, I could still see - POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB in the container environment. However for any other env variable other than these 3, its working fine.. any idea @LinPy? – Karan Raina Sep 18 '19 at 11:22
  • I declared another environmebt variable in docker-compose.yml file using the same syntax. I could see an empty sttring for that variables value from inside the container But for these 3 vars, i still see the values that ere passed. What happens when you do this? `docker exec -ti 3b631d907153 printenv` – Karan Raina Sep 18 '19 at 11:31
  • this is what i got `KARAN= POSTGRES_USER=testw POSTGRES_PASSWORD=test1cdqq POSTGRES_DB=db1 ` (KARAN was my custom env variable) – Karan Raina Sep 18 '19 at 11:33
  • @LinPy is there something to do with docker-compose version? Because, I updated my version now to -> ```docker-compose version 1.24.1, build 4667896``` and now, environment variables are not updating to empty strings, as mentioned by @KaranRaina – Shirish Bajpai Sep 18 '19 at 12:38
  • I used `docker-compose version 1.24.1, build 4667896b` and working fine with me, sorry I can not help you more – LinPy Sep 18 '19 at 12:43
8

update

Before using docker secrets in Compose, take into consideration this SO question and that github answer.


You can use docker secrets in Compose.

As stated in the relevant section of the docker postgresql docs:

As an alternative to passing sensitive information via environment variables, _FILE may be appended to some of the previously listed environment variables, causing the initialization script to load the values for those variables from files present in the container. In particular, this can be used to load passwords from Docker secrets stored in /run/secrets/<secret_name> files.

Therefore your compose file can read:

version: "3"
services:
  db:
    image: postgres:10.0-alpine
  environment:
    POSTGRES_USER_FILE: /run/secrets/user
    POSTGRES_PASSWORD_FILE: /run/secrets/pass
    POSTGRES_DB_FILE: /run/secrets/db
  secrets:
    - user
    - pass
    - db

secrets:
  user:
    file: user.txt
  pass:
    file: pass.txt
  db: 
    file: db.txt
raratiru
  • 8,748
  • 4
  • 73
  • 113
  • Aren't files in `/run/secrets` deleted if the image is re-built ? – Florent Aug 31 '22 at 19:47
  • @Florent I do not use secrets any more. Nevertheless I imagine that those files could be copied using a Dockerfile. The example above is somehow over simplified. – raratiru Sep 02 '22 at 23:02
1

For any other env variable, you can check @LinPy's answer. It smartly uses Docker Image Build-time Variables to override the values. But at least, I was unable to benefit in this case maybe because these were some "special" variables of postgres and was not able to override them(any explanation is welcome in the comment section).

So, now coming to the solution -

Problem - Don't want postgres' username/password to be visible as environment variable.

Solution - Don't specify them is compose's environment variable section.

Instead, make user/database using a script. Postgres' image runs an entrylevel script, which in turn looks for any .sh/.sql file in docker-entrypoint-initdb.d directory and run it.

Example,

directory structure -

-docker-compose.yml
-docker-entrypoint-initdb.d
  -init.sql

My docker-compose file -

version: "3"

services:
    db:
      image: postgres:10.0-alpine
      ports:
        - 8765:5432
      volumes:
        - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d

init.sql file

CREATE DATABASE docker_db;CREATE USER docker_user with encrypted password 'pass';GRANT ALL PRIVILEGES ON DATABASE docker_db TO docker_user;
docker-compose up
docker exec -it container-id bash
psql -U docker_user docker_db
psql (10.0)
Type "help" for help.

docker_db=>

Now, delete init.sql from your host, as it is shared volume, it will also be deleted from your container.

Shirish Bajpai
  • 608
  • 1
  • 5
  • 16
0

I don't want anyone to exec into the container and find out the variables.

This isn't realistic and I wouldn't worry about it.

The fundamental problem here is that Docker doesn't especially have any access controls over which docker commands someone can run. If you can run any docker command at all then you have unrestricted root-level access to the host. For instance, you can

docker run --rm -it -v /:/host busybox vi /host/etc/sudoers
docker exec -it myapp_db env
docker inspect myapp_dbadmin
docker exec -it myapp_app cat ./db_config.yml
docker run --rm -it -v /:/host busybox cat /host/$PWD/db_config.yml

The easiest thing to do here would be to not let any users you don't trust have access to the system at all. (If you're in a cloud environment, two cloud instances of half the size generally have the same cost as one bigger instance, so partitioning users this way could be straightforward.)

In theory you could limit things by making sure docker access is restricted (for instance, behind sudo), passing credentials only in files, and making sure the corresponding host files also have appropriate file permissions so they can't be read. That generally involves moving this configuration out of the docker-compose.yml file. It's not a "usual" Docker configuration but it does address the problem that environment variables aren't really that secure.

David Maze
  • 130,717
  • 29
  • 175
  • 215
0

You have this way described on the Postgres page of DockerHub.

"Docker Secrets

As an alternative to passing sensitive information via environment variables, _FILE may be appended to some of the previously listed environment variables, causing the initialization script to load the values for those variables from files present in the container. In particular, this can be used to load passwords from Docker secrets stored in /run/secrets/<secret_name> files. For example:

$ docker run --name some-postgres -e POSTGRES_PASSWORD_FILE=/run/secrets/postgres-passwd -d postgres

Currently, this is only supported for POSTGRES_INITDB_ARGS, POSTGRES_PASSWORD, POSTGRES_USER, and POSTGRES_DB."

What can be seen when inspecting the service:
enter image description here

Mohamed Bdr
  • 967
  • 3
  • 9
  • 20
Jean-Louis
  • 27
  • 7