Merge pull request #8588 from dokku/sanitize-openresty-filenames
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user