feat: Enable reading env vars from files

This commit is contained in:
aartoni 2025-02-13 18:21:09 +01:00
parent 83bfe72d48
commit ea5e62ccba
4 changed files with 89 additions and 0 deletions

View File

@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. The format
### Breaking
- **environment variables** ending in `__FILE` will cause the content of the file addressed by their value to be written in the corresponding variable without the `__FILE` suffix
- **saslauthd** mechanism support via ENV `SASLAUTHD_MECHANISMS` with `pam`, `shadow`, `mysql` values has been removed. Only `ldap` and `rimap` remain supported ([#4259](https://github.com/docker-mailserver/docker-mailserver/pull/4259))
- **getmail6** has been refactored: ([#4156](https://github.com/docker-mailserver/docker-mailserver/pull/4156))
- The [DMS config volume](https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/optional-config/#volumes) now has support for `getmailrc_general.cf` for overriding [common default settings](https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/mail-getmail/#common-options). If you previously mounted this config file directly to `/etc/getmailrc_general` you should switch to our config volume support.

View File

@ -6,6 +6,10 @@ title: Environment Variables
Values in **bold** are the default values. If an option doesn't work as documented here, check if you are running the latest image. The current `master` branch corresponds to the image `ghcr.io/docker-mailserver/docker-mailserver:edge`.
!!! tip
If an environment variable `<VAR>__FILE` is set and points to a valid file, the content of that file will be loaded into `<VAR>`.
#### General
##### OVERRIDE_HOSTNAME

View File

@ -5,6 +5,7 @@ declare -A VARS
function _early_variables_setup() {
__environment_variables_log_level
__environment_variables_from_files
_obtain_hostname_and_domainname
__environment_variables_backwards_compatibility
__environment_variables_general_setup
@ -247,3 +248,24 @@ function __environment_variables_export() {
sort -o /root/.bashrc /root/.bashrc
sort -o /etc/dms-settings /etc/dms-settings
}
# This function reads any environment variable ending with `__FILE` from its
# referenced file, then makes it available under the same name without `__FILE`.
function __environment_variables_from_files() {
for file_env_var in $(env | grep -Po '^.+?__FILE'); do
local env_var="${file_env_var/__FILE/}"
local file_path="${!file_env_var}"
if [[ -n "${!env_var}" ]]; then
_log 'warn' "Ignoring ${env_var} since ${file_env_var} is also set"
continue
fi
if [[ -f "${file_path}" ]]; then
_log 'info' "Getting secret ${env_var} from ${file_path}"
export "${env_var}"="$(< "${file_path}")"
else
_log 'error' "File ${file_path} does not exist, defined in ${file_env_var}"
fi
done
}

View File

@ -0,0 +1,62 @@
load "${REPOSITORY_ROOT}/test/helper/setup"
load "${REPOSITORY_ROOT}/test/helper/common"
export CONTAINER1_NAME='dms-test_env-files_success'
export CONTAINER2_NAME='dms-test_env-files_warning'
export CONTAINER3_NAME='dms-test_env-files_error'
setup_file() {
export TEST__FILE
TEST__FILE=$(mktemp)
export NON_EXISTENT__FILE="/tmp/non_existent_secret"
echo 1 > "${TEST__FILE}"
}
teardown_file() {
rm -f "${TEST__FILE}"
docker rm -f "${CONTAINER1_NAME}" "${CONTAINER2_NAME}" "${CONTAINER3_NAME}"
}
@test "Environment variables are loaded from files" {
export CONTAINER_NAME="${CONTAINER1_NAME}"
local CUSTOM_SETUP_ARGUMENTS=(
--env ENABLE_POP3__FILE="${TEST__FILE}"
-v "${TEST__FILE}:${TEST__FILE}"
)
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
run docker logs "${CONTAINER_NAME}"
assert_success
assert_line --partial "Getting secret ENABLE_POP3 from ${TEST__FILE}"
_exec_in_container [ -f /etc/dovecot/protocols.d/pop3d.protocol ]
assert_success
}
@test "Existing environment variables take precedence over __FILE variants" {
export CONTAINER_NAME="${CONTAINER2_NAME}"
local CUSTOM_SETUP_ARGUMENTS=(
--env TEST="manual-secret"
--env TEST__FILE="${TEST__FILE}"
)
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
run docker logs "${CONTAINER_NAME}"
assert_success
assert_line --partial "Ignoring TEST since TEST__FILE is also set"
}
@test "Non-existent file triggers an error" {
export CONTAINER_NAME="${CONTAINER3_NAME}"
local CUSTOM_SETUP_ARGUMENTS=(
--env TEST__FILE="${NON_EXISTENT__FILE}"
)
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
run docker logs "${CONTAINER_NAME}"
assert_success
assert_line --partial "File ${NON_EXISTENT__FILE} does not exist"
}