Safe ways to specify postgres parameters for healthchecks in docker compose

50.2k views Asked by At

I'm using secrets to manage username, password, and dbname with a Docker stack using Postgres as a DB. I now want to use the healthcheck feature that Docker provides.

docker-compose.yml

x-db-secrets: &db_secrets
    - psql_user
    - psql_pass
    - psql_dbname

services:
  db:
    image: postgres:13.1
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      - POSTGRES_USER_FILE=/run/secrets/psql_user
      - POSTGRES_DB_FILE=/run/secrets/psql_dbname
      - POSTGRES_PASSWORD_FILE=/run/secrets/psql_pass
    secrets: *db_secrets
    healthcheck:
      test: pg_isready -U myuser -d db_prod
      interval: 10s
      timeout: 3s
      retries: 3

(... other services...)

volumes:
  postgres_data:
  static_content:
  media_content:

secrets:
  psql_user:
    external: true
  psql_pass:
    external: true
  psql_dbname:
    external: true

As can be noted in the healthcheck: section, I'm exposing the db username & the dbname with the healthcheck. My question (with some follow-up based on the answer):

  • Does it matter? The docker-compose.yml file doesn't live on the host where this code will run. Therefore a potential attacker wouldn't have access to this info from there... however, since docker will perform the healthcheck, it would possibly live traces in that container (shell? docker configs files where that procedure is stored?)
  • If it does matter, how can I perform the healthchecks without exposing that information? I would have to use the secrets directly, however I cannot figure out how to use the secrets in the docker-compose file...

Thoughts? Workaround?

Additional details:

  • I'm currently checking if the DB is up from the (flask) container that uses it. E.g. the flask service checks if it can ping the DB or not. However I'd rather compartmentalize things, and the DB container should be the one telling if it's healthy or not (imo).
4

There are 4 answers

7
Josh Dando On BEST ANSWER

So this can be done by using a .env file and slightly modifiying your docker-compose.yml file.

POSTGRES_HOST=db
POSTGRES_USER=root
POSTGRES_PASSWORD=password
POSTGRES_DB=dev
services:
  db:
    image: postgres:13.1
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    env_file:
      - .env
    secrets: *db_secrets
    healthcheck:
      test: ["CMD-SHELL", "sh -c 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}'"]
      interval: 10s
      timeout: 3s
      retries: 3
5
pringi On

You can use pg_isready without any username/password to check if container is 'healthy'.

  bbepostgres:
    image: postgres:14.2
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=pangea_local_dev
      - PGUSER=postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"]
      interval: 30s
      timeout: 60s
      retries: 5
      start_period: 80s  

This will do the same thing as you wanted.

Reference: https://github.com/peter-evans/docker-compose-healthcheck

0
Florentin On

Since, the docker socket is on the host, the healthcheck command is visible through docker inspect, so it does (really) matter only if the password could be retrieved from username and/or dbname.

One could say that it always matter, but it depends on your needs.

0
Eyal Solomon On

as mentioned above, you can use the pg_isready

I also added a 'dummy' container to wait for multiple container, for example postgres and mysql

version: "3.2"

services:
  postgres-db:
    image: postgres:13
    restart: always
    environment:   
      POSTGRES_PASSWORD: password   
    ports:
      - "5432:5432"
    networks:
      - default
    healthcheck:
      test: pg_isready -U postgres
    enter code here

  mysql-db:
    platform: linux/x86_64
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "3306:3306"
    healthcheck:
      test: 'mysql --user=root --password=password --execute "SHOW DATABASES;"'
    
  ready:
    image: hello-world
    depends_on:
      postgres-db:
        condition: service_completed_successfully
      mysql-db:
        condition: service_healthy