main's tree, with next merged on 26 October, as one commit onto prod, on sneak's order. Model: opus-5-5 Co-authored-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #198.
This commit is contained in:
+110
-90
@@ -13,30 +13,35 @@ set -u
|
||||
# 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
|
||||
check_port() {
|
||||
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
|
||||
;;
|
||||
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
|
||||
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, so
|
||||
# unset or empty, it trusts no one. nginx.conf includes the file written
|
||||
# here, one set_real_ip_from line per entry.
|
||||
# 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
|
||||
@@ -45,70 +50,64 @@ fi
|
||||
# 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:-}"
|
||||
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
|
||||
write_trusted_proxies() {
|
||||
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. 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.
|
||||
#
|
||||
# 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.
|
||||
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
|
||||
exit 1
|
||||
fi
|
||||
chown -R netwatch:netwatch /data "$DATA_DIR" || exit 1
|
||||
chmod 750 /data "$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
|
||||
# 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.
|
||||
prepare_data_dir() {
|
||||
export DATA_DIR="${DATA_DIR:-/data/reports}"
|
||||
netwatch-server prepare-data-dir "$DATA_DIR" || exit 1
|
||||
}
|
||||
|
||||
# netwatch-server runs as the netwatch user and listens on loopback
|
||||
# only, on a port other than the public one; 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
|
||||
# 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=$!
|
||||
start_backend() {
|
||||
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 nginx.conf into
|
||||
# conf.d with nginx listening on PORT. NGINX_ENVSUBST_FILTER limits
|
||||
# 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=$!
|
||||
start_nginx() {
|
||||
NGINX_ENVSUBST_FILTER='^PORT$' \
|
||||
/docker-entrypoint.sh nginx -g 'daemon off;' &
|
||||
nginx=$!
|
||||
}
|
||||
|
||||
running() {
|
||||
kill -0 "$1" 2>/dev/null
|
||||
@@ -117,26 +116,47 @@ running() {
|
||||
# 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
|
||||
wait_for_stop_or_exit() {
|
||||
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"
|
||||
stop_both() {
|
||||
kill -TERM "$backend" "$nginx" 2>/dev/null
|
||||
while running "$backend" || running "$nginx"; do
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
# 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
|
||||
exit_with_status() {
|
||||
wait "$backend"
|
||||
backend_status=$?
|
||||
wait "$nginx"
|
||||
nginx_status=$?
|
||||
echo "entrypoint: netwatch-server exited $backend_status," \
|
||||
"nginx exited $nginx_status"
|
||||
if [ -n "$stop_requested" ] && [ "$backend_status" -eq 0 ] &&
|
||||
[ "$nginx_status" -eq 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
main() {
|
||||
check_port
|
||||
write_trusted_proxies
|
||||
prepare_data_dir
|
||||
# A stop signal is only noted here; wait_for_stop_or_exit acts on it.
|
||||
stop_requested=""
|
||||
trap 'stop_requested=yes' TERM INT
|
||||
start_backend
|
||||
start_nginx
|
||||
wait_for_stop_or_exit
|
||||
stop_both
|
||||
exit_with_status
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user