From 0dfa468a0c9c61f7fb5b8fb6be3c5774a9cf5616 Mon Sep 17 00:00:00 2001 From: blaipr Date: Thu, 3 Sep 2026 01:40:41 +0200 Subject: [PATCH] fix: create the runtime directories without world access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The entrypoint creates four runtime directories, chowns them to www-data, and chmods exactly one. Measured on the running container: config is 0750, var/backup, var/cache and var/temp are 0755. var/backup holds the backup archives — the database dump and config.xml — and the XML export, which carries every account's encrypted secret and key, and with no export password the name, login, URL and notes of every account in the clear. Those files are restricted to their owner, but only once fully written: the archive and export handlers chmod 0600 after the write, and compressDirectory() walks the whole application tree first. A world-traversable parent is what makes that window reachable. 0750 is also what the application itself would have chosen — DirectoryHandler::create() defaults to it, and never gets the chance because checkOrCreate() only creates a directory that is not already there. Verified on the container: at 0755 'nobody' can enter var/backup, at 0750 it cannot, and the login page still serves 200. --- docker/entrypoint.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index ecd14b2ac..a382518e4 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -23,10 +23,20 @@ if [ ! -f .env ]; then fi # sysPass needs these writable at runtime (config.xml, caches, proxies, backups). +# +# 0750, not the umask's 0755. var/backup holds the backup archives — the database dump and +# config.xml — and the XML export, which carries every account's encrypted secret and key, and +# with no export password its name, login, URL and notes in the clear. Those files are restricted +# to their owner, but only once they are fully written: the handlers chmod after the write, so a +# world-traversable directory leaves a window in which anybody else on the host can read them. +# var/temp holds the intermediates on the way there. +# +# This is the mode DirectoryHandler::create() would have used, and it never gets the chance — +# checkOrCreate() only creates a directory that is not already there, and these are. for dir in config var/cache var/temp var/backup; do mkdir -p "$dir" chown -R www-data:www-data "$dir" + chmod 750 "$dir" done -chmod 750 config exec "$@"