Where is the pgpass file in pgadmin4 docker container when this file is mounted as a volume

Viewed 2633

I'm using the following image https://hub.docker.com/r/dpage/pgadmin4/ to set up pgAdmin4 on Ubuntu 18-04.

I have mounted a volume containing a pgpass file (which was also chmod for the pgadmin user inside the container) as you can see in my Compose file:

version: '3.8'
services:
  pgadmin4:
    image: dpage/pgadmin4
    container_name: pgadmin4
    environment:
      - PGADMIN_DEFAULT_EMAIL=me@localhost
      - PGADMIN_DEFAULT_PASSWORD=******************
      - PGADMIN_LISTEN_PORT=5050
      - PGADMIN_SERVER_JSON_FILE=servers.json
    volumes:
      - ./config/servers.json:/pgadmin4/servers.json # <-- this file is well taken into account
      - ./config/pgpass:/pgpass # <- there is no way to find this one on the other hand
    ports:
      - "5000:5000"
    restart: unless-stopped
    network_mode: host

But the it seems it's not recognized from the pgadmin webpage when I right click on a server and check its Advanced properties:

no pgpass file in pgadmin interface

And if I manually specify /pgpass in the top greenish box where there's only a slash in the image, it says:

pgpass not found

But if I log into the container, I can actually list that file:

/ $ ls -larth /pgpass
-rw-------    1 pgadmin  pgadmin      574 Mar 10 22:37 /pgpass

What did I do wrong?
How can I get the pgpass file to be recognized by the application?

5 Answers

I got it working with the following insight.

In servers.json when you specify:

"PassFile": "/pgpass"

It means that / in the path begins in the user's storage dir, i.e.

pattern:

/var/lib/pgadmin/storage/<USERNAME>_<DOMAIN>/

example:

/var/lib/pgadmin/storage/postgres_acme.com/

Here's a working example that copies everything into the right spot and sets the perms correctly.

  pgadmin:
    image: dpage/pgadmin4
    restart: unless-stopped
    environment:
      PGADMIN_DEFAULT_EMAIL: postgres@acme.com
      PGADMIN_DEFAULT_PASSWORD: postgres
      PGADMIN_LISTEN_ADDRESS: '0.0.0.0'
      PGADMIN_LISTEN_PORT: 5050
    tty: true
    ports:
      - 5050:5050
    volumes:
      - ~/data/pgadmin_data:/var/lib/pgadmin
      - ./local-cloud/servers.json:/pgadmin4/servers.json # preconfigured servers/connections   
      - ./local-cloud/pgpass:/pgadmin4/pgpass # passwords for the connections in this file
    entrypoint: >
      /bin/sh -c "
      mkdir -m 700 /var/lib/pgadmin/storage/postgres_acme.com;
      chown -R pgadmin:pgadmin /var/lib/pgadmin/storage/postgres_acme.com;
      cp -prv /pgadmin4/pgpass /var/lib/pgadmin/storage/postgres_acme.com/;
      chmod 600 /var/lib/pgadmin/storage/postgres_acme.com/pgpass;
      /entrypoint.sh
      " 

The following config worked for me:

  • pgpass
  • servers.json
  • docker-compose.yaml
  • dockerfile_for_pgadmin

pgpass

docker_postgres_db:5432:postgres:postgres:postgres

servers.json

{
  "Servers": {
    "1": {
      "Name": "docker_postgres",
      "Group": "docker_postgres_group",
      "Host": "docker_postgres_db",
      "Port": 5432,
      "MaintenanceDB": "postgres",
      "Username": "postgres",
      "PassFile": "/pgpass",
      "SSLMode": "prefer"
    }
  }
}

docker-compose.yaml

version: "3.9"
services:

  docker_postgres_db:
    image: postgres
    volumes:
      - ./postgres_db_data:/var/lib/postgresql/data # mkdir postgres_db_data before docker compose up
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - "15432:5432"


  pgadmin:
     build:
       context: .
       dockerfile: ./dockerfile_for_pgadmin
     environment:
       PGADMIN_DEFAULT_EMAIL: pgadmin@pgadmin.com
       PGADMIN_DEFAULT_PASSWORD: pgadmin
     ports:
       - "5050:80"
     volumes:
       - ./servers.json:/pgadmin4/servers.json # preconfigured servers/connections

dockerfile+for_pgadmin

FROM dpage/pgadmin4
USER pgadmin
RUN mkdir -p  /var/lib/pgadmin/storage/pgadmin_pgadmin.com
COPY ./pgpass /var/lib/pgadmin/storage/pgadmin_pgadmin.com/
USER root
RUN chown pgadmin:pgadmin /var/lib/pgadmin/storage/pgadmin_pgadmin.com/pgpass
RUN chmod 0600 /var/lib/pgadmin/storage/pgadmin_pgadmin.com/pgpass
USER pgadmin
ENTRYPOINT ["/entrypoint.sh"]

On pgAdmin 6.2, PassFile points to the absolute path inside container, instead of a path under STORAGE_DIR (/var/lib/pgadmin).

Before the entrypoint phase, just need to set owner and permissions for the pgpass file.

docker-compose.yml

  pgadmin:
    image: dpage/pgadmin4:6.2
    entrypoint: >
      /bin/sh -c "
      cp -f /pgadmin4/pgpass /var/lib/pgadmin/;
      chmod 600 /var/lib/pgadmin/pgpass;
      chown pgadmin:pgadmin /var/lib/pgadmin/pgpass;
      /entrypoint.sh
      "
    environment:
      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org}
      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin}
      PGADMIN_CONFIG_SERVER_MODE: "False"
      PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
    volumes:
      - ./config/servers.json:/pgadmin4/servers.json
      - ./config/pgpass:/pgadmin4/pgpass
    ports:
      - "${PGADMIN_PORT:-5050}:80"

servers.json

{
  "Servers": {
    "1": {
      "Name": "pgadmin4@pgadmin.org",
      "Group": "Servers",
      "Host": "postgres",
      "Port": 5432,
      "MaintenanceDB": "postgres",
      "Username": "postgres",
      "SSLMode": "prefer",
      "PassFile": "/var/lib/pgadmin/pgpass"
    }
  }
}

pgpass

postgres:5432:postgres:postgres:Welcome01

Update:

Updated entrypoint on docker-compose.yml and PassFile on servers.json for a cross platform working solution.

Update 2:

I created a container image (dcagatay/pwless-pgadmin4) for passwordless pgadmin4.

The problem here seems to be that '/' in the servers.json file does not mean '/' in the filesystem, but something relative to the STORAGE_DIR set in the config. In fact, a separate storage directory for each user is created, so with your user me@localhost you will have to mount ./config/pgpass to /var/lib/pgadmin4/storage/me_localhost/pgpass, but still refer to it as /pgpass in your servers.json.

I'm running the latest version of pgadmin4 as of this post (6.11). It took me forever to find the answer as to how to set a pgpass file location without storing it in the user's uploads dir (insecure IMO).

Unfortunately it does not seem to work using an absolute path e.g. /var/lib/pgadmin/pgpass.

However, what did work was this workaround I found here: https://github.com/rowanruseler/helm-charts/issues/72#issuecomment-1002300143

Basically if you use ../../pgpass, you can traverse the filesystem instead of the default behaviour of looking inside the user's uploads folder.

Example servers.json:

{
  "Servers": {
    "1": {
      "Name": "my-postgres-instance",
      "Group": "Servers",
      "Host": "postgres",
      "Port": 5432,
      "MaintenanceDB": "postgres",
      "Username": "postgres",
      "SSLMode": "prefer",
      "PassFile": "../../pgpass"
    }
  }
}

Also, setting the file permission as 0600 is a critical step - the file can not be world-readable, see https://stackoverflow.com/a/28152568/15198761 for more info.

In a K8s environment, using the offical pgadmin image I use a configmap for the servers.json along with the following command:

          command:
            - sh
            - -c
            - |
              set -e
              cp -f /pgadmin4/pgpass /var/lib/pgadmin/
              chown 5050:5050 /var/lib/pgadmin/pgpass
              chmod 0600 /var/lib/pgadmin/pgpass
              /entrypoint.sh

Using a combination of the above, I was finally able to connect to my postgres instance without needing to put in a password or keeping the pgpass file in the user's uploads dir.

Related