Entrypoint checks DATA_DIR in full before acting on it as root (closes #80)
check / check (push) Failing after 12m54s

`bin/entrypoint.sh` now checks `DATA_DIR` before it creates anything or
changes an owner or mode: it must be `/data` or a path below it with no
`.`, `..` or empty part, and no part of it that exists, `/data`
included, may be a symbolic link. Anything else stops the start with one
message naming `DATA_DIR`. Only then does it create `DATA_DIR`, give
`/data` and everything in it to `netwatch` (`chown -R -h`, so a link in
it is not followed) and set mode 750 on `/data` and `DATA_DIR`. The
README section "Running under upaas" says which values are accepted.

Model: opus-5-5
This commit is contained in:
2026-09-29 10:56:53 +00:00
parent 7dfb3b8c32
commit 64587679a4
3 changed files with 45 additions and 17 deletions
+33 -15
View File
@@ -63,25 +63,43 @@ done > /etc/nginx/trusted-proxies.conf
# netwatch-server keeps its report files in DATA_DIR, on the /data
# volume, which may be a host directory owned by root or by another
# uid. Both are given to the netwatch user here, with the mode the
# server gives a directory it creates, so the host directory needs no
# preparing.
# uid. /data and everything in it are given to the netwatch user here,
# and /data and DATA_DIR get the mode the server gives a directory it
# creates, so the host directory needs no preparing.
#
# chown and chmod, run as root, change whatever a symbolic link on the
# path points to, anywhere in the container, and the netwatch user can
# put one in /data. So the start stops unless readlink -f, which
# follows every link on a path, gives /data and DATA_DIR back as they
# are. It also writes a path in full, so a DATA_DIR with '.', '..' or
# an extra '/' in it is refused too.
# This runs as root, so nothing is created or changed until DATA_DIR is
# known to be /data or a path below it, with no '.', '..' or empty
# part, and no part of it that exists, /data included, is a symbolic
# link: the netwatch user can put one in /data, and root would follow
# it anywhere in the container. Nothing else runs in the container yet,
# so no link can appear after the check.
export DATA_DIR="${DATA_DIR:-/data/reports}"
mkdir -p "$DATA_DIR" || exit 1
if [ "$(readlink -f /data)" != /data ] ||
[ "$(readlink -f "$DATA_DIR")" != "$DATA_DIR" ]; then
echo "entrypoint: DATA_DIR must be a full path with no '.', '..'," \
"extra '/' or symbolic link on it or on /data, not '$DATA_DIR'" >&2
data_dir_ok() {
# With a / added at the end, a last part of '.' or '..', and a / at
# the end, match these patterns too.
case "$DATA_DIR/" in
*/./* | */../* | *//*) return 1 ;;
/data/*) ;;
*) return 1 ;;
esac
# Each part from DATA_DIR up to /data. [ -L ] is false for a part
# that does not exist.
dir="$DATA_DIR"
while [ "$dir" != /data ]; do
[ -L "$dir" ] && return 1
dir="${dir%/*}"
done
[ ! -L /data ]
}
if ! data_dir_ok; then
echo "entrypoint: DATA_DIR must be /data or a path below it, with no" \
"'.', '..', extra '/' or symbolic link on it, not '$DATA_DIR'" >&2
exit 1
fi
chown -R netwatch:netwatch /data "$DATA_DIR" || exit 1
mkdir -p "$DATA_DIR" || exit 1
# -h: a symbolic link in /data is itself given to netwatch, not what it
# points to.
chown -R -h netwatch:netwatch /data || exit 1
chmod 750 /data "$DATA_DIR" || exit 1
# A stop signal is only noted here; the loop below acts on it.