Merge pull request #8588 from dokku/sanitize-openresty-filenames

This commit is contained in:
Jose Diaz-Gonzalez
2026-05-09 15:36:01 -04:00
committed by GitHub
4 changed files with 97 additions and 2 deletions

View File

@@ -124,6 +124,8 @@ The following folders within an app repository may have `*.conf` files that will
- `openresty/http-includes/`: Injected in the `server` block serving http(s) requests for the app.
- `openresty/http-location-includes/`: Injected in the `location` block that proxies to the app in the app's respective `server` block.
Custom snippets filenames may only include alphanumeric, underscore, and dot characters. For security reasons, filenames that contain other characters will be ignored.
### Label Management
The OpenResty plugin allows you to add custom container labels to apps. These labels are injected into containers during deployment and can be used to configure OpenResty behavior beyond what the plugin provides by default.

View File

@@ -4,6 +4,42 @@ source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/internal-functions"
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
fn-openresty-vhosts-copy-includes-from-source() {
declare desc="copy regular include files from the app repo; abort on unsafe filenames"
declare SRC_DIR="$1" DEST_DIR="$2"
local file filename
[[ -d "$SRC_DIR" ]] || return 0
for file in "$SRC_DIR"/*; do
[[ -e "$file" ]] || continue
filename="$(basename "$file")"
if [[ "$filename" =~ [^a-zA-Z0-9_.-] ]]; then
dokku_log_fail "Aborting deploy: include file has unsafe filename: $filename"
fi
[[ -L "$file" ]] && continue
[[ -f "$file" ]] || continue
cp -f "$file" "$DEST_DIR/"
done
}
fn-openresty-vhosts-sanitize-extracted-includes() {
declare desc="post-extract: abort on unsafe filenames; remove non-regular files"
declare TARGET_DIR="$1"
local file filename
[[ -d "$TARGET_DIR" ]] || return 0
for file in "$TARGET_DIR"/*; do
[[ -e "$file" ]] || continue
filename="$(basename "$file")"
if [[ "$filename" =~ [^a-zA-Z0-9_.-] ]]; then
dokku_log_fail "Aborting deploy: include file has unsafe filename: $filename"
fi
if [[ -L "$file" ]] || [[ ! -f "$file" ]]; then
rm -rf "$file"
fi
done
}
fn-openresty-vhosts-copy-from-image() {
declare APP="$1" IMAGE_NAME="$2"
local CONF_PATH="openresty/http-includes"
@@ -12,12 +48,14 @@ fn-openresty-vhosts-copy-from-image() {
mkdir -p "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP"
find "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/" -maxdepth 1 -name 'openresty-http-includes.*' -type d -exec rm -r {} +
copy_dir_from_image "$IMAGE_NAME" "$CONF_PATH" "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID" 2>/dev/null || true
fn-openresty-vhosts-sanitize-extracted-includes "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID"
if [[ ! -f "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID" ]]; then
touch "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID.missing"
fi
find "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/" -maxdepth 1 -name 'openresty-location-includes.*' -type d -exec rm -r {} +
copy_dir_from_image "$IMAGE_NAME" "$LOCATION_CONF_PATH" "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-location-includes.$DOKKU_PID" 2>/dev/null || true
fn-openresty-vhosts-sanitize-extracted-includes "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-location-includes.$DOKKU_PID"
if [[ ! -f "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-location-includes.$DOKKU_PID" ]]; then
touch "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-location-includes.$DOKKU_PID.missing"
fi
@@ -32,7 +70,7 @@ fn-openresty-vhosts-copy-from-directory() {
pushd "$SOURCECODE_WORK_DIR" >/dev/null
find "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/" -maxdepth 1 -name 'openresty-http-includes.*' -type d -exec rm -r {} +
mkdir -p "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID/"
cp -f "$CONF_PATH"/* "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID/"
fn-openresty-vhosts-copy-includes-from-source "$CONF_PATH" "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID"
popd &>/dev/null || pushd "/tmp" >/dev/null
else
touch "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID.missing"
@@ -42,7 +80,7 @@ fn-openresty-vhosts-copy-from-directory() {
pushd "$SOURCECODE_WORK_DIR" >/dev/null
find "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/" -maxdepth 1 -name 'openresty-location-includes.*' -type d -exec rm -r {} +
mkdir -p "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-location-includes.$DOKKU_PID/"
cp -f "$LOCATION_CONF_PATH"/* "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-location-includes.$DOKKU_PID/"
fn-openresty-vhosts-copy-includes-from-source "$LOCATION_CONF_PATH" "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-location-includes.$DOKKU_PID"
popd &>/dev/null || pushd "/tmp" >/dev/null
else
touch "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-location-includes.$DOKKU_PID.missing"

View File

@@ -43,6 +43,18 @@ trigger-openresty-vhosts-docker-args-process-deploy() {
continue
fi
# check that filename includes only alphanumeric characters, underscores, and dots
if [[ "$filename" =~ [^a-zA-Z0-9_.-] ]]; then
dokku_log_warn "Skipping include file with non-alphanumeric characters: $filename"
continue
fi
# check that filename does not contain any special characters
if [[ "$filename" =~ [\'\"\ \$\`\(\)\;] ]]; then
dokku_log_warn "Skipping include file with unsafe filename: $filename"
continue
fi
DATA="$(base64 -w 0 <"$include_dir/$filename")"
output="$output '--label=openresty.include-http-$filename=$DATA'"
done
@@ -61,6 +73,18 @@ trigger-openresty-vhosts-docker-args-process-deploy() {
continue
fi
# check that filename includes only alphanumeric characters, underscores, and dots
if [[ "$filename" =~ [^a-zA-Z0-9_.-] ]]; then
dokku_log_warn "Skipping include file with non-alphanumeric characters: $filename"
continue
fi
# check that filename does not contain any special characters
if [[ "$filename" =~ [\'\"\ \$\`\(\)\;] ]]; then
dokku_log_warn "Skipping include file with unsafe filename: $filename"
continue
fi
DATA="$(base64 -w 0 <"$include_dir/$filename")"
output="$output '--label=openresty.include-location-http-$filename=$DATA'"
done

View File

@@ -253,6 +253,24 @@ teardown() {
assert_output_contains "charset UTF-8;"
}
@test "(openresty) [security] eval injection via malicious include filename" {
rm -f /tmp/openresty-include
run /bin/bash -c "dokku proxy:set $TEST_APP openresty"
echo "output: $output"
echo "status: $status"
assert_success
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_openresty_include_unsafe
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "unsafe filename" -1
# No injection payload to test since we're using a simple space character
# The test should have failed during core-post-extract, not during eval
}
@test "(openresty) label management" {
run /bin/bash -c "dokku proxy:set $TEST_APP openresty"
echo "output: $output"
@@ -335,3 +353,16 @@ add_openresty_include() {
touch "$APP_REPO_DIR/openresty/http-location-includes/example.conf"
echo "# location-block" >>"$APP_REPO_DIR/openresty/http-location-includes/example.conf"
}
add_openresty_include_unsafe() {
local APP="$1"
local APP_REPO_DIR="$2"
[[ -z "$APP" ]] && local APP="$TEST_APP"
mkdir -p "$APP_REPO_DIR/openresty/http-includes"
# Create a filename with a space - simpler test that should be rejected by [^a-zA-Z0-9_.-]
printf 'charset UTF-8;\n' >"$APP_REPO_DIR/openresty/http-includes/unsafe filename.conf"
mkdir -p "$APP_REPO_DIR/openresty/http-location-includes"
printf '# location\n' >"$APP_REPO_DIR/openresty/http-location-includes/unsafe filename.conf"
}