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:
+106
@@ -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
|
||||
Reference in New Issue
Block a user