Home > Software design >  Customize postgres start command in default docker postgres image
Customize postgres start command in default docker postgres image

Time:08-29

I want to change the start command of postgres to support SSL in the default docker image

db.Docerfile

FROM postgres:14.5-alpine

COPY ./.docker/dev/init-database.sh /docker-entrypoint-initdb.d/

COPY ./.docker/dev/migrations/database_schema.tar ./

COPY ./.docker/dev/certs/out/server.key /var/lib/postgresql

COPY ./.docker/dev/certs/out/server.crt /var/lib/postgresql

COPY ./.docker/dev/certs/out/myCA.crt /var/lib/postgresql

COPY ./.docker/dev/certs/out/myCA.crl /var/lib/postgresql

COPY ./.docker/dev/certs/out/news_user.key ./

COPY ./.docker/dev/certs/out/news_user.crt ./

RUN chown 0:70 /var/lib/postgresql/server.key && chmod 640 /var/lib/postgresql/server.key

RUN chown 0:70 /var/lib/postgresql/server.crt && chmod 640 /var/lib/postgresql/server.crt

RUN chown 0:70 /var/lib/postgresql/myCA.crt && chmod 640 /var/lib/postgresql/myCA.crt

RUN chown 0:70 /var/lib/postgresql/myCA.crl && chmod 640 /var/lib/postgresql/myCA.crl

RUN chown 0:70 ./news_user.key && chmod 640 ./news_user.key

RUN chown 0:70 ./news_user.crt && chmod 640 ./news_user.crt

RUN chown postgres:postgres /docker-entrypoint-initdb.d/init-database.sh

EXPOSE 5432

CMD postgres -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/myCA.crt -c ssl_crl_file=/var/lib/postgresql/myCA.crl


When I run this image, I get an error saying 
"root" execution of the PostgreSQL server is not permitted.
news_database  | The server must be started under an unprivileged user ID to prevent
news_database  | possible system security compromise.  See the documentation for
news_database  | more information on how to properly start the server.

The default postgres image comes with a user named postgres so I tried adding a user postgres line before CMD

Now it gives me a new error saying

postgres: could not access the server configuration file "/var/lib/postgresql/data/postgresql.conf": No such file or directory

Can someone kindly tell me how to fix this so that the postgres command works?

UPDATE 1

I am not running the dockerfile directly, I run it via a docker-compose.yml file since I have a python script that accesses the database in another container

docker.compose.yml

version: "3.8"
services:
  news_database:
    build:
      context: ../..
      dockerfile: ./.docker/dev/db.Dockerfile
    container_name: news_database
    restart: unless-stopped
    env_file:
      - .env
    ports:
      - "5432:5432"
    volumes:
      - news_db:/var/lib/postgresql/data
    # https://stackoverflow.com/a/55835081/5371505
    # https://stackoverflow.com/questions/72213661/test-connection-to-postgres-with-ssl-with-the-command-line
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d 'hostaddr=$DATABASE_HOST user=$DATABASE_USER port=$DATABASE_PORT dbname=$DATABASE_NAME'"]
      interval: 5s
      timeout: 5s
      retries: 5

  # Dont add a 'restart' policy to the app because we run it as a cronjob regardless of whether it succeeds or fails
  news_app:
    # https://stackoverflow.com/questions/65594752/docker-compose-how-to-reference-files-in-other-directories
    build:
      context: ../..
      dockerfile: ./.docker/dev/app.Dockerfile
    env_file:
      - .env
    image: news_app
    container_name: news_app
    depends_on:
      news_database:
        condition: service_healthy

volumes:
  news_db:
    driver: local

I run the above file with this command

docker compose -f ".docker/dev/docker-compose.yml" up -d --build news_database && docker compose -f ".docker/dev/docker-compose.yml" logs --follow

CodePudding user response:

I see a couple of problems with your Dockerfile. First, I've stripped out a few bits to reduce the complexity to something I can reproduce locally, so I have:

FROM postgres:14.5-alpine

COPY ./certs/out/server.key /var/lib/postgresql
COPY ./certs/out/server.crt /var/lib/postgresql

RUN chown 0:70 /var/lib/postgresql/server.key && chmod 640 /var/lib/postgresql/server.key
RUN chown 0:70 /var/lib/postgresql/server.crt && chmod 640 /var/lib/postgresql/server.crt

EXPOSE 5432

CMD postgres -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/myCA.crt -c ssl_crl_file=/var/lib/postgresql/myCA.crl

If we build a pgtest image from this Dockerfile and run it, we see:

$ docker build -t pgtest .
...
$ docker run --rm pgtest
"root" execution of the PostgreSQL server is not permitted.
The server must be started under an unprivileged user ID to prevent
possible system security compromise.  See the documentation for
more information on how to properly start the server.

What's going on here? It turns out this is caused by how you've written your CMD directive. You need to use the "exec style", which means passing in a JSON array rather than a string:

CMD ["postgres", \
  "-c", "ssl=on", \
  "-c", "ssl_cert_file=/var/lib/postgresql/server.crt", \
  "-c", "ssl_key_file=/var/lib/postgresql/server.key"]

This is necessary because there's a check in the entrypoint script for the image that looks like this:

if [ "$1" = 'postgres' ] && ! _pg_want_help "$@"; then

When you write:

CMD postgres -c ...

This wraps the command in a call to sh -c, so $1 will be sh, thus skipping the important database initialization logic. When we use the exec format of the CMD statement, $1 will be postgres, as expected.

With that fix, the image runs correctly:

$ docker run --rm -e POSTGRES_PASSWORD=secret pgtest
.
.
.
PostgreSQL init process complete; ready for start up.

2022-08-28 12:48:48.784 UTC [1] LOG:  starting PostgreSQL 14.5 on x86_64-pc-linux-musl, compiled by gcc (Alpine 11.2.1_git20220219) 11.2.1 20220219, 64-bit
2022-08-28 12:48:48.784 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
2022-08-28 12:48:48.784 UTC [1] LOG:  listening on IPv6 address "::", port 5432
2022-08-28 12:48:48.786 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
2022-08-28 12:48:48.789 UTC [48] LOG:  database system was shut down at 2022-08-28 12:48:48 UTC
2022-08-28 12:48:48.792 UTC [1] LOG:  database system is ready to accept connections
  • Related