fix(safe-paths): unlink symlinks and create files as the customer, never as root
drop_symlink and the logs/ symlink sweep now unlink AS THE CUSTOMER, and user_touch no longer falls back to a root chown -h (a parent dir swapped for a symlink between check and use could still redirect a root path operation). A root-owned leftover error_log is fixed by the existing chown -R, which never follows symlinks. Every operation these helpers perform is now safe even if the customer races it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0ffccac4b8
commit
94a73ea8ab
1 file changed
+15
-14
+15
-14
@@ -27,19 +27,20 @@ as_user() {
|
||||
fi
|
||||
}
|
||||
|
||||
# drop_symlink PATH : if PATH is a symlink, unlink it (rm on a symlink never
|
||||
# touches the target) and log it.
|
||||
# drop_symlink PATH : if PATH is a symlink, unlink it and log it. The unlink
|
||||
# runs AS THE CUSTOMER: if a parent dir is swapped for a symlink between the
|
||||
# check and the rm, the customer can only unlink what they could already.
|
||||
drop_symlink() {
|
||||
if [ -L "$1" ]; then
|
||||
echo "entrypoint: SECURITY: $1 was a symlink to '$(readlink "$1")' - removed" >&2
|
||||
rm -f -- "$1"
|
||||
echo "entrypoint: SECURITY: $1 was a symlink to '$(readlink "$1")' - removing" >&2
|
||||
as_user rm -f -- "$1" || echo "entrypoint: WARNING: could not remove symlink $1" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# secure_home : run once, right after adduser, before anything else touches
|
||||
# /home/$user.
|
||||
secure_home() {
|
||||
local h="/home/$user" d
|
||||
local h="/home/$user"
|
||||
if [ -L "$h" ] || [ ! -d "$h" ]; then
|
||||
echo "entrypoint: FATAL: $h is not a real directory (symlink or missing) - refusing to start" >&2
|
||||
exit 1
|
||||
@@ -49,11 +50,10 @@ secure_home() {
|
||||
drop_symlink "$h/logs"
|
||||
# public_html may legitimately be a symlink (release-dir deploys); it is left
|
||||
# alone and every root-side operation on it runs as the customer instead.
|
||||
if [ -d "$h/logs" ]; then
|
||||
# find without -L never follows symlinks (start path verified real above).
|
||||
find "$h/logs" -xdev -type l -print0 2>/dev/null | while IFS= read -r -d '' d; do
|
||||
drop_symlink "$d"
|
||||
done
|
||||
if [ -d "$h/logs" ] && [ ! -L "$h/logs" ]; then
|
||||
# As the customer, find without -L never follows symlinks; -delete unlinks.
|
||||
as_user find "$h/logs" -xdev -type l \
|
||||
-printf 'entrypoint: SECURITY: %p was a symlink to %l - removed\n' -delete >&2 || true
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -62,12 +62,13 @@ user_mkdir() {
|
||||
as_user mkdir -p -- "$@"
|
||||
}
|
||||
|
||||
# user_touch FILE : create FILE as the customer. If it already exists owned by
|
||||
# someone else (root-created by an older image), fix ownership with chown -h,
|
||||
# which never follows a symlink; a symlink there is dropped first.
|
||||
# user_touch FILE : create FILE as the customer (a symlink there is dropped
|
||||
# first). Root never touches/chowns it: if it already exists root-owned (left by
|
||||
# an older image) the touch fails quietly and the entrypoint's chown -R, which
|
||||
# never follows symlinks, fixes the owner.
|
||||
user_touch() {
|
||||
drop_symlink "$1"
|
||||
as_user touch -- "$1" 2>/dev/null || chown -h "$user:$user" -- "$1" 2>/dev/null || true
|
||||
as_user touch -- "$1" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# load_user_crontab : install /home/$user/crontab, read AS THE CUSTOMER. Root
|
||||
|
||||
Reference in new issue
Block a user