From a5272e08e5f2ecf0937ad9eaaab539cafac10377 Mon Sep 17 00:00:00 2001 From: junkerderprovinz Date: Tue, 1 Sep 2026 03:24:50 +0200 Subject: [PATCH] fix(nginx): stop the files alias resolving above the download root The location lacked a trailing slash while its alias had one, so nginx appended the remainder of the URI to the alias verbatim. A request for /files../x therefore resolved to /../x, one level above the download root, and served whatever nginx could read there. Adding the slash to the location makes nginx strip the matched prefix instead of concatenating, which closes it. An exact-match redirect keeps /files without a slash working, so a direct visit still lands on the listing. The dashboard always requests api/files/ with the slash, so the redirect only covers typed URLs and old bookmarks. The two files have to land together. init-nginx deletes this block when downloads are off or HARDEN_DESKTOP is set, and its old range keyed on the literal "files {", which the new opening line no longer is. Left alone, a hardened container would have kept serving the download root. The deletion now runs before the SUBFOLDER substitution and addresses the template token directly, one range per block, so it cannot be steered by a user supplied subfolder: SUBFOLDER=/files/ is legal, and a looser pattern would have deleted the dashboard root with it. Verified with a plain nginx against the rendered config, on both server blocks and with PASSWORD set. Upstream answers the traversal 200 with the file contents; patched it answers 404; the download and the redirect are unchanged. With hardening the endpoint is gone entirely, 404 for both, matching upstream behaviour in that mode. Rendering was also checked for SUBFOLDER of /, /desktop/ and /files/: the surviving blocks match upstream exactly in each case. Scope worth stating plainly: with PASSWORD set this was never reachable unauthenticated. auth_basic sits at server level and is inherited by this location, so the traversal answered 401 without credentials, which I measured too. It exposed files only on instances running without a password, where the desktop is already open. Defence in depth rather than an urgent hole. --- root/defaults/default.conf | 10 ++++++++-- root/etc/s6-overlay/s6-rc.d/init-nginx/run | 10 +++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/root/defaults/default.conf b/root/defaults/default.conf index eaafd6b..d010bc0 100644 --- a/root/defaults/default.conf +++ b/root/defaults/default.conf @@ -38,7 +38,10 @@ server { client_max_body_size 10M; proxy_pass http://127.0.0.1:CWS; } - location SUBFOLDERfiles { + location = SUBFOLDERfiles { + return 301 SUBFOLDERfiles/; + } + location SUBFOLDERfiles/ { fancyindex on; fancyindex_footer SUBFOLDERnginx/footer.html; fancyindex_header SUBFOLDERnginx/header.html; @@ -111,7 +114,10 @@ server { client_max_body_size 10M; proxy_pass http://127.0.0.1:CWS; } - location SUBFOLDERfiles { + location = SUBFOLDERfiles { + return 301 SUBFOLDERfiles/; + } + location SUBFOLDERfiles/ { fancyindex on; fancyindex_footer SUBFOLDERnginx/footer.html; fancyindex_header SUBFOLDERnginx/header.html; diff --git a/root/etc/s6-overlay/s6-rc.d/init-nginx/run b/root/etc/s6-overlay/s6-rc.d/init-nginx/run index 161c802..158ad9b 100755 --- a/root/etc/s6-overlay/s6-rc.d/init-nginx/run +++ b/root/etc/s6-overlay/s6-rc.d/init-nginx/run @@ -31,12 +31,16 @@ cp /defaults/default.conf ${NGINX_CONFIG} sed -i "s/3000/$CPORT/g" ${NGINX_CONFIG} sed -i "s/3001/$CHPORT/g" ${NGINX_CONFIG} sed -i "s/CWS/$CWS/g" ${NGINX_CONFIG} +# Drop the download endpoint BEFORE SUBFOLDER is substituted, so the addresses match +# the literal template token instead of user supplied text. A subfolder of "/files/" +# is legal and would otherwise make a looser pattern delete the dashboard root. +if [[ ${SELKIES_FILE_TRANSFERS,,} != *"download"* ]] || [[ ${HARDEN_DESKTOP,,} == "true" ]]; then + sed -i -e '/location = SUBFOLDERfiles {/,/^ }/d' \ + -e '/location SUBFOLDERfiles\/ {/,/^ }/d' ${NGINX_CONFIG} +fi sed -i "s|SUBFOLDER|$SFOLDER|g" ${NGINX_CONFIG} sed -i "s|REPLACE_DOWNLOADS_PATH|$FILE_MANAGER_PATH|g" ${NGINX_CONFIG} s6-setuidgid abc mkdir -p ${FILE_MANAGER_PATH} -if [[ ${SELKIES_FILE_TRANSFERS,,} != *"download"* ]] || [[ ${HARDEN_DESKTOP,,} == "true" ]]; then - sed -i '/files {/,/^ }/d' ${NGINX_CONFIG} -fi if [ -n "${DISABLE_IPV6}" ]; then sed -i '/listen \[::\]/d' ${NGINX_CONFIG} fi