mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-28 15:29:09 +01:00
[infra] Unify self-hosted deployment paths in getting-started.sh
Make getting-started.sh the single entry point for all self-hosted deployments. The wizard now asks two independent questions - identity provider (built-in vs standalone OIDC) and architecture (combined netbird-server vs separate management/signal/relay containers) - and renders the matching Docker Compose deployment with full reverse-proxy parity (built-in Traefik, external Traefik, Nginx, NPM, Caddy, manual). Highlights: - setup.env contract: every wizard answer is persisted; --non-interactive re-renders idempotently from the file (IaC), --render-only generates without starting services. Secrets are generated once and appended so re-renders never rotate them. - Split architecture renders a modern management.json (embedded Dex or external OIDC via PKCE), drops coturn entirely (the relay container serves STUN via NB_ENABLE_STUN), and optionally adds a PostgreSQL container when the postgres engine is selected without a DSN. - The standalone-IdP path is framed around its real differentiator: multi-account support. The built-in IdP supports external SSO connectors but enforces single account mode. - configure.sh, getting-started-with-dex.sh and getting-started-with-zitadel.sh print deprecation banners; their templates are frozen pending removal. - New tests/test-render.sh validates all 8 combos (JSON validity, compose config, idempotent re-render, combined+external rejection) and runs in CI as the test-render-matrix job.
This commit is contained in:
Executable
+196
@@ -0,0 +1,196 @@
|
||||
#!/bin/bash
|
||||
# Render-validation tests for getting-started.sh.
|
||||
# Runs the script in --non-interactive --render-only mode for every supported
|
||||
# architecture / IdP / reverse-proxy combination and validates the generated
|
||||
# files. Requires jq; uses `docker compose config` when docker is available.
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
GETTING_STARTED="$SCRIPT_DIR/../getting-started.sh"
|
||||
WORK_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||
|
||||
FAILURES=0
|
||||
|
||||
compose_validate() {
|
||||
if docker compose version &>/dev/null; then
|
||||
docker compose -f "$1" config -q
|
||||
return $?
|
||||
fi
|
||||
if command -v docker-compose &>/dev/null; then
|
||||
docker-compose -f "$1" config -q
|
||||
return $?
|
||||
fi
|
||||
# Without docker fall back to a structural sanity check
|
||||
grep -q "^services:" "$1"
|
||||
return $?
|
||||
}
|
||||
|
||||
write_oidc_fixture() {
|
||||
# A file:// OpenID discovery document stands in for a real IdP
|
||||
cat > "$1" <<EOF
|
||||
{
|
||||
"issuer": "https://idp.example.org/realms/netbird",
|
||||
"authorization_endpoint": "https://idp.example.org/realms/netbird/protocol/openid-connect/auth",
|
||||
"token_endpoint": "https://idp.example.org/realms/netbird/protocol/openid-connect/token",
|
||||
"jwks_uri": "https://idp.example.org/realms/netbird/protocol/openid-connect/certs",
|
||||
"device_authorization_endpoint": "https://idp.example.org/realms/netbird/protocol/openid-connect/auth/device"
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
run_case() {
|
||||
local name="$1"
|
||||
shift
|
||||
local expected_files=("$@")
|
||||
local case_dir="$WORK_DIR/$name"
|
||||
mkdir -p "$case_dir"
|
||||
|
||||
# setup.env content is provided on stdin
|
||||
cat > "$case_dir/setup.env"
|
||||
|
||||
echo "--- case: $name"
|
||||
if ! (cd "$case_dir" && bash "$GETTING_STARTED" --non-interactive --render-only > render.log 2>&1); then
|
||||
echo "FAIL($name): render exited non-zero"
|
||||
tail -5 "$case_dir/render.log" | sed 's/^/ /'
|
||||
FAILURES=$((FAILURES + 1))
|
||||
return 0
|
||||
fi
|
||||
|
||||
for f in "${expected_files[@]}"; do
|
||||
if [[ ! -f "$case_dir/$f" ]]; then
|
||||
echo "FAIL($name): expected file $f was not generated"
|
||||
FAILURES=$((FAILURES + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -f "$case_dir/management.json" ]] && ! jq . "$case_dir/management.json" > /dev/null; then
|
||||
echo "FAIL($name): management.json is not valid JSON"
|
||||
FAILURES=$((FAILURES + 1))
|
||||
fi
|
||||
|
||||
if [[ -f "$case_dir/docker-compose.yml" ]] && ! compose_validate "$case_dir/docker-compose.yml"; then
|
||||
echo "FAIL($name): docker-compose.yml failed validation"
|
||||
FAILURES=$((FAILURES + 1))
|
||||
fi
|
||||
|
||||
# Re-rendering from the persisted setup.env must be byte-identical (idempotent)
|
||||
local checksums_before checksums_after
|
||||
checksums_before=$(cd "$case_dir" && sha256sum docker-compose.yml dashboard.env config.yaml management.json 2>/dev/null || true)
|
||||
if ! (cd "$case_dir" && bash "$GETTING_STARTED" --non-interactive --render-only > render2.log 2>&1); then
|
||||
echo "FAIL($name): re-render exited non-zero"
|
||||
FAILURES=$((FAILURES + 1))
|
||||
return 0
|
||||
fi
|
||||
checksums_after=$(cd "$case_dir" && sha256sum docker-compose.yml dashboard.env config.yaml management.json 2>/dev/null || true)
|
||||
if [[ "$checksums_before" != "$checksums_after" ]]; then
|
||||
echo "FAIL($name): re-render from setup.env is not idempotent"
|
||||
FAILURES=$((FAILURES + 1))
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
OIDC_FIXTURE="$WORK_DIR/openid-configuration-fixture.json"
|
||||
write_oidc_fixture "$OIDC_FIXTURE"
|
||||
|
||||
run_case combined-embedded-traefik docker-compose.yml config.yaml dashboard.env <<EOF
|
||||
NETBIRD_DOMAIN="netbird.example.org"
|
||||
NETBIRD_ARCHITECTURE="combined"
|
||||
NETBIRD_IDP_MODE="embedded"
|
||||
NETBIRD_REVERSE_PROXY_TYPE="0"
|
||||
NETBIRD_TRAEFIK_ACME_EMAIL="admin@example.org"
|
||||
EOF
|
||||
|
||||
run_case combined-embedded-nginx docker-compose.yml config.yaml dashboard.env nginx-netbird.conf <<EOF
|
||||
NETBIRD_DOMAIN="netbird.example.org"
|
||||
NETBIRD_ARCHITECTURE="combined"
|
||||
NETBIRD_IDP_MODE="embedded"
|
||||
NETBIRD_REVERSE_PROXY_TYPE="2"
|
||||
EOF
|
||||
|
||||
run_case split-embedded-traefik docker-compose.yml management.json dashboard.env <<EOF
|
||||
NETBIRD_DOMAIN="netbird.example.org"
|
||||
NETBIRD_ARCHITECTURE="split"
|
||||
NETBIRD_IDP_MODE="embedded"
|
||||
NETBIRD_REVERSE_PROXY_TYPE="0"
|
||||
NETBIRD_TRAEFIK_ACME_EMAIL="admin@example.org"
|
||||
EOF
|
||||
|
||||
run_case split-embedded-external-traefik docker-compose.yml management.json dashboard.env <<EOF
|
||||
NETBIRD_DOMAIN="netbird.example.org"
|
||||
NETBIRD_ARCHITECTURE="split"
|
||||
NETBIRD_IDP_MODE="embedded"
|
||||
NETBIRD_REVERSE_PROXY_TYPE="1"
|
||||
NETBIRD_TRAEFIK_ENTRYPOINT="websecure"
|
||||
NETBIRD_TRAEFIK_CERTRESOLVER="letsencrypt"
|
||||
EOF
|
||||
|
||||
run_case split-external-nginx-postgres docker-compose.yml management.json dashboard.env nginx-netbird.conf <<EOF
|
||||
NETBIRD_DOMAIN="netbird.example.org"
|
||||
NETBIRD_ARCHITECTURE="split"
|
||||
NETBIRD_IDP_MODE="external"
|
||||
NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="file://$OIDC_FIXTURE"
|
||||
NETBIRD_AUTH_CLIENT_ID="netbird"
|
||||
NETBIRD_AUTH_CLIENT_SECRET="some-secret"
|
||||
NETBIRD_REVERSE_PROXY_TYPE="2"
|
||||
NETBIRD_STORE_CONFIG_ENGINE="postgres"
|
||||
EOF
|
||||
|
||||
run_case split-external-manual docker-compose.yml management.json dashboard.env <<EOF
|
||||
NETBIRD_DOMAIN="netbird.example.org"
|
||||
NETBIRD_ARCHITECTURE="split"
|
||||
NETBIRD_IDP_MODE="external"
|
||||
NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="file://$OIDC_FIXTURE"
|
||||
NETBIRD_AUTH_CLIENT_ID="netbird"
|
||||
NETBIRD_REVERSE_PROXY_TYPE="5"
|
||||
NETBIRD_BIND_LOCALHOST_ONLY="false"
|
||||
EOF
|
||||
|
||||
# Auto-forced split must be accepted when no architecture is given with external IdP
|
||||
run_case split-external-defaulted docker-compose.yml management.json dashboard.env <<EOF
|
||||
NETBIRD_DOMAIN="netbird.example.org"
|
||||
NETBIRD_IDP_MODE="external"
|
||||
NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="file://$OIDC_FIXTURE"
|
||||
NETBIRD_AUTH_CLIENT_ID="netbird"
|
||||
NETBIRD_REVERSE_PROXY_TYPE="5"
|
||||
EOF
|
||||
|
||||
# Invalid combination must fail: combined + external IdP
|
||||
echo "--- case: combined-external-rejected"
|
||||
REJECT_DIR="$WORK_DIR/combined-external-rejected"
|
||||
mkdir -p "$REJECT_DIR"
|
||||
cat > "$REJECT_DIR/setup.env" <<EOF
|
||||
NETBIRD_DOMAIN="netbird.example.org"
|
||||
NETBIRD_ARCHITECTURE="combined"
|
||||
NETBIRD_IDP_MODE="external"
|
||||
NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="file://$OIDC_FIXTURE"
|
||||
NETBIRD_AUTH_CLIENT_ID="netbird"
|
||||
NETBIRD_REVERSE_PROXY_TYPE="5"
|
||||
EOF
|
||||
if (cd "$REJECT_DIR" && bash "$GETTING_STARTED" --non-interactive --render-only > render.log 2>&1); then
|
||||
echo "FAIL(combined-external-rejected): combined + external IdP was not rejected"
|
||||
FAILURES=$((FAILURES + 1))
|
||||
fi
|
||||
|
||||
# Spot-check rendered content
|
||||
SPLIT_EXT="$WORK_DIR/split-external-nginx-postgres"
|
||||
if [[ -f "$SPLIT_EXT/management.json" ]]; then
|
||||
[[ $(jq -r '.HttpConfig.AuthIssuer' "$SPLIT_EXT/management.json") == "https://idp.example.org/realms/netbird" ]] || { echo "FAIL: external AuthIssuer mismatch"; FAILURES=$((FAILURES + 1)); }
|
||||
[[ $(jq -r '.EmbeddedIdP' "$SPLIT_EXT/management.json") == "null" ]] || { echo "FAIL: external mode must not configure EmbeddedIdP"; FAILURES=$((FAILURES + 1)); }
|
||||
[[ $(jq -r '.StoreConfig.Engine' "$SPLIT_EXT/management.json") == "postgres" ]] || { echo "FAIL: postgres engine not set"; FAILURES=$((FAILURES + 1)); }
|
||||
grep -q "postgres:" "$SPLIT_EXT/docker-compose.yml" || { echo "FAIL: postgres container missing"; FAILURES=$((FAILURES + 1)); }
|
||||
fi
|
||||
|
||||
SPLIT_EMB="$WORK_DIR/split-embedded-traefik"
|
||||
if [[ -f "$SPLIT_EMB/management.json" ]]; then
|
||||
[[ $(jq -r '.EmbeddedIdP.Enabled' "$SPLIT_EMB/management.json") == "true" ]] || { echo "FAIL: embedded IdP not enabled in split mode"; FAILURES=$((FAILURES + 1)); }
|
||||
[[ $(jq -r '.Relay.Addresses[0]' "$SPLIT_EMB/management.json") == "rels://netbird.example.org:443" ]] || { echo "FAIL: relay address mismatch"; FAILURES=$((FAILURES + 1)); }
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [[ $FAILURES -gt 0 ]]; then
|
||||
echo "$FAILURES test(s) failed"
|
||||
exit 1
|
||||
fi
|
||||
echo "All render tests passed"
|
||||
Reference in New Issue
Block a user