#!/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, so # unset or empty, it trusts no one. nginx would look up a hostname at # start and trust whatever address it found, so a value with a # character no address has stops the container here. An entry such as # 999.1.1.1 gets past this, and nginx refuses it at start as a # hostname it cannot find. TRUSTED_PROXIES="${TRUSTED_PROXIES:-}" case "$TRUSTED_PROXIES" in *[!0-9A-Fa-f.:/,\ ]*) echo "entrypoint: TRUSTED_PROXIES must be IP addresses or CIDRs" \ "separated by commas, not '$TRUSTED_PROXIES'" >&2 exit 1 ;; esac # nginx.conf includes this file; an empty one trusts no proxy. for proxy in $(echo "$TRUSTED_PROXIES" | tr ',' ' '); do echo "set_real_ip_from $proxy;" done > /etc/nginx/trusted-proxies.conf # 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; 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 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