Files
romm/docker/init_scripts/init
Michael Manganiello 27ba5a1159 fix: Set Gunicorn option forwarded-allow-ips
Currently, the `request.url_for` and `URLPath.make_absolute_url` methods
always build URLs with "http" scheme, even when the original requested
URL is using "https".

The reason for this is that Gunicorn does not allow IPs other than
127.0.0.1 to set secure headers by default. As regular RomM
installations don't know which frontend IPs will try to set security
headers in advance, we can disable this validation, and fix URL
building.

A simple way to test this change is to access any of the `feed` endpoints,
which generate URLs using the mentioned methods. Accessing the endpoint
using "https" scheme must generate "https" URLs.

Reference:
* https://github.com/encode/starlette/issues/538#issuecomment-2054013679
* https://docs.gunicorn.org/en/stable/settings.html#forwarded-allow-ips
2024-08-09 01:20:17 -03:00

169 lines
4.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -o errexit # treat errors as fatal
set -o nounset # treat unset variables as an error
set -o pipefail # treat errors in pipes as fatal
shopt -s inherit_errexit # inherit errexit
# make it possible to disable the inotify watcher process
ENABLE_RESCAN_ON_FILESYSTEM_CHANGE="${ENABLE_RESCAN_ON_FILESYSTEM_CHANGE:="true"}"
# Set INIT_DEBUG to false if not set by docker env
# (this env var is currently undocumented and usually just needed for development purposes)
INIT_DEBUG="${INIT_DEBUG:="false"}"
# print debug log output if enabled
debug_log() {
if [[ ${INIT_DEBUG} == "true" ]]; then
echo "DEBUG: [init][$(date +"%Y-%m-%d %T")]" "${@}" || true
fi
}
# print debug log output if enabled
info_log() {
echo "INFO: [init][$(date +"%Y-%m-%d %T")]" "${@}" || true
}
# print error log output if enabled
error_log() {
echo "ERROR: [init][$(date +"%Y-%m-%d %T")]" "${@}" || true
exit 1
}
wait_for_gunicorn_socket() {
info_log "waiting for gunicorn socket file..."
while [[ ! -S /tmp/gunicorn.sock ]]; do
sleep 1
done
info_log "gunicorn socket file found"
}
# function that runs or main process and creates a corresponding PID file,
start_bin_gunicorn() {
# cleanup potentially leftover socket
rm /tmp/gunicorn.sock -f
# commands to start our main application and store its PID to check for crashes
info_log "starting gunicorn"
gunicorn \
--access-logfile - \
--error-logfile - \
--worker-class uvicorn.workers.UvicornWorker \
--bind=0.0.0.0:5000 \
--bind=unix:/tmp/gunicorn.sock \
--pid=/tmp/gunicorn.pid \
--forwarded-allow-ips="*" \
--workers "${GUNICORN_WORKERS:=2}" \
main:app &
}
# Commands to start nginx (handling PID creation internally)
start_bin_nginx() {
wait_for_gunicorn_socket
info_log "starting nginx"
if [[ ${EUID} -ne 0 ]]; then
nginx
else
# if container runs as root, drop permissions
nginx -g 'user romm;'
fi
}
start_bin_redis-server() {
info_log "starting redis-server"
# Check if /usr/local/etc/redis/redis.conf exists and use it if so
if [[ -f /usr/local/etc/redis/redis.conf ]]; then
redis-server /usr/local/etc/redis/redis.conf &
else
redis-server --dir /redis-data &
fi
REDIS_PID=$!
echo "${REDIS_PID}" >/tmp/redis-server.pid
}
# function that runs our independent python scripts and creates corresponding PID files,
start_python() {
SCRIPT="${1}"
info_log "starting ${SCRIPT}.py"
python3 "${SCRIPT}.py" &
WATCHER_PID=$!
echo "${WATCHER_PID}" >"/tmp/${SCRIPT}.pid"
}
watchdog_process_pid() {
TYPE=$1
PROCESS=$2
if [[ -f "/tmp/${PROCESS}.pid" ]]; then
# check if the pid we last wrote to our state file is actually active
PID=$(cat "/tmp/${PROCESS}.pid") || true
if [[ -d "/proc/${PID}" ]]; then
debug_log "${PROCESS} still running, no need to start"
else
if [[ ${TYPE} == "bin" ]]; then
start_bin_"${PROCESS}"
elif [[ ${TYPE} == "python" ]]; then
start_python "${PROCESS}"
fi
fi
else
if [[ ${TYPE} == "bin" ]]; then
start_bin_"${PROCESS}"
elif [[ ${TYPE} == "python" ]]; then
start_python "${PROCESS}"
fi
fi
}
# switch to backend directory
cd /backend || { error_log "/backend directory doesn't seem to exist"; }
info_log "Starting up, please wait..."
# clear any leftover PID files
rm /tmp/*.pid -f
# function definition done, lets start our main loop
while true; do
# check for died processes every 5 seconds
sleep 5
# Start redis server if we dont have a corresponding PID file
# and REDIS_HOST is not set (which would mean we're using an external redis)
if [[ -z ${REDIS_HOST:=""} ]]; then
watchdog_process_pid bin redis-server
fi
# Run needed database migrations on startup,
# but only if it was not successful since the last full docker container start
if [[ ${ALEMBIC_SUCCESS:="false"} == "false" ]]; then
if alembic upgrade head; then
debug_log "database schema migrations suceeded"
ALEMBIC_SUCCESS="true"
else
error_log "Something went horribly wrong with our database"
fi
else
debug_log "database schema already upgraded during current container lifecycle"
fi
# Start gunicorn if we dont have a corresponding PID file
watchdog_process_pid bin gunicorn
# Start nginx if we dont have a corresponding PID file
watchdog_process_pid bin nginx
# only start the watcher.py if we actually want to use the rescan on fs change feature
if [[ ${ENABLE_RESCAN_ON_FILESYSTEM_CHANGE} == "true" ]]; then
# Start watcher if we dont have a corresponding PID file
watchdog_process_pid python watcher
fi
# Start background worker processes
debug_log "Starting worker and scheduler"
# Start worker if we dont have a corresponding PID file
watchdog_process_pid python worker
# Start scheduler if we dont have a corresponding PID file
watchdog_process_pid python scheduler
done