fix(entrypoint): fail closed on planted symlinks before nginx starts
drop_symlink unlinks as root (never follows the target) and refuses to start if the path is still a symlink. Immediately before nginx (which opens its logs under /home/$user/logs as root) starts, logs/, logs/nginx and logs/nodejs must be real directories and nothing under logs/ may be other than a regular file or directory, else the entrypoint exits 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a84f05d41b
commit
d400d5eb39
1 file changed
+25
-9
+25
-9
@@ -31,26 +31,33 @@ as_user() {
|
|||||||
runuser -u "$user" -- "$@"
|
runuser -u "$user" -- "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
# Unlink AS THE CUSTOMER, so a parent dir swapped for a symlink between the
|
# Fail closed: refusing to start only hurts the customer who planted the path,
|
||||||
# check and the rm can only reach what the customer could already.
|
# which is preferred to root following it.
|
||||||
|
die() {
|
||||||
|
echo "entrypoint: FATAL: $* - refusing to start" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
# Unlink as root: rm on the final component never touches the link target and
|
||||||
|
# the customer cannot deny it. Still a symlink afterwards -> refuse to start.
|
||||||
drop_symlink() {
|
drop_symlink() {
|
||||||
if [ -L "$1" ]; then
|
if [ -L "$1" ]; then
|
||||||
echo "entrypoint: SECURITY: $1 was a symlink to '$(readlink "$1")' - removing" >&2
|
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
|
rm -f -- "$1"
|
||||||
|
[ -L "$1" ] && die "could not remove symlink $1"
|
||||||
fi
|
fi
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
if [ -L /home/$user ] || [ ! -d /home/$user ]; then
|
if [ -L /home/$user ] || [ ! -d /home/$user ]; then
|
||||||
echo "entrypoint: FATAL: /home/$user is not a real directory - refusing to start" >&2
|
die "/home/$user is not a real directory"
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
chown -h $user:$user /home/$user
|
chown -h $user:$user /home/$user
|
||||||
# app/ may legitimately be a symlink (releases/current deploys): it is left
|
# app/ may legitimately be a symlink (releases/current deploys): it is left
|
||||||
# alone, and every root-side operation on it below runs as the customer.
|
# alone, and every root-side operation on it below runs as the customer.
|
||||||
drop_symlink /home/$user/logs
|
drop_symlink /home/$user/logs
|
||||||
if [ -d /home/$user/logs ] && [ ! -L /home/$user/logs ]; then
|
if [ -d /home/$user/logs ]; then
|
||||||
# nginx opens its logs here as root. As the customer, find without -L never
|
# nginx opens its logs here as root. find without -L never follows symlinks;
|
||||||
# follows symlinks; -delete unlinks them.
|
# -delete unlinks the link itself. Re-checked fail-closed before nginx starts.
|
||||||
as_user find /home/$user/logs -xdev -type l \
|
find /home/$user/logs -xdev -type l \
|
||||||
-printf 'entrypoint: SECURITY: %p was a symlink to %l - removed\n' -delete >&2 || true
|
-printf 'entrypoint: SECURITY: %p was a symlink to %l - removed\n' -delete >&2 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -81,6 +88,15 @@ chown -R $user:$user /home/$user
|
|||||||
chmod 755 /home/$user
|
chmod 755 /home/$user
|
||||||
as_user find /home/$user -type d ! -perm -o=x -exec chmod o+x {} + 2>/dev/null || true
|
as_user find /home/$user -type d ! -perm -o=x -exec chmod o+x {} + 2>/dev/null || true
|
||||||
|
|
||||||
|
# nginx opens its logs under /home/$user/logs as root: last fail-closed check
|
||||||
|
# immediately before it starts. logs/, logs/nginx, logs/nodejs must be real
|
||||||
|
# dirs and nothing under logs/ may be other than a regular file or directory.
|
||||||
|
for d in /home/$user/logs /home/$user/logs/nginx /home/$user/logs/nodejs; do
|
||||||
|
[ -d "$d" ] && [ ! -L "$d" ] || die "$d is not a real directory"
|
||||||
|
done
|
||||||
|
bad=$(find /home/$user/logs -xdev ! -type f ! -type d -print -quit 2>&1)
|
||||||
|
[ -z "$bad" ] || die "unsafe entry under /home/$user/logs: $bad"
|
||||||
|
|
||||||
# Start nginx
|
# Start nginx
|
||||||
nginx
|
nginx
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user