#!/bin/bash set -euo pipefail # NetBox runtime setup for Cloudron: # 1. create the persistent data dirs (media / reports / scripts — the # image paths are build-time symlinks into /app/data) # 2. persist the Django SECRET_KEY (>= 50 chars; rotating it invalidates # sessions and encrypted values) # 3. map the Cloudron addon + platform env onto the image's env-driven # configuration (netbox-docker configuration/configuration.py): # DB_* from postgresql, REDIS[_CACHE]_* from redis, OIDC from the # platform OIDC provider # 4. background the RQ worker (gated on the web port so it only starts # after the entrypoint's migrations), then exec the upstream # entrypoint chain, which waits for the DB, migrates, and hands over # to Granian on :8080 # # Authentication: SSO via the Cloudron platform OIDC provider # (REMOTE_AUTH_BACKEND = OpenIdConnectAuth + SOCIAL_AUTH_OIDC_*). The # local Django login stays available on /login/ for admin bootstrap; # SSO-registered users are created WITHOUT privileges (see README). DATA_DIR="/app/data" SECRET_FILE="${DATA_DIR}/.secret_key" # --- 1. persistent data directories ------------------------------------------- mkdir -p "${DATA_DIR}/media" "${DATA_DIR}/reports" "${DATA_DIR}/scripts" # --- 2. persistent SECRET_KEY -------------------------------------------------- # hex on purpose: 64 chars, comfortably over NetBox's 50-char minimum if [[ ! -s "${SECRET_FILE}" ]]; then ( umask 077; head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > "${SECRET_FILE}" ) echo "Generated new NetBox secret key" fi export SECRET_KEY="$(cat "${SECRET_FILE}")" # --- 3. Cloudron -> netbox-docker environment ----------------------------------- export DB_HOST="${CLOUDRON_POSTGRESQL_HOST:-127.0.0.1}" export DB_PORT="${CLOUDRON_POSTGRESQL_PORT:-5432}" export DB_NAME="${CLOUDRON_POSTGRESQL_DATABASE:-netbox}" export DB_USER="${CLOUDRON_POSTGRESQL_USERNAME:-netbox}" export DB_PASSWORD="${CLOUDRON_POSTGRESQL_PASSWORD:-}" # one Cloudron redis instance, two logical databases (upstream convention: # tasks on 0, caching on 1) export REDIS_HOST="${CLOUDRON_REDIS_HOST:-127.0.0.1}" export REDIS_PORT="${CLOUDRON_REDIS_PORT:-6379}" export REDIS_PASSWORD="${CLOUDRON_REDIS_PASSWORD:-}" export REDIS_DATABASE=0 export REDIS_CACHE_HOST="${CLOUDRON_REDIS_HOST:-127.0.0.1}" export REDIS_CACHE_PORT="${CLOUDRON_REDIS_PORT:-6379}" export REDIS_CACHE_PASSWORD="${CLOUDRON_REDIS_PASSWORD:-}" export REDIS_CACHE_DATABASE=1 # hosts / CSRF: Cloudron terminates TLS at the platform proxy export ALLOWED_HOSTS="${CLOUDRON_APP_DOMAIN:-localhost} localhost" export CSRF_TRUSTED_ORIGINS="${CLOUDRON_APP_ORIGIN:-http://localhost}" # first admin is created by the operator via the Cloudron terminal # (README): the entrypoint's own superuser seeding is skipped export SKIP_SUPERUSER=true # operator knobs (see .env.example) export TIME_ZONE="${NETBOX_TIME_ZONE:-UTC}" export GRANIAN_WORKERS="${NETBOX_GRANIAN_WORKERS:-4}" export METRICS_ENABLED="${NETBOX_METRICS_ENABLED:-false}" # --- 3b. platform OIDC provider -> python-social-auth --------------------------- # social-core needs the issuer base URL WITH a trailing slash for # .well-known/openid-configuration discovery OIDC_ISSUER="${CLOUDRON_OIDC_ISSUER:-}" OIDC_ISSUER="${OIDC_ISSUER%/}/" export REMOTE_AUTH_BACKEND='social_core.backends.open_id_connect.OpenIdConnectAuth' export SOCIAL_AUTH_OIDC_OIDC_ENDPOINT="${OIDC_ISSUER}" export SOCIAL_AUTH_OIDC_KEY="${CLOUDRON_OIDC_CLIENT_ID:-}" export SOCIAL_AUTH_OIDC_SECRET="${CLOUDRON_OIDC_CLIENT_SECRET:-}" export SOCIAL_AUTH_OIDC_SCOPE='openid profile email' export LOGOUT_REDIRECT_URL="${CLOUDRON_APP_ORIGIN:-/}" # --- 4. RQ worker + upstream entrypoint ----------------------------------------- # wait for the addons FIRST: netbox-docker's entrypoint gives up on the DB # after only DB_WAIT_TIMEOUT=30s, which can lose the race with a cold # Cloudron postgres addon wait_tcp() { local host="$1" port="$2" name="$3" echo "Waiting for ${name} at ${host}:${port} ..." until (exec 3<>"/dev/tcp/${host}/${port}") 2>/dev/null; do echo "${name} is unavailable - sleeping" sleep 2 done echo "${name} is up" } wait_tcp "${DB_HOST}" "${DB_PORT}" "PostgreSQL" wait_tcp "${REDIS_HOST}" "${REDIS_PORT}" "Redis" # The worker must not run before the schema exists; Granian answering on # 8080 is the signal that docker-entrypoint.sh finished migrating. ( until (exec 3<>/dev/tcp/127.0.0.1/8080) 2>/dev/null; do sleep 2; done echo "web port is up - starting RQ worker" exec python3 /opt/netbox/netbox/manage.py rqworker ) & echo "Starting NetBox (migrations run automatically, then Granian binds :8080) ..." exec /opt/netbox/docker-entrypoint.sh /opt/netbox/launch-netbox.sh