#!/usr/bin/env bash set -euo pipefail # ============================================================================= # NetBox Diode Project - Bootstrap Script # Generates secrets, writes configs, and prepares all services for startup. # ============================================================================= SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" NETBOX_DOCKER_DIR="/home/user/netbox-docker" HOST_IP="172.19.77.160" echo "==> Generating secrets..." gen_secret() { openssl rand -hex 32 } REDIS_PASSWORD="$(gen_secret)" DIODE_DB_PASSWORD="$(gen_secret)" HYDRA_DB_PASSWORD="$(gen_secret)" HYDRA_SYSTEM_SECRET="$(gen_secret)" # OAuth2 client secrets (used by Diode services and Orb Agent) INGESTER_CLIENT_SECRET="$(gen_secret)" RECONCILER_CLIENT_SECRET="$(gen_secret)" NETBOX_TO_DIODE_CLIENT_SECRET="$(gen_secret)" echo "==> Writing .env file..." cat > "${SCRIPT_DIR}/.env" < Writing OAuth2 client credentials..." cat > "${SCRIPT_DIR}/oauth2/client/client-credentials.json" < Writing OAuth2 bootstrap script..." cat > "${SCRIPT_DIR}/oauth2/client/bootstrap-clients.sh" <<'BOOTSTRAP_EOF' #!/usr/bin/env bash set -euo pipefail # Constants CREDENTIALS_FILE="/etc/config/oauth2/client/client-credentials.json" # Create the credentials file if it doesn't exist if [ ! -f "$CREDENTIALS_FILE" ]; then echo "ERROR: credentials file $CREDENTIALS_FILE not found" exit 1 fi # Wait for Hydra to be ready sleep 3 # Function to create client create_client() { local client_id=$1 local client_secret=$2 local scope=$3 local exists_in_hydra=false # Check if client exists in Hydra if hydra get oauth2-client $client_id --endpoint $HYDRA_ADMIN_URL >/dev/null 2>&1; then exists_in_hydra=true fi # Upsert behavior: remove stale client definition so scope/secret updates are applied. if [ "$exists_in_hydra" = true ]; then echo "INFO: client $client_id exists in Hydra, replacing to refresh scope/secret" hydra delete oauth2-client "$client_id" --endpoint "$HYDRA_ADMIN_URL" >/dev/null fi hydra create oauth2-client --endpoint "$HYDRA_ADMIN_URL" \ --id "$client_id" \ --secret "$client_secret" \ --grant-type "client_credentials" \ --response-type "token" \ --scope "$scope" \ --token-endpoint-auth-method "client_secret_post" \ --format json >/dev/null echo "INFO: client $client_id created/updated" } # Load client credentials jq -c '.[]' "$CREDENTIALS_FILE" | while read -r client; do client_id=$(echo "$client" | jq -r '.client_id') client_secret=$(echo "$client" | jq -r '.client_secret') scope=$(echo "$client" | jq -r '.scope') create_client "$client_id" "$client_secret" "$scope" done BOOTSTRAP_EOF chmod +x "${SCRIPT_DIR}/oauth2/client/bootstrap-clients.sh" echo "==> Writing Orb Agent config..." cat > "${SCRIPT_DIR}/orb-agent/agent.yaml" < Writing nginx.conf..." mkdir -p "${SCRIPT_DIR}/nginx" cat > "${SCRIPT_DIR}/nginx/nginx.conf" <<'NGINX_EOF' upstream diode-ingester { server diode-ingester:8081; } upstream diode-reconciler { server diode-reconciler:8081; } upstream diode-auth { server diode-auth:8080; } server { listen 8080; listen [::]:8080; http2 on; server_name localhost; client_max_body_size 25m; location /auth/introspect { internal; proxy_method POST; proxy_pass http://diode-auth/introspect; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URI $request_uri; } location /diode/auth { rewrite /diode/auth/(.*) /$1 break; proxy_pass http://diode-auth; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /diode/diode.v1.IngesterService { auth_request /auth/introspect; auth_request_set $auth_status $upstream_status; error_page 401 = @error401; error_page 403 = @error403; rewrite /diode/(.*) /$1 break; grpc_pass grpc://diode-ingester; grpc_set_header Host $host; grpc_set_header X-Real-IP $remote_addr; grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for; grpc_set_header X-Forwarded-Proto $scheme; } location /diode/diode.v1.ReconcilerService { auth_request /auth/introspect; auth_request_set $auth_status $upstream_status; error_page 401 = @error401; error_page 403 = @error403; rewrite /diode/(.*) /$1 break; grpc_pass grpc://diode-reconciler; grpc_set_header Host $host; grpc_set_header X-Real-IP $remote_addr; grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for; grpc_set_header X-Forwarded-Proto $scheme; } location /health { return 200 'OK'; add_header Content-Type text/plain; } location @error401 { return 401 '{"error":"unauthorized","error_description":"Authentication required"}'; } location @error403 { return 403 '{"error":"forbidden","error_description":"Access denied"}'; } } NGINX_EOF echo "==> Updating NetBox plugins.py..." PLUGINS_FILE="${NETBOX_DOCKER_DIR}/configuration/plugins.py" cat > "${PLUGINS_FILE}" < Creating NetBox Diode plugin Dockerfile..." cat > "${NETBOX_DOCKER_DIR}/Dockerfile.diode-plugin" <<'DOCKERFILE_EOF' FROM netboxcommunity/netbox:v4.5-4.0.1 # Install the Diode NetBox plugin RUN /usr/local/bin/uv pip install --python /opt/netbox/venv/bin/python netboxlabs-diode-netbox-plugin==1.7.0 DOCKERFILE_EOF echo "==> Updating NetBox docker-compose.override.yml..." cat > "${NETBOX_DOCKER_DIR}/docker-compose.override.yml" <<'OVERRIDE_EOF' services: netbox: &netbox-override build: context: . dockerfile: Dockerfile.diode-plugin ports: - "8000:8080" environment: SKIP_SUPERUSER: "false" SUPERUSER_API_TOKEN: "0123456789abcdef0123456789abcdef01234567" SUPERUSER_EMAIL: "admin@example.com" SUPERUSER_NAME: "admin" SUPERUSER_PASSWORD: "admin" ALLOWED_HOSTS: "*" extra_hosts: - "host.docker.internal:host-gateway" netbox-worker: <<: *netbox-override ports: [] OVERRIDE_EOF echo "" echo "============================================================" echo " Setup complete!" echo "============================================================" echo "" echo "Generated files:" echo " ${SCRIPT_DIR}/.env" echo " ${SCRIPT_DIR}/nginx/nginx.conf" echo " ${SCRIPT_DIR}/oauth2/client/client-credentials.json" echo " ${SCRIPT_DIR}/oauth2/client/bootstrap-clients.sh" echo " ${SCRIPT_DIR}/orb-agent/agent.yaml" echo " ${NETBOX_DOCKER_DIR}/Dockerfile.diode-plugin" echo " ${NETBOX_DOCKER_DIR}/docker-compose.override.yml" echo " ${NETBOX_DOCKER_DIR}/configuration/plugins.py" echo "" echo "Next steps:" echo " 1. cd ${NETBOX_DOCKER_DIR} && docker compose build --no-cache && docker compose up -d" echo " 2. docker compose exec netbox python /opt/netbox/netbox/manage.py migrate netbox_diode_plugin" echo " 3. cd ${SCRIPT_DIR} && docker compose up -d" echo ""