feat: add NetBox Cloudron package (Infrastructure) [#648]

Official-image wrapper of netboxcommunity/netbox v4.6.10
(netbox-docker 5.0.2, Granian), digest-pinned. OIDC via
python-social-auth wired to the platform provider; RQ worker
backgrounded behind a web-port gate; addon waits cover the 30s
upstream DB timeout. Full-stack verified: migrations, Granian,
worker, login page + SSO button. 16/~57.

Detail + verification: https://projects.knownelement.com/issues/648#note-4976
This commit is contained in:
2026-09-06 06:26:54 -05:00
parent ee5344f5a5
commit cead1523ad
11 changed files with 392 additions and 14 deletions
+106
View File
@@ -0,0 +1,106 @@
#!/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