simplerisk / docker

SimpleRisk Docker Repository
https://www.simplerisk.com
Mozilla Public License 2.0
29 stars 21 forks source link

support secrets #83

Open kale1d0code opened 5 months ago

kale1d0code commented 5 months ago

the readme does not provide information on how to use secrets, many projects opt for a prefix or suffix like "_FILE" for their environment variables.

I have a posix compliant function for fetching secrets and populating environment variables if you need one provided. my function also supports default values if the variable is not set.

jsokol commented 5 months ago

If you have examples for how you would propose for this to work, we are happy to consider implementing them into the project.

kale1d0code commented 5 months ago

take the DB_SETUP_PASS environment variable as an example

you can expect a DB_SETUP_PASS_FILE environment variable which would be set to a file path (typically /run/secrets/)

if this environment variable is set then take the contents of the file found in DB_SETUP_PASS_FILE and set DB_SETUP_PASS with it. if the environment variable is not set then it can check to see if DB_SETUP_PASS has been set otherwise use a default.

I made the following for my own docker images:

load_envs() {
  local var_str="$1"
  local var
  local def

  for pair in $var_str; do
    var="${pair%%:-*}"
    def="${pair#*:-}"
    file_env "$var" "$def"
  done
}

file_env() {
  local var="$1"
  local fileVar="${var}_FILE"
  local def="${2:-}"

  local varVal=$(eval "echo \$$var")
  local fileVarVal=$(eval "echo \$$fileVar")

  if [ -n "$varVal" ] && [ -n "$fileVarVal" ]; then
    echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
    exit 1
  fi

  local val="$def"
  if [ -n "$varVal" ]; then
    val="$varVal"
  elif [ -n "$fileVarVal" ]; then
    echo "loading $var from file"
    val=$(< "$fileVarVal")
  fi

  export "$var"="$val"
  unset "$fileVar"
}

and can be used like

load_envs "DATABASE_HOST DATABASE_PORT:-3306 DATABASE_NAME DATABASE_USER DATABASE_PASS ADMIN_USER:-admin ADMIN_PASS:-secret"

I have also made sure that these functions work in a posix compliant environment

kale1d0code commented 5 months ago

placing values like passwords for database connections or certificates for tls within environment variables is inherently insecure as these can easily be printed out and more importantly it mixes sensitive information with deployment files (passwords embedded in docker-compose files etc) docker secrets avoid this issue by mounting a file containing the secret within /run/secrets/ and then only populating the environment variable the secret is for on the process which docker is watching inside the container. any other process like a shell session into the container will not be able to see the secret within the environment variable.

This can be taken one step further by changing the config.php file to use a function to resolve the values from environment variables instead of the values being stored plain text within the config file. Wordpress takes a similar approach with their docker flavour of deployment using a function to populate their config file from environment variables.

The function I provided would be used within the entrypoint script to populate the environment variables containing secrets. The PHP function for the config.php is not required although would synergise with the entrypoint function.

This would enable end users to set secrets for the root database password, database name, user name, hostname, etc.

example docker-compose file could be as follows:

version: 3.8

secrets:
  dbms_root_pass:
    external: true
    name: grc-dbms-root-pass
  db_name:
    external: true
    name: grc-db-name
  db_user:
    external: true
    name: grc-db-user
  db_pass:
    external: true
    name: grc-db-pass

services:
  simplerisk:
    image: simplerisk/simplerisk-minimal:${SIMPLERISK_VERSION:-latest}
    restart: always
    depends_on:
    - dbms
    environment:
    - SIMPLERISK_DB_HOSTNAME=dbms
    - SIMPLERISK_DB_PORT=3306
    - DB_SETUP=manual
    - DB_SETUP_USER=root
    - DB_SETUP_PASS_FILE=/run/secrets/dbms_root_pass
    - SIMPLERISK_DB_USERNAME_FILE=/run/secrets/db_user
    - SIMPLERISK_DB_PASSWORD_FILE=/run/secrets/db_pass
    - SIMPLERISK_DB_DATABASE_FILE=/run/secrets/db_name
    - SIMPLERISK_DB_FOR_SESSIONS=false
    - SIMPLERISK_CRON_SETUP=true
    secrets:
    - db_name
    - db_user
    - db_pass
    - dbms_root_pass

  dbms:
    image:  mariadb:${MARIADB_VERSION}
    restart: always
    command: --sql-mode=""
    environment:
    - MARIADB_ROOT_PASSWORD_FILE=/run/secrets/dbms_root_pass
    #- MARIADB_DATABASE_FILE=/run/secrets/db_name
    #- MARIADB_USER_FILE=/run/secrets/db_user
    #- MARIADB_PASSWORD_FILE=/run/secrets/db_pass
    secrets:
    #- db_name
    #- db_user
    #- db_pass
    - dbms_root_pass
jsokol commented 5 months ago

I sincerely appreciate the additional explanation. We are a product built by and for security practitioners so I understood the value proposition when you originally mentioned it. That's why it has remained open. That said, all of our resources are tied up working on other projects right now so this will need to be on the backburner. I will keep it open as a reminder of a future enhancement to the Docker image, but since both our customers and us use these images, changing them would need to come with a deployment plan, as well, so no small task.