check / check (push) Waiting to run
A hard link in /data shares its owner with a file that can be anywhere on the same filesystem, so chowning it gave that file to the netwatch user. A file with more than one name now keeps its owner and its path is logged. Owners are now set on the opened directory, file or link after checking it, as chmodDir sets modes, so a hard link swapped in afterwards is caught. Every other file is opened without following it; a symbolic link is given to the netwatch user itself, and devices, named pipes and sockets keep their owner. This needs Linux, the image's system; elsewhere it returns an error. A failed owner change names the file. Model: opus-5-5
135 lines
5.5 KiB
Bash
Executable File
135 lines
5.5 KiB
Bash
Executable File
#!/bin/sh
|
|
# The container's entrypoint: runs netwatch-server and nginx side by
|
|
# side. TERM or INT stops both, and the container exits 0 if both exit
|
|
# cleanly. If either exits on its own, the other is stopped too and the
|
|
# container exits non-zero, so the platform restarts it instead of
|
|
# leaving it half up.
|
|
#
|
|
# No set -e: kill and wait return non-zero here in normal operation.
|
|
set -u
|
|
|
|
# PORT is the public port nginx listens on, 8080 when unset or empty.
|
|
# nginx would take a value such as localhost or unix:/tmp/x.sock as an
|
|
# address and start anyway, and reports a bad port without naming
|
|
# PORT, so a value that is not a usable port stops the container here,
|
|
# before either process starts.
|
|
export PORT="${PORT:-8080}"
|
|
case "$PORT" in
|
|
*[!0-9]*)
|
|
echo "entrypoint: PORT must be a port number, not '$PORT'" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
# The length is checked first because, for a number too big for it,
|
|
# the shell's test prints an error and is false, so the range checks
|
|
# alone would let it through.
|
|
if [ "${#PORT}" -gt 5 ] || [ "$PORT" -lt 1 ] || [ "$PORT" -gt 65535 ]; then
|
|
echo "entrypoint: PORT must be from 1 to 65535, not '$PORT'" >&2
|
|
exit 1
|
|
fi
|
|
if [ "$PORT" -eq 8081 ]; then
|
|
echo "entrypoint: PORT cannot be 8081, netwatch-server listens there" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# TRUSTED_PROXIES names the reverse proxies in front of the container,
|
|
# as IP addresses or CIDRs separated by commas. nginx takes the client
|
|
# address from X-Forwarded-For only on a request from one of them.
|
|
# Unset, they are the three private ranges of RFC 1918; set and empty,
|
|
# nginx trusts no one, which is why the default below follows - and not
|
|
# :-. configs/nginx.conf includes the file written here, one
|
|
# set_real_ip_from line per entry.
|
|
#
|
|
# nginx looks up an entry it cannot read as an address as a hostname,
|
|
# and trusts what it finds (1.2.3 is found as 1.2.0.3). So each entry
|
|
# is made a CIDR, a lone address getting /128 if it is IPv6 and /32 if
|
|
# not, and netwatch-server checks it with the parsing it gives its own
|
|
# TRUSTED_PROXIES. Its error, naming the CIDR, is dropped for the one
|
|
# below, naming the entry as written. set -f keeps a * in an entry from
|
|
# becoming a list of file names.
|
|
TRUSTED_PROXIES="${TRUSTED_PROXIES-10.0.0.0/8,172.16.0.0/12,192.168.0.0/16}"
|
|
set -f
|
|
for proxy in $(printf '%s' "$TRUSTED_PROXIES" | tr ',' ' '); do
|
|
case "$proxy" in
|
|
*/*) cidr="$proxy" ;;
|
|
*:*) cidr="$proxy/128" ;;
|
|
*) cidr="$proxy/32" ;;
|
|
esac
|
|
if ! netwatch-server check-cidr "$cidr" 2> /dev/null; then
|
|
echo "entrypoint: TRUSTED_PROXIES must be IP addresses or CIDRs" \
|
|
"separated by commas; '$proxy' is neither" >&2
|
|
exit 1
|
|
fi
|
|
echo "set_real_ip_from $cidr;"
|
|
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. Here, as root, netwatch-server prepare-data-dir creates DATA_DIR
|
|
# and gives /data and the directories, regular files and symbolic links
|
|
# in it to the netwatch user, so the host directory needs no preparing.
|
|
# A file with a second name (hard link) keeps its owner, as that name
|
|
# could be outside /data, and its path is logged. It stops the start,
|
|
# naming DATA_DIR, unless DATA_DIR is /data or a path below it, and it
|
|
# acts on nothing outside /data, whatever symbolic or hard links it
|
|
# meets there.
|
|
export DATA_DIR="${DATA_DIR:-/data/reports}"
|
|
netwatch-server prepare-data-dir "$DATA_DIR" || exit 1
|
|
|
|
# A stop signal is only noted here; the loop below acts on it.
|
|
stop_requested=""
|
|
trap 'stop_requested=yes' TERM INT
|
|
|
|
# netwatch-server runs as the netwatch user and listens on loopback
|
|
# only, on a port other than the public one; configs/nginx.conf proxies
|
|
# to this address. Its only client is nginx, so it takes the client
|
|
# address nginx passes on from 127.0.0.1 alone, whatever TRUSTED_PROXIES
|
|
# the container has. The netwatch user has no login shell, hence -s
|
|
# /bin/sh. busybox su replaces itself with the command instead of
|
|
# staying on as its parent, so $! is the server's own PID.
|
|
BIND_ADDRESS=127.0.0.1 PORT=8081 TRUSTED_PROXIES=127.0.0.1/32 \
|
|
su -s /bin/sh netwatch -c 'exec netwatch-server' &
|
|
backend=$!
|
|
|
|
# nginx starts through the nginx image's own entrypoint, which applies
|
|
# the image's start-up configuration and then replaces itself with
|
|
# nginx. Part of that start-up configuration renders configs/nginx.conf
|
|
# into conf.d with nginx listening on PORT. NGINX_ENVSUBST_FILTER limits
|
|
# that rendering to PORT: a variable nginx itself uses, such as $uri,
|
|
# would otherwise be replaced by an environment variable of the same
|
|
# name.
|
|
NGINX_ENVSUBST_FILTER='^PORT$' \
|
|
/docker-entrypoint.sh nginx -g 'daemon off;' &
|
|
nginx=$!
|
|
|
|
running() {
|
|
kill -0 "$1" 2>/dev/null
|
|
}
|
|
|
|
# POSIX sh cannot wait for whichever of two children exits first, so
|
|
# look once a second. The shell collects a child that has exited while
|
|
# it runs sleep, and running() is false for that child from then on.
|
|
while [ -z "$stop_requested" ] && running "$backend" && running "$nginx"; do
|
|
sleep 1
|
|
done
|
|
|
|
# Stop both, then wait until neither is left.
|
|
kill -TERM "$backend" "$nginx" 2>/dev/null
|
|
while running "$backend" || running "$nginx"; do
|
|
sleep 1
|
|
done
|
|
|
|
wait "$backend"
|
|
backend_status=$?
|
|
wait "$nginx"
|
|
nginx_status=$?
|
|
echo "entrypoint: netwatch-server exited $backend_status," \
|
|
"nginx exited $nginx_status"
|
|
|
|
# Success is a requested stop that both processes exited cleanly from.
|
|
if [ -n "$stop_requested" ] && [ "$backend_status" -eq 0 ] &&
|
|
[ "$nginx_status" -eq 0 ]; then
|
|
exit 0
|
|
fi
|
|
exit 1
|