Allow Use of Docker Secrets and Automagically URL Encode DB Passwords

This feature request comes with a working solution. I’m just not sure where to implement it in the Vaultwarden repo so i can’t create create a pull request proposing an implementation myself.

Feature Request
Support passing a raw (i.e. non-url encoded) database password as a secret to the Vaultwarden docker container.

Background
When using MariaDB, MySQL, or PostgreSQL as the database backend for Vaultwarden, you must specify DATABASE_URL as a environmental variable. Additionally, the password for these must be URL Encoded. It is assumed that you will construct this variable outside of the container and pass it as a variable, exposing it to all the world.

However, it is possible to make the docker container construct it using just 15 lines of code instead. You won’t even need to add any packages.

Barebones Example:
The set-env-from-secrets.sh script is used to read docker secret db_password, url encode it, and use it to construct and set the DATABASE_URL environmental variable. To ensure it is run before vaultwarden, it is copied to /etc/vaultwarden.d/set-env-from-secrets.sh in the vaultwarden container.

In addition, the set-env-from-secrets.sh file also sets the ADMIN_TOKEN variable, so you can check the admin diagnostics page to verify PostgresSQL is being used as the backend.

This example assumes the following directory layout:

docker-compose.yaml
./config/set-env-from-secrets.sh
./secrets/admin_token
./secrets/db_password

File Contents

docker-compose.yaml
version: '3'
services:
  postgres:
    image: docker.io/library/postgres:14
    restart: always
    environment:
      POSTGRES_DB: 'vaultwarden'
      POSTGRES_USER: 'warden'
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    volumes:
      - postgres_vol:/var/lib/postgresql/data:rw
    expose:
      - 5432

  vaultwarden:
    image: docker.io/vaultwarden/server:latest
    depends_on:
      - postgres
    restart: always
    environment:
      URL_TEMPLATE: 'postgres://warden:%s@postgres/vaultwarden'
    configs:
      - source: set_vaultwarden_env
        target: /etc/vaultwarden.d/set-env-from-secrets.sh
    secrets:
      - admin_token
      - db_password
    volumes:
      - vaultwarden_vol:/data/:rw
    ports:
      - "8080:80"

secrets:
  admin_token:
    file: ./secrets/admin_token
  db_password:
    file: ./secrets/db_password

configs:
  set_vaultwarden_env:
    file: ./config/vaultwarden/set-env-from-secrets.sh

volumes:
  postgres_vol:
  vaultwarden_vol:
set-env-from-secrets.sh
#!/usr/bin/env sh
url_encode () {
  string=$*
  while [ -n "$string" ]; do
    tail=${string#?}
    head=${string%$tail}
    case $head in
      [-._~0-9A-Za-z]) printf %c "$head";;
      *) printf %%%02x "'$head"
    esac
    string=$tail
  done
  echo
}
export DATABASE_URL=$(printf "$URL_TEMPLATE" "$(url_encode $(cat /run/secrets/db_password))")
export ADMIN_TOKEN=$(cat /run/secrets/admin_token)

The admin_token and db_password files contain password strings. Set them yourself or generate them via:

gpg --dry-run --gen-random --armor 1 64 > ./secrets/admin_token 2> /dev/null
gpg --dry-run --gen-random --armor 1 64 > ./secrets/db_password 2> /dev/null

I’m not sure if we want this as part of the codebase it self.
But a mention of this somewhere in the wiki probably somewhere regarding docker-compose would be nice i think.

A clever workaround (at least for postgres) is to use libpq’s environment to pass the credentials out of band from DB url. This avoids the encoding issue and prevents the information from being presented on the admin page.
If/when diesel stops using libpq - then this might break.