From d6487791b23906f313226281ddb34356988131be Mon Sep 17 00:00:00 2001 From: Petar Shtuchkin Date: Fri, 5 Dec 2025 16:32:07 +0200 Subject: [PATCH 1/5] REDIS_PASSWORD[_FILE] env and empy password warning --- debian/docker-entrypoint.sh | 200 ++++++++++++++++++++++++++++++------ 1 file changed, 169 insertions(+), 31 deletions(-) diff --git a/debian/docker-entrypoint.sh b/debian/docker-entrypoint.sh index d0a21fe4d..f58a83aef 100755 --- a/debian/docker-entrypoint.sh +++ b/debian/docker-entrypoint.sh @@ -90,6 +90,146 @@ fix_perms_and_owner() { done } +requirepass_arg_present() { + # TODO: maybe better to check that provided password is not empty? + for arg in "$@"; do + if [ "$arg" = "--requirepass" ]; then + return 0 + fi + done + return 1 +} + +# Assume that requirepass, include or user directives means that we have +# security settings and there is no point to show empty password warning. +config_has_security_settings() { + if [ ! -r "$1" ]; then + return 1 + fi + # TODO: skip user for sentinel as it always has one + grep -q -e '^[[:space:]]*\(requirepass\|include\|user\)[[:space:]][[:space:]]*[^[:space:]]' "$1" +} + +show_empty_password_warning() { + local config="$1" + shift + local pw + pw=$(get_provided_password) + if [ -z "$SKIP_PASSWORD_WARNING" ] && [ -z "$pw" ] && ! requirepass_arg_present "$@" && ! config_has_security_settings "$config"; then + cat >&2 <<-'EOF' + ******************************************************************************** + ******************************************************************************** + ** ** + ** ##### WARNING ##### ** + ** ** + ** NO PASSWORD HAS BEEN SET! ** + ** ** + ** Redis is starting WITHOUT authentication enabled. ** + ** Anyone with network access to this container can connect ** + ** to Redis without a password and execute commands. ** + ** ** + ** This is INSECURE and should only be used in development environments. ** + ** ** + ** To secure your Redis instance, set a password using one of: ** + ** - REDIS_PASSWORD or REDIS_PASSWORD_FILE environment variable ** + ** - requirepass directive in redis.conf ** + ** - --requirepass command line option ** + ** - ACL users configuration ** + ** ** + ** Use SKIP_PASSWORD_WARNING=1 to hide this warning. ** + ** ** + ******************************************************************************** + ******************************************************************************** +EOF + fi +} + +enquote_config_value() { + # TODO: properly escape and quote password for config + echo "$1" +} + +# Provide error handling +get_password_from_file() { + if [ ! -r "$1" ]; then + return 1 + fi + # TODO: check for multiline, null bytes, etc + head -1 "$1" +} + +get_provided_password() { + # TODO: Handle cases when both are set with priority or error + if [ -n "$REDIS_PASSWORD" ]; then + echo "$REDIS_PASSWORD" + return 0 + fi + # TODO: Make sure to fail if file is not readable and not optional + if [ -n "$REDIS_PASSWORD_FILE" ]; then + get_password_from_file "$REDIS_PASSWORD_FILE" || : + return 0 + fi +} + +create_password_arg() { + # nameref to caller's variable + local -n _out=$1 + local no_include_conf="$2" + + local pw + pw=$(get_provided_password) + if [ -z "$pw" ]; then + _out=() + return 0 + fi + + # Try to use config file for password, to avoid showing it in process list. + if [ -z "$no_include_conf" ] && [ -w /tmp ] && touch /tmp/requirepass.conf && chmod 0600 /tmp/requirepass.conf; then + echo "requirepass $(enquote_config_value "$pw")" > /tmp/requirepass.conf + _out=(--include /tmp/requirepass.conf) + return 0 + else + # Fallback to --requirepass if /tmp is not writable. + _out=(--requirepass "$pw") + fi +} + +create_module_args() { + # nameref to caller's variable + local -n _out=$1 + _out=() + + modules_dir="/usr/local/lib/redis/modules" + if [ ! -d "$modules_dir" ]; then + echo "Warning: Default Redis modules directory $modules_dir does not exist." + elif [ -n "$(ls -A $modules_dir 2>/dev/null)" ]; then + for module in "$modules_dir"/*.so; + do + if [ ! -s "$module" ]; then + echo "Skipping module $module: file has no size." + continue + fi + + if [ -d "$module" ]; then + echo "Skipping module $module: is a directory." + continue + fi + + if [ ! -r "$module" ]; then + echo "Skipping module $module: file is not readable." + continue + fi + + if [ ! -x "$module" ]; then + echo "Warning: Module $module is not executable." + continue + fi + + _out+=("--loadmodule" "$module") + done + fi +} + # first arg is `-f` or `--some-option` # or first arg is `something.conf` if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then @@ -106,7 +246,7 @@ if check_for_sentinel "$CMD" "$@"; then fi # if is server and its first arg is not an option then it's a config -if [ "$IS_REDIS_SERVER" ] && [ "${2#-}" = "$2" ]; then +if [ "$IS_REDIS_SERVER" ] || [ "$IS_REDIS_SENTINEL" ] && [ "${2#-}" = "$2" ]; then CONFIG="$2" fi @@ -147,38 +287,36 @@ if [ "$um" = '0022' ]; then umask 0077 fi +# inject generated arguments before user arguments to keep override ability +current_args=("$@") +head_args=("${current_args[@]}") +tail_args=() +if [ "$IS_REDIS_SERVER" ] || [ "$IS_REDIS_SENTINEL" ]; then + # head_args: command and (optionally) config + # tail_args: user supplied arguments + if [ -n "$CONFIG" ]; then + head_args=("${current_args[@]:0:2}") + tail_args=("${current_args[@]:2}") + else + head_args=("${current_args[@]:0:1}") + tail_args=("${current_args[@]:1}") + fi + + pass_arg=() + # sentinel doesn't support --include + create_password_arg pass_arg $IS_REDIS_SENTINEL + head_args+=("${pass_arg[@]}") + + show_empty_password_warning "$CONFIG" "$@" +fi + if [ "$IS_REDIS_SERVER" ] && ! [ "$IS_REDIS_SENTINEL" ]; then echo "Starting Redis Server" - modules_dir="/usr/local/lib/redis/modules/" - - if [ ! -d "$modules_dir" ]; then - echo "Warning: Default Redis modules directory $modules_dir does not exist." - elif [ -n "$(ls -A $modules_dir 2>/dev/null)" ]; then - for module in "$modules_dir"/*.so; - do - if [ ! -s "$module" ]; then - echo "Skipping module $module: file has no size." - continue - fi - - if [ -d "$module" ]; then - echo "Skipping module $module: is a directory." - continue - fi - - if [ ! -r "$module" ]; then - echo "Skipping module $module: file is not readable." - continue - fi - if [ ! -x "$module" ]; then - echo "Warning: Module $module is not executable." - continue - fi - - set -- "$@" --loadmodule "$module" - done - fi + module_args=() + create_module_args module_args + + head_args+=("${module_args[@]}") fi -exec "$@" +exec "${head_args[@]}" "${tail_args[@]}" From 67e57c59b375bbe6c9ec6200136a07d7f37e2683 Mon Sep 17 00:00:00 2001 From: Petar Shtuchkin Date: Fri, 5 Dec 2025 18:30:16 +0200 Subject: [PATCH 2/5] Explicitly handle special arguments --- debian/docker-entrypoint.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/debian/docker-entrypoint.sh b/debian/docker-entrypoint.sh index f58a83aef..04bf763b0 100755 --- a/debian/docker-entrypoint.sh +++ b/debian/docker-entrypoint.sh @@ -90,6 +90,18 @@ fix_perms_and_owner() { done } +first_arg_is_special() { + if [ "$1" = "-v" ] \ + || [ "$1" = "--version" ] \ + || [ "$1" = "-h" ] \ + || [ "$1" = "--help" ] \ + || [ "$1" = "--test-memory" ] \ + || [ "$1" = "--check-system" ]; then \ + return 0 + fi + return 1 +} + requirepass_arg_present() { # TODO: maybe better to check that provided password is not empty? for arg in "$@"; do @@ -292,6 +304,9 @@ current_args=("$@") head_args=("${current_args[@]}") tail_args=() if [ "$IS_REDIS_SERVER" ] || [ "$IS_REDIS_SENTINEL" ]; then + if first_arg_is_special "$2"; then + exec "${head_args[@]}" + fi # head_args: command and (optionally) config # tail_args: user supplied arguments if [ -n "$CONFIG" ]; then From ec97280feac63d365d17dee7a24097c81dc35600 Mon Sep 17 00:00:00 2001 From: Petar Shtuchkin Date: Mon, 8 Dec 2025 15:47:51 +0200 Subject: [PATCH 3/5] Fork with sleep to show warning after all logs --- debian/docker-entrypoint.sh | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/debian/docker-entrypoint.sh b/debian/docker-entrypoint.sh index 04bf763b0..ada2f6589 100755 --- a/debian/docker-entrypoint.sh +++ b/debian/docker-entrypoint.sh @@ -122,13 +122,11 @@ config_has_security_settings() { grep -q -e '^[[:space:]]*\(requirepass\|include\|user\)[[:space:]][[:space:]]*[^[:space:]]' "$1" } -show_empty_password_warning() { - local config="$1" - shift - local pw - pw=$(get_provided_password) - if [ -z "$SKIP_PASSWORD_WARNING" ] && [ -z "$pw" ] && ! requirepass_arg_present "$@" && ! config_has_security_settings "$config"; then - cat >&2 <<-'EOF' +sleep_and_print_empty_password_warning() { + if [ -n "$1" ]; then + sleep "$1" + fi + cat >&2 <<-'EOF' ******************************************************************************** ******************************************************************************** ** ** @@ -153,6 +151,17 @@ show_empty_password_warning() { ******************************************************************************** ******************************************************************************** EOF +} + +show_empty_password_warning() { + local config="$1" + shift + local pw + pw=$(get_provided_password) + + if [ -z "$SKIP_PASSWORD_WARNING" ] && [ -z "$pw" ] && ! requirepass_arg_present "$@" && ! config_has_security_settings "$config"; then + export -f sleep_and_print_empty_password_warning + setsid -f bash -c 'sleep_and_print_empty_password_warning 1' fi } From ad72ccc3220e9bf8a2896a529eb8b4bb0a06b345 Mon Sep 17 00:00:00 2001 From: Petar Shtuchkin Date: Mon, 8 Dec 2025 16:21:07 +0200 Subject: [PATCH 4/5] Add workflow dispatch to pre-merge --- .github/workflows/pre-merge.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/pre-merge.yml b/.github/workflows/pre-merge.yml index 59e8eeab2..d907f7402 100644 --- a/.github/workflows/pre-merge.yml +++ b/.github/workflows/pre-merge.yml @@ -20,6 +20,17 @@ on: docker_images_metadata: description: 'Array of structured Docker images metadata that were published' value: ${{ jobs.collect-images-metadata.outputs.docker_images_metadata }} + workflow_dispatch: + inputs: + release_tag: + description: 'Release tag to build' + required: true + type: string + publish_image: + description: 'Publish Docker image to GHCR' + required: false + type: boolean + default: false jobs: build-and-test: From 9ffc09a68d398a202bc4648a1671b2b2f6a63a5a Mon Sep 17 00:00:00 2001 From: Petar Shtuchkin Date: Mon, 8 Dec 2025 16:23:13 +0200 Subject: [PATCH 5/5] release_tag not required for pre-merge --- .github/workflows/pre-merge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-merge.yml b/.github/workflows/pre-merge.yml index d907f7402..7304b5a18 100644 --- a/.github/workflows/pre-merge.yml +++ b/.github/workflows/pre-merge.yml @@ -24,7 +24,7 @@ on: inputs: release_tag: description: 'Release tag to build' - required: true + required: false type: string publish_image: description: 'Publish Docker image to GHCR'