diff --git a/.env b/.env new file mode 100644 index 0000000..a455455 --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +Username: admin@netbird.rozic-dev.com +Password: Sx49nmh6YxC5SeojHn+b4ApVcQE0WfL1c+TwJm7e/NE@ +NetBird_URL: https://netbird.rozic-dev.com +Zitadel_console: https://netbird.rozic-dev.com/ui/console diff --git a/Netbird-compose-backup.tar.gz b/Netbird-compose-backup.tar.gz new file mode 100644 index 0000000..93cf1ef Binary files /dev/null and b/Netbird-compose-backup.tar.gz differ diff --git a/dashboard.env b/dashboard.env new file mode 100644 index 0000000..65a299d --- /dev/null +++ b/dashboard.env @@ -0,0 +1,12 @@ +NETBIRD_MGMT_API_ENDPOINT=https://netbird.rozic-dev.com +NETBIRD_MGMT_GRPC_API_ENDPOINT=https://netbird.rozic-dev.com +AUTH_AUDIENCE=347529025415086083 +AUTH_CLIENT_ID=347529025415086083 +AUTH_AUTHORITY=https://netbird.rozic-dev.com +USE_AUTH0=false +AUTH_SUPPORTED_SCOPES=openid profile email offline_access +AUTH_REDIRECT_URI=/nb-auth +AUTH_SILENT_REDIRECT_URI=/nb-silent-auth +NGINX_SSL_PORT=443 +LETSENCRYPT_DOMAIN=none +NETBIRD_DOMAIN=netbird.rozic-dev.com diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8bbe236 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,290 @@ +services: + # UI dashboard + dashboard: + image: netbirdio/dashboard:latest + restart: unless-stopped + networks: + - netbird + - traefik_traefik + env_file: + - ./dashboard.env + labels: + - traefik.enable=true + - traefik.docker.network=traefik_default + - traefik.http.services.netbird-dashboard.loadbalancer.server.port=80 + - traefik.http.routers.netbird-dashboard.rule=Host(`netbird.rozic-dev.com`) + - traefik.http.routers.netbird-dashboard.entrypoints=websecure + - traefik.http.routers.netbird-dashboard.tls=true + - traefik.http.routers.netbird-dashboard.tls.certresolver=letsencrypt + - traefik.http.routers.netbird-dashboard.priority=50 + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + + # Signal + signal: + image: netbirdio/signal:latest + restart: unless-stopped + networks: + - netbird + - traefik_traefik + labels: + - traefik.enable=true + - traefik.docker.network=traefik_default + - traefik.http.services.netbird-signal.loadbalancer.server.port=10000 + - traefik.http.services.netbird-signal.loadbalancer.server.scheme=h2c + - traefik.http.routers.netbird-signal.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/signalexchange.SignalExchange/`) + - traefik.http.routers.netbird-signal.entrypoints=websecure + - traefik.http.routers.netbird-signal.service=netbird-signal + - traefik.http.routers.netbird-signal.tls=true + - traefik.http.routers.netbird-signal.tls.certresolver=letsencrypt + - traefik.http.routers.netbird-signal.priority=200 + # WebSocket route for signal - pass through without stripping prefix + - traefik.http.services.netbird-signal-ws.loadbalancer.server.port=80 + - traefik.http.routers.netbird-signal-ws.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/ws-proxy/signal`) + - traefik.http.routers.netbird-signal-ws.entrypoints=websecure + - traefik.http.routers.netbird-signal-ws.service=netbird-signal-ws + - traefik.http.routers.netbird-signal-ws.tls=true + - traefik.http.routers.netbird-signal-ws.tls.certresolver=letsencrypt + - traefik.http.routers.netbird-signal-ws.priority=300 + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + + # Relay + relay: + image: netbirdio/relay:latest + restart: unless-stopped + networks: + - netbird + - traefik_traefik + env_file: + - ./relay.env + labels: + - traefik.enable=true + - traefik.docker.network=traefik_default + - traefik.http.services.netbird-relay.loadbalancer.server.port=33080 + - traefik.http.routers.netbird-relay.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/relay`) + - traefik.http.routers.netbird-relay.entrypoints=websecure + - traefik.http.routers.netbird-relay.tls=true + - traefik.http.routers.netbird-relay.tls.certresolver=letsencrypt + - traefik.http.routers.netbird-relay.priority=200 + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + + # Management + management: + image: netbirdio/management:latest + restart: unless-stopped + networks: + - netbird + - traefik_traefik + volumes: + - netbird_management:/var/lib/netbird + - ./management.json:/etc/netbird/management.json + command: [ + "--port", "80", + "--log-file", "console", + "--log-level", "info", + "--disable-anonymous-metrics=false", + "--single-account-mode-domain=netbird.selfhosted", + "--dns-domain=netbird.selfhosted", + "--idp-sign-key-refresh-enabled" + ] + labels: + - traefik.enable=true + - traefik.docker.network=traefik_default + - traefik.http.services.netbird-management.loadbalancer.server.port=80 + - traefik.http.services.netbird-management-grpc.loadbalancer.server.port=80 + - traefik.http.services.netbird-management-grpc.loadbalancer.server.scheme=h2c + # REST API + - traefik.http.routers.netbird-api.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/api`) + - traefik.http.routers.netbird-api.entrypoints=websecure + - traefik.http.routers.netbird-api.service=netbird-management + - traefik.http.routers.netbird-api.tls=true + - traefik.http.routers.netbird-api.tls.certresolver=letsencrypt + - traefik.http.routers.netbird-api.priority=200 + # gRPC + - traefik.http.routers.netbird-management-grpc.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/management.ManagementService/`) + - traefik.http.routers.netbird-management-grpc.entrypoints=websecure + - traefik.http.routers.netbird-management-grpc.service=netbird-management-grpc + - traefik.http.routers.netbird-management-grpc.tls=true + - traefik.http.routers.netbird-management-grpc.tls.certresolver=letsencrypt + - traefik.http.routers.netbird-management-grpc.priority=200 + # WebSocket route for management + - traefik.http.routers.netbird-management-ws.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/ws-proxy/management`) + - traefik.http.routers.netbird-management-ws.entrypoints=websecure + - traefik.http.routers.netbird-management-ws.service=netbird-management + - traefik.http.routers.netbird-management-ws.tls=true + - traefik.http.routers.netbird-management-ws.tls.certresolver=letsencrypt + - traefik.http.routers.netbird-management-ws.priority=300 + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + + # Coturn + coturn: + image: coturn/coturn + restart: unless-stopped + volumes: + - ./turnserver.conf:/etc/turnserver.conf:ro + network_mode: host + command: + - -c /etc/turnserver.conf + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + + # Zitadel - identity provider + zitadel: + restart: 'always' + image: 'ghcr.io/zitadel/zitadel:v2.59.3' + command: 'start-from-init --masterkeyFromEnv --tlsMode external' + ports: + - "8085:8080" # <-- added + env_file: + - ./zitadel.env + environment: + ZITADEL_PROJECTIONS_MAXFAILURES: "3" + ZITADEL_PROJECTIONS_RETRYDELAY: "2s" + ZITADEL_PROJECTIONS_MAXPARALLELPROJECTIONS: "4" + ZITADEL_PROJECTIONS_TARGETS1_PARALLEL_PREFILLS: "2" + ZITADEL_PROJECTIONS_TARGETS1_BATCHSIZE: "500" + depends_on: + zdb: + condition: 'service_healthy' + volumes: + - ./machinekey:/machinekey + - netbird_zitadel_certs:/zdb-certs:ro + networks: + - netbird + - traefik_traefik + labels: + - traefik.enable=true + - traefik.docker.network=traefik_default + - traefik.http.services.zitadel.loadbalancer.server.port=8080 + - traefik.http.services.zitadel.loadbalancer.server.scheme=h2c + # OIDC wellknown + - traefik.http.routers.zitadel-wellknown.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/.well-known`) + - traefik.http.routers.zitadel-wellknown.entrypoints=websecure + - traefik.http.routers.zitadel-wellknown.service=zitadel + - traefik.http.routers.zitadel-wellknown.priority=300 + - traefik.http.routers.zitadel-wellknown.tls=true + - traefik.http.routers.zitadel-wellknown.tls.certresolver=letsencrypt + # OAuth + - traefik.http.routers.zitadel-oauth.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/oauth`) + - traefik.http.routers.zitadel-oauth.entrypoints=websecure + - traefik.http.routers.zitadel-oauth.service=zitadel + - traefik.http.routers.zitadel-oauth.priority=300 + - traefik.http.routers.zitadel-oauth.tls=true + - traefik.http.routers.zitadel-oauth.tls.certresolver=letsencrypt + # OIDC + - traefik.http.routers.zitadel-oidc.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/oidc`) + - traefik.http.routers.zitadel-oidc.entrypoints=websecure + - traefik.http.routers.zitadel-oidc.service=zitadel + - traefik.http.routers.zitadel-oidc.priority=300 + - traefik.http.routers.zitadel-oidc.tls=true + - traefik.http.routers.zitadel-oidc.tls.certresolver=letsencrypt + # UI Console + - traefik.http.routers.zitadel-ui.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/ui`) + - traefik.http.routers.zitadel-ui.entrypoints=websecure + - traefik.http.routers.zitadel-ui.service=zitadel + - traefik.http.routers.zitadel-ui.priority=300 + - traefik.http.routers.zitadel-ui.tls=true + - traefik.http.routers.zitadel-ui.tls.certresolver=letsencrypt + # Device flow + - traefik.http.routers.zitadel-device.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/device`) + - traefik.http.routers.zitadel-device.entrypoints=websecure + - traefik.http.routers.zitadel-device.service=zitadel + - traefik.http.routers.zitadel-device.priority=300 + - traefik.http.routers.zitadel-device.tls=true + - traefik.http.routers.zitadel-device.tls.certresolver=letsencrypt + # Management API + - traefik.http.routers.zitadel-mgmt.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/management/v1`) + - traefik.http.routers.zitadel-mgmt.entrypoints=websecure + - traefik.http.routers.zitadel-mgmt.service=zitadel + - traefik.http.routers.zitadel-mgmt.priority=300 + - traefik.http.routers.zitadel-mgmt.tls=true + - traefik.http.routers.zitadel-mgmt.tls.certresolver=letsencrypt + # Admin API + - traefik.http.routers.zitadel-admin.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/admin/v1`) + - traefik.http.routers.zitadel-admin.entrypoints=websecure + - traefik.http.routers.zitadel-admin.service=zitadel + - traefik.http.routers.zitadel-admin.priority=300 + - traefik.http.routers.zitadel-admin.tls=true + - traefik.http.routers.zitadel-admin.tls.certresolver=letsencrypt + # gRPC endpoints + - traefik.http.routers.zitadel-grpc-auth.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/zitadel.auth.v1.AuthService/`) + - traefik.http.routers.zitadel-grpc-auth.entrypoints=websecure + - traefik.http.routers.zitadel-grpc-auth.service=zitadel + - traefik.http.routers.zitadel-grpc-auth.priority=400 + - traefik.http.routers.zitadel-grpc-auth.tls=true + - traefik.http.routers.zitadel-grpc-auth.tls.certresolver=letsencrypt + - traefik.http.routers.zitadel-grpc-admin.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/zitadel.admin.v1.AdminService/`) + - traefik.http.routers.zitadel-grpc-admin.entrypoints=websecure + - traefik.http.routers.zitadel-grpc-admin.service=zitadel + - traefik.http.routers.zitadel-grpc-admin.priority=400 + - traefik.http.routers.zitadel-grpc-admin.tls=true + - traefik.http.routers.zitadel-grpc-admin.tls.certresolver=letsencrypt + - traefik.http.routers.zitadel-grpc-mgmt.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/zitadel.management.v1.ManagementService/`) + - traefik.http.routers.zitadel-grpc-mgmt.entrypoints=websecure + - traefik.http.routers.zitadel-grpc-mgmt.service=zitadel + - traefik.http.routers.zitadel-grpc-mgmt.priority=400 + - traefik.http.routers.zitadel-grpc-mgmt.tls=true + - traefik.http.routers.zitadel-grpc-mgmt.tls.certresolver=letsencrypt + # === CRITICAL ROUTE: /auth/v1 for PAT + wait_api === + - traefik.http.routers.zitadel-auth-v1.rule=Host(`netbird.rozic-dev.com`) && PathPrefix(`/auth/v1`) + - traefik.http.routers.zitadel-auth-v1.entrypoints=websecure + - traefik.http.routers.zitadel-auth-v1.service=zitadel + - traefik.http.routers.zitadel-auth-v1.tls=true + - traefik.http.routers.zitadel-auth-v1.tls.certresolver=letsencrypt + - traefik.http.routers.zitadel-auth-v1.priority=950 + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + + # Postgres for Zitadel + zdb: + restart: 'always' + networks: [netbird] + image: 'postgres:15-alpine' + env_file: + - ./zdb.env + volumes: + - netbird_zdb_data:/var/lib/postgresql/data:rw + healthcheck: + test: ["CMD-SHELL", "pg_isready -U root"] + interval: 5s + timeout: 60s + retries: 10 + start_period: 5s + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + +volumes: + netbird_zdb_data: + netbird_management: + netbird_zitadel_certs: + +networks: + netbird: + driver: bridge + traefik_traefik: + external: true + name: traefik_default diff --git a/install-netbird-traefik.sh b/install-netbird-traefik.sh old mode 100644 new mode 100755 index bb5cce6..baef4fc --- a/install-netbird-traefik.sh +++ b/install-netbird-traefik.sh @@ -2,14 +2,17 @@ set -e -# Configuration +# ============================================ +# CONFIG +# ============================================ TRAEFIK_NETWORK="traefik_traefik" -TRAEFIK_CERTRESOLVER="webssl" -NETBIRD_DOMAIN="netbird.yblis.fr" - +TRAEFIK_CERTRESOLVER="letsencrypt" +NETBIRD_DOMAIN="netbird.rozic-dev.com" export NETBIRD_DOMAIN -# Error handling functions +# ============================================ +# ERROR HANDLING HELPERS +# ============================================ handle_request_command_status() { PARSED_RESPONSE=$1 FUNCTION_NAME=$2 @@ -31,7 +34,9 @@ handle_zitadel_request_response() { sleep 1 } -# Dependency checks +# ============================================ +# DEPENDENCY CHECKS +# ============================================ check_jq() { if ! command -v jq &> /dev/null then @@ -51,12 +56,13 @@ check_docker_compose() { echo "docker compose" return fi - echo "docker-compose is not installed or not in PATH. Please follow the steps from the official guide: https://docs.docker.com/engine/install/" > /dev/stderr exit 1 } -# Wait functions +# ============================================ +# WAIT HELPERS +# ============================================ wait_pat() { PAT_PATH=$1 set +e @@ -78,40 +84,50 @@ wait_api() { counter=1 while true; do FLAGS="-s" - if [[ $counter -eq 45 ]]; then + if [[ $counter -eq 120 ]]; then FLAGS="-v" echo "" fi - - curl $FLAGS --fail --connect-timeout 1 -o /dev/null "$INSTANCE_URL/auth/v1/users/me" -H "Authorization: Bearer $PAT" + curl $FLAGS --connect-timeout 5 -o /dev/null \ + http://localhost:8085/auth/v1/users/me \ + -H "Authorization: Bearer $PAT" 2>/dev/null if [[ $? -eq 0 ]]; then break fi - if [[ $counter -eq 45 ]]; then - echo "" - echo "Unable to connect to Zitadel for more than 45s, please check the output above, your firewall rules and container logs" + if [[ $counter -eq 120 ]]; then + echo "Zitadel still not ready via http://localhost:8085 after 120s – checking logs..." + docker logs netbird-compose-zitadel-1 --tail 50 exit 1 fi echo -n " ." - sleep 1 + sleep 2 counter=$((counter + 1)) done echo " done" set -e } -# Zitadel API functions +# ============================================ +# ZITADEL API HELPERS +# ============================================ create_new_project() { INSTANCE_URL=$1 PAT=$2 PROJECT_NAME="NETBIRD" - RESPONSE=$( curl -sS -X POST "$INSTANCE_URL/management/v1/projects" \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ -d '{"name": "'"$PROJECT_NAME"'"}' ) + # Log the full response for debugging + echo "$RESPONSE" >> zitadel_api.log + # Check if response is valid JSON + if ! echo "$RESPONSE" | jq empty 2>/dev/null; then + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" >> zitadel_api.log + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" > /dev/stderr + exit 1 + fi PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.id') handle_zitadel_request_response "$PARSED_RESPONSE" "create_new_project" "$RESPONSE" echo "$PARSED_RESPONSE" @@ -120,12 +136,13 @@ create_new_project() { create_new_application() { INSTANCE_URL=$1 PAT=$2 - APPLICATION_NAME=$3 - BASE_REDIRECT_URL1=$4 - BASE_REDIRECT_URL2=$5 - LOGOUT_URL=$6 - ZITADEL_DEV_MODE=$7 - DEVICE_CODE=$8 + PROJECT_ID=$3 + APPLICATION_NAME=$4 + BASE_REDIRECT_URL1=$5 + BASE_REDIRECT_URL2=$6 + LOGOUT_URL=$7 + ZITADEL_DEV_MODE=$8 + DEVICE_CODE=$9 if [[ $DEVICE_CODE == "true" ]]; then GRANT_TYPES='["OIDC_GRANT_TYPE_AUTHORIZATION_CODE","OIDC_GRANT_TYPE_DEVICE_CODE","OIDC_GRANT_TYPE_REFRESH_TOKEN"]' @@ -160,6 +177,16 @@ create_new_application() { }' ) + # Log the full response for debugging + echo "$RESPONSE" >> zitadel_api.log + + # Check if response is valid JSON + if ! echo "$RESPONSE" | jq empty 2>/dev/null; then + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" >> zitadel_api.log + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" > /dev/stderr + exit 1 + fi + PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.clientId') handle_zitadel_request_response "$PARSED_RESPONSE" "create_new_application" "$RESPONSE" echo "$PARSED_RESPONSE" @@ -168,7 +195,6 @@ create_new_application() { create_service_user() { INSTANCE_URL=$1 PAT=$2 - RESPONSE=$( curl -sS -X POST "$INSTANCE_URL/management/v1/users/machine" \ -H "Authorization: Bearer $PAT" \ @@ -180,6 +206,14 @@ create_service_user() { "accessTokenType": "ACCESS_TOKEN_TYPE_JWT" }' ) + # Log the full response for debugging + echo "$RESPONSE" >> zitadel_api.log + # Check if response is valid JSON + if ! echo "$RESPONSE" | jq empty 2>/dev/null; then + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" >> zitadel_api.log + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" > /dev/stderr + exit 1 + fi PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.userId') handle_zitadel_request_response "$PARSED_RESPONSE" "create_service_user" "$RESPONSE" echo "$PARSED_RESPONSE" @@ -189,13 +223,20 @@ create_service_user_secret() { INSTANCE_URL=$1 PAT=$2 USER_ID=$3 - RESPONSE=$( curl -sS -X PUT "$INSTANCE_URL/management/v1/users/$USER_ID/secret" \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ -d '{}' ) + # Log the full response for debugging + echo "$RESPONSE" >> zitadel_api.log + # Check if response is valid JSON + if ! echo "$RESPONSE" | jq empty 2>/dev/null; then + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" >> zitadel_api.log + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" > /dev/stderr + exit 1 + fi SERVICE_USER_CLIENT_ID=$(echo "$RESPONSE" | jq -r '.clientId') handle_zitadel_request_response "$SERVICE_USER_CLIENT_ID" "create_service_user_secret_id" "$RESPONSE" SERVICE_USER_CLIENT_SECRET=$(echo "$RESPONSE" | jq -r '.clientSecret') @@ -206,7 +247,6 @@ add_organization_user_manager() { INSTANCE_URL=$1 PAT=$2 USER_ID=$3 - RESPONSE=$( curl -sS -X POST "$INSTANCE_URL/management/v1/orgs/me/members" \ -H "Authorization: Bearer $PAT" \ @@ -218,6 +258,14 @@ add_organization_user_manager() { ] }' ) + # Log the full response for debugging + echo "$RESPONSE" >> zitadel_api.log + # Check if response is valid JSON + if ! echo "$RESPONSE" | jq empty 2>/dev/null; then + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" >> zitadel_api.log + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" > /dev/stderr + exit 1 + fi PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.details.creationDate') handle_zitadel_request_response "$PARSED_RESPONSE" "add_organization_user_manager" "$RESPONSE" echo "$PARSED_RESPONSE" @@ -246,6 +294,14 @@ create_admin_user() { "passwordChangeRequired": true }' ) + # Log the full response for debugging + echo "$RESPONSE" >> zitadel_api.log + # Check if response is valid JSON + if ! echo "$RESPONSE" | jq empty 2>/dev/null; then + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" >> zitadel_api.log + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" > /dev/stderr + exit 1 + fi PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.userId') handle_zitadel_request_response "$PARSED_RESPONSE" "create_admin_user" "$RESPONSE" echo "$PARSED_RESPONSE" @@ -255,7 +311,6 @@ add_instance_admin() { INSTANCE_URL=$1 PAT=$2 USER_ID=$3 - RESPONSE=$( curl -sS -X POST "$INSTANCE_URL/admin/v1/members" \ -H "Authorization: Bearer $PAT" \ @@ -267,6 +322,14 @@ add_instance_admin() { ] }' ) + # Log the full response for debugging + echo "$RESPONSE" >> zitadel_api.log + # Check if response is valid JSON + if ! echo "$RESPONSE" | jq empty 2>/dev/null; then + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" >> zitadel_api.log + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" > /dev/stderr + exit 1 + fi PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.details.creationDate') handle_zitadel_request_response "$PARSED_RESPONSE" "add_instance_admin" "$RESPONSE" echo "$PARSED_RESPONSE" @@ -275,34 +338,57 @@ add_instance_admin() { delete_auto_service_user() { INSTANCE_URL=$1 PAT=$2 - RESPONSE=$( curl -sS -X GET "$INSTANCE_URL/auth/v1/users/me" \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ ) + # Log the full response for debugging + echo "$RESPONSE" >> zitadel_api.log + # Check if response is valid JSON + if ! echo "$RESPONSE" | jq empty 2>/dev/null; then + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" >> zitadel_api.log + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" > /dev/stderr + exit 1 + fi USER_ID=$(echo "$RESPONSE" | jq -r '.user.id') handle_zitadel_request_response "$USER_ID" "delete_auto_service_user_get_user" "$RESPONSE" - RESPONSE=$( curl -sS -X DELETE "$INSTANCE_URL/admin/v1/members/$USER_ID" \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ ) + # Log the full response for debugging + echo "$RESPONSE" >> zitadel_api.log + # Check if response is valid JSON + if ! echo "$RESPONSE" | jq empty 2>/dev/null; then + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" >> zitadel_api.log + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" > /dev/stderr + exit 1 + fi PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.details.changeDate') handle_zitadel_request_response "$PARSED_RESPONSE" "delete_auto_service_user_remove_instance_permissions" "$RESPONSE" - RESPONSE=$( curl -sS -X DELETE "$INSTANCE_URL/management/v1/orgs/me/members/$USER_ID" \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ ) + # Log the full response for debugging + echo "$RESPONSE" >> zitadel_api.log + # Check if response is valid JSON + if ! echo "$RESPONSE" | jq empty 2>/dev/null; then + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" >> zitadel_api.log + echo "ERROR: Invalid response from Zitadel API: $RESPONSE" > /dev/stderr + exit 1 + fi PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.details.changeDate') handle_zitadel_request_response "$PARSED_RESPONSE" "delete_auto_service_user_remove_org_permissions" "$RESPONSE" echo "$PARSED_RESPONSE" } -# Get external IP for TURN server +# ============================================ +# TURN IP +# ============================================ get_turn_external_ip() { TURN_EXTERNAL_IP_CONFIG="#external-ip=" IP=$(curl -s -4 https://jsonip.com | jq -r '.ip') @@ -312,15 +398,18 @@ get_turn_external_ip() { echo "$TURN_EXTERNAL_IP_CONFIG" } -# Main initialization function +# ============================================ +# MAIN +# ============================================ main() { + # Clear log file + > zitadel_api.log + echo "Initializing NetBird with Traefik..." - # Check dependencies check_jq DOCKER_COMPOSE_COMMAND=$(check_docker_compose) - # Check if files already exist if [ -f zitadel.env ]; then echo "Generated files already exist, if you want to reinitialize the environment, please remove them first." echo "You can use the following commands:" @@ -330,7 +419,7 @@ main() { exit 1 fi - # Generate passwords and secrets + # === Generate passwords & secrets === ZITADEL_MASTERKEY="$(openssl rand -base64 32 | head -c 32)" POSTGRES_ROOT_PASSWORD="$(openssl rand -base64 32 | sed 's/=//g')@" POSTGRES_ZITADEL_PASSWORD="$(openssl rand -base64 32 | sed 's/=//g')@" @@ -348,7 +437,7 @@ main() { echo "Generating configuration files..." - # Generate zitadel.env + # === zitadel.env === cat > zitadel.env < zdb.env < turnserver.conf < relay.env < dashboard.env echo "" > management.json - # Generate docker-compose.yml + # === docker-compose.yml template === cat > docker-compose.yml <<'EOF' services: # UI dashboard dashboard: image: netbirdio/dashboard:latest restart: unless-stopped - networks: + networks: - netbird - traefik_traefik env_file: - ./dashboard.env labels: - traefik.enable=true - - traefik.docker.network=traefik_traefik + - traefik.docker.network=traefik_default - traefik.http.services.netbird-dashboard.loadbalancer.server.port=80 - traefik.http.routers.netbird-dashboard.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) - - traefik.http.routers.netbird-dashboard.entrypoints=https + - traefik.http.routers.netbird-dashboard.entrypoints=websecure - traefik.http.routers.netbird-dashboard.tls=true - traefik.http.routers.netbird-dashboard.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.netbird-dashboard.priority=50 @@ -444,16 +531,16 @@ services: signal: image: netbirdio/signal:latest restart: unless-stopped - networks: + networks: - netbird - traefik_traefik labels: - traefik.enable=true - - traefik.docker.network=traefik_traefik + - traefik.docker.network=traefik_default - traefik.http.services.netbird-signal.loadbalancer.server.port=10000 - traefik.http.services.netbird-signal.loadbalancer.server.scheme=h2c - traefik.http.routers.netbird-signal.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/signalexchange.SignalExchange/`) - - traefik.http.routers.netbird-signal.entrypoints=https + - traefik.http.routers.netbird-signal.entrypoints=websecure - traefik.http.routers.netbird-signal.service=netbird-signal - traefik.http.routers.netbird-signal.tls=true - traefik.http.routers.netbird-signal.tls.certresolver=NETBIRD_TRAEFIK_SSL @@ -461,7 +548,7 @@ services: # WebSocket route for signal - pass through without stripping prefix - traefik.http.services.netbird-signal-ws.loadbalancer.server.port=80 - traefik.http.routers.netbird-signal-ws.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/ws-proxy/signal`) - - traefik.http.routers.netbird-signal-ws.entrypoints=https + - traefik.http.routers.netbird-signal-ws.entrypoints=websecure - traefik.http.routers.netbird-signal-ws.service=netbird-signal-ws - traefik.http.routers.netbird-signal-ws.tls=true - traefik.http.routers.netbird-signal-ws.tls.certresolver=NETBIRD_TRAEFIK_SSL @@ -476,17 +563,17 @@ services: relay: image: netbirdio/relay:latest restart: unless-stopped - networks: + networks: - netbird - traefik_traefik env_file: - ./relay.env labels: - traefik.enable=true - - traefik.docker.network=traefik_traefik + - traefik.docker.network=traefik_default - traefik.http.services.netbird-relay.loadbalancer.server.port=33080 - traefik.http.routers.netbird-relay.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/relay`) - - traefik.http.routers.netbird-relay.entrypoints=https + - traefik.http.routers.netbird-relay.entrypoints=websecure - traefik.http.routers.netbird-relay.tls=true - traefik.http.routers.netbird-relay.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.netbird-relay.priority=200 @@ -500,7 +587,7 @@ services: management: image: netbirdio/management:latest restart: unless-stopped - networks: + networks: - netbird - traefik_traefik volumes: @@ -517,27 +604,27 @@ services: ] labels: - traefik.enable=true - - traefik.docker.network=traefik_traefik + - traefik.docker.network=traefik_default - traefik.http.services.netbird-management.loadbalancer.server.port=80 - traefik.http.services.netbird-management-grpc.loadbalancer.server.port=80 - traefik.http.services.netbird-management-grpc.loadbalancer.server.scheme=h2c # REST API - traefik.http.routers.netbird-api.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/api`) - - traefik.http.routers.netbird-api.entrypoints=https + - traefik.http.routers.netbird-api.entrypoints=websecure - traefik.http.routers.netbird-api.service=netbird-management - traefik.http.routers.netbird-api.tls=true - traefik.http.routers.netbird-api.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.netbird-api.priority=200 # gRPC - traefik.http.routers.netbird-management-grpc.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/management.ManagementService/`) - - traefik.http.routers.netbird-management-grpc.entrypoints=https + - traefik.http.routers.netbird-management-grpc.entrypoints=websecure - traefik.http.routers.netbird-management-grpc.service=netbird-management-grpc - traefik.http.routers.netbird-management-grpc.tls=true - traefik.http.routers.netbird-management-grpc.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.netbird-management-grpc.priority=200 - # WebSocket route for management - pass through without stripping prefix + # WebSocket route for management - traefik.http.routers.netbird-management-ws.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/ws-proxy/management`) - - traefik.http.routers.netbird-management-ws.entrypoints=https + - traefik.http.routers.netbird-management-ws.entrypoints=websecure - traefik.http.routers.netbird-management-ws.service=netbird-management - traefik.http.routers.netbird-management-ws.tls=true - traefik.http.routers.netbird-management-ws.tls.certresolver=NETBIRD_TRAEFIK_SSL @@ -566,101 +653,107 @@ services: # Zitadel - identity provider zitadel: restart: 'always' - image: 'ghcr.io/zitadel/zitadel:v2.64.1' + image: 'ghcr.io/zitadel/zitadel:v2.59.3' command: 'start-from-init --masterkeyFromEnv --tlsMode external' + ports: + - "8085:8080" # <-- added env_file: - ./zitadel.env + environment: + ZITADEL_PROJECTIONS_MAXFAILURES: "3" + ZITADEL_PROJECTIONS_RETRYDELAY: "2s" + ZITADEL_PROJECTIONS_MAXPARALLELPROJECTIONS: "4" + ZITADEL_PROJECTIONS_TARGETS1_PARALLEL_PREFILLS: "2" + ZITADEL_PROJECTIONS_TARGETS1_BATCHSIZE: "500" depends_on: zdb: condition: 'service_healthy' volumes: - ./machinekey:/machinekey - netbird_zitadel_certs:/zdb-certs:ro - networks: + networks: - netbird - traefik_traefik labels: - traefik.enable=true - - traefik.docker.network=traefik_traefik + - traefik.docker.network=traefik_default - traefik.http.services.zitadel.loadbalancer.server.port=8080 - traefik.http.services.zitadel.loadbalancer.server.scheme=h2c # OIDC wellknown - traefik.http.routers.zitadel-wellknown.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/.well-known`) - - traefik.http.routers.zitadel-wellknown.entrypoints=https + - traefik.http.routers.zitadel-wellknown.entrypoints=websecure - traefik.http.routers.zitadel-wellknown.service=zitadel - traefik.http.routers.zitadel-wellknown.priority=300 - traefik.http.routers.zitadel-wellknown.tls=true - traefik.http.routers.zitadel-wellknown.tls.certresolver=NETBIRD_TRAEFIK_SSL # OAuth - traefik.http.routers.zitadel-oauth.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/oauth`) - - traefik.http.routers.zitadel-oauth.entrypoints=https + - traefik.http.routers.zitadel-oauth.entrypoints=websecure - traefik.http.routers.zitadel-oauth.service=zitadel - traefik.http.routers.zitadel-oauth.priority=300 - traefik.http.routers.zitadel-oauth.tls=true - traefik.http.routers.zitadel-oauth.tls.certresolver=NETBIRD_TRAEFIK_SSL # OIDC - traefik.http.routers.zitadel-oidc.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/oidc`) - - traefik.http.routers.zitadel-oidc.entrypoints=https + - traefik.http.routers.zitadel-oidc.entrypoints=websecure - traefik.http.routers.zitadel-oidc.service=zitadel - traefik.http.routers.zitadel-oidc.priority=300 - traefik.http.routers.zitadel-oidc.tls=true - traefik.http.routers.zitadel-oidc.tls.certresolver=NETBIRD_TRAEFIK_SSL # UI Console - traefik.http.routers.zitadel-ui.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/ui`) - - traefik.http.routers.zitadel-ui.entrypoints=https + - traefik.http.routers.zitadel-ui.entrypoints=websecure - traefik.http.routers.zitadel-ui.service=zitadel - traefik.http.routers.zitadel-ui.priority=300 - traefik.http.routers.zitadel-ui.tls=true - traefik.http.routers.zitadel-ui.tls.certresolver=NETBIRD_TRAEFIK_SSL # Device flow - traefik.http.routers.zitadel-device.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/device`) - - traefik.http.routers.zitadel-device.entrypoints=https + - traefik.http.routers.zitadel-device.entrypoints=websecure - traefik.http.routers.zitadel-device.service=zitadel - traefik.http.routers.zitadel-device.priority=300 - traefik.http.routers.zitadel-device.tls=true - traefik.http.routers.zitadel-device.tls.certresolver=NETBIRD_TRAEFIK_SSL # Management API - traefik.http.routers.zitadel-mgmt.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/management/v1`) - - traefik.http.routers.zitadel-mgmt.entrypoints=https + - traefik.http.routers.zitadel-mgmt.entrypoints=websecure - traefik.http.routers.zitadel-mgmt.service=zitadel - traefik.http.routers.zitadel-mgmt.priority=300 - traefik.http.routers.zitadel-mgmt.tls=true - traefik.http.routers.zitadel-mgmt.tls.certresolver=NETBIRD_TRAEFIK_SSL - # Auth API - - traefik.http.routers.zitadel-auth.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/auth/v1`) - - traefik.http.routers.zitadel-auth.entrypoints=https - - traefik.http.routers.zitadel-auth.service=zitadel - - traefik.http.routers.zitadel-auth.priority=300 - - traefik.http.routers.zitadel-auth.tls=true - - traefik.http.routers.zitadel-auth.tls.certresolver=NETBIRD_TRAEFIK_SSL # Admin API - traefik.http.routers.zitadel-admin.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/admin/v1`) - - traefik.http.routers.zitadel-admin.entrypoints=https + - traefik.http.routers.zitadel-admin.entrypoints=websecure - traefik.http.routers.zitadel-admin.service=zitadel - traefik.http.routers.zitadel-admin.priority=300 - traefik.http.routers.zitadel-admin.tls=true - traefik.http.routers.zitadel-admin.tls.certresolver=NETBIRD_TRAEFIK_SSL - # Endpoints gRPC Zitadel + # gRPC endpoints - traefik.http.routers.zitadel-grpc-auth.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/zitadel.auth.v1.AuthService/`) - - traefik.http.routers.zitadel-grpc-auth.entrypoints=https + - traefik.http.routers.zitadel-grpc-auth.entrypoints=websecure - traefik.http.routers.zitadel-grpc-auth.service=zitadel - traefik.http.routers.zitadel-grpc-auth.priority=400 - traefik.http.routers.zitadel-grpc-auth.tls=true - traefik.http.routers.zitadel-grpc-auth.tls.certresolver=NETBIRD_TRAEFIK_SSL - - traefik.http.routers.zitadel-grpc-admin.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/zitadel.admin.v1.AdminService/`) - - traefik.http.routers.zitadel-grpc-admin.entrypoints=https + - traefik.http.routers.zitadel-grpc-admin.entrypoints=websecure - traefik.http.routers.zitadel-grpc-admin.service=zitadel - traefik.http.routers.zitadel-grpc-admin.priority=400 - traefik.http.routers.zitadel-grpc-admin.tls=true - traefik.http.routers.zitadel-grpc-admin.tls.certresolver=NETBIRD_TRAEFIK_SSL - - traefik.http.routers.zitadel-grpc-mgmt.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/zitadel.management.v1.ManagementService/`) - - traefik.http.routers.zitadel-grpc-mgmt.entrypoints=https + - traefik.http.routers.zitadel-grpc-mgmt.entrypoints=websecure - traefik.http.routers.zitadel-grpc-mgmt.service=zitadel - traefik.http.routers.zitadel-grpc-mgmt.priority=400 - traefik.http.routers.zitadel-grpc-mgmt.tls=true - traefik.http.routers.zitadel-grpc-mgmt.tls.certresolver=NETBIRD_TRAEFIK_SSL + # === CRITICAL ROUTE: /auth/v1 for PAT + wait_api === + - traefik.http.routers.zitadel-auth-v1.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/auth/v1`) + - traefik.http.routers.zitadel-auth-v1.entrypoints=websecure + - traefik.http.routers.zitadel-auth-v1.service=zitadel + - traefik.http.routers.zitadel-auth-v1.tls=true + - traefik.http.routers.zitadel-auth-v1.tls.certresolver=NETBIRD_TRAEFIK_SSL + - traefik.http.routers.zitadel-auth-v1.priority=950 logging: driver: "json-file" options: @@ -671,13 +764,13 @@ services: zdb: restart: 'always' networks: [netbird] - image: 'postgres:16-alpine' + image: 'postgres:15-alpine' env_file: - ./zdb.env volumes: - netbird_zdb_data:/var/lib/postgresql/data:rw healthcheck: - test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"] + test: ["CMD-SHELL", "pg_isready -U root"] interval: 5s timeout: 60s retries: 10 @@ -698,10 +791,13 @@ networks: driver: bridge traefik_traefik: external: true + name: traefik_default EOF -sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/${TRAEFIK_CERTRESOLVER}/g" docker-compose.yml - # Create machinekey directory + # Replace placeholders + sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/${TRAEFIK_CERTRESOLVER}/g" docker-compose.yml + + # Create machinekey dir mkdir -p machinekey chmod 777 machinekey @@ -717,7 +813,6 @@ sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/ echo "Waiting for Zitadel to initialize..." sleep 60 - # Configuration automatique de Zitadel echo "Configuring Zitadel applications..." INSTANCE_URL="https://$NETBIRD_DOMAIN" @@ -727,7 +822,7 @@ sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/ wait_pat "$TOKEN_PATH" echo "Reading Zitadel PAT" - PAT=$(cat $TOKEN_PATH) + PAT=$(cat "$TOKEN_PATH") if [ "$PAT" = "null" ]; then echo "Failed getting Zitadel PAT" exit 1 @@ -736,29 +831,24 @@ sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/ echo -n "Waiting for Zitadel to become ready " wait_api "$INSTANCE_URL" "$PAT" - # Create project echo "Creating Zitadel project" PROJECT_ID=$(create_new_project "$INSTANCE_URL" "$PAT") - # Create applications echo "Creating Dashboard application" - DASHBOARD_APPLICATION_CLIENT_ID=$(create_new_application "$INSTANCE_URL" "$PAT" "Dashboard" "https://$NETBIRD_DOMAIN/nb-auth" "https://$NETBIRD_DOMAIN/nb-silent-auth" "https://$NETBIRD_DOMAIN/" "false" "false") + DASHBOARD_APPLICATION_CLIENT_ID=$(create_new_application "$INSTANCE_URL" "$PAT" "$PROJECT_ID" "Dashboard" "https://$NETBIRD_DOMAIN/nb-auth" "https://$NETBIRD_DOMAIN/nb-silent-auth" "https://$NETBIRD_DOMAIN/" "false" "false") echo "Creating CLI application" - CLI_APPLICATION_CLIENT_ID=$(create_new_application "$INSTANCE_URL" "$PAT" "Cli" "http://localhost:53000/" "http://localhost:54000/" "http://localhost:53000/" "true" "true") + CLI_APPLICATION_CLIENT_ID=$(create_new_application "$INSTANCE_URL" "$PAT" "$PROJECT_ID" "Cli" "http://localhost:53000/" "http://localhost:54000/" "http://localhost:53000/" "true" "true") - # Create service user echo "Creating service user" MACHINE_USER_ID=$(create_service_user "$INSTANCE_URL" "$PAT") create_service_user_secret "$INSTANCE_URL" "$PAT" "$MACHINE_USER_ID" add_organization_user_manager "$INSTANCE_URL" "$PAT" "$MACHINE_USER_ID" - # Create admin user echo "Creating admin user" HUMAN_USER_ID=$(create_admin_user "$INSTANCE_URL" "$PAT" "$ZITADEL_ADMIN_USERNAME" "$ZITADEL_ADMIN_PASSWORD") add_instance_admin "$INSTANCE_URL" "$PAT" "$HUMAN_USER_ID" - # Clean up auto service user echo "Cleaning up auto service user" DATE=$(delete_auto_service_user "$INSTANCE_URL" "$PAT") if [ "$DATE" = "null" ]; then @@ -766,7 +856,6 @@ sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/ echo "Please remove it manually" fi - # Generate NetBird configuration echo "Generating NetBird configuration..." # dashboard.env @@ -867,5 +956,4 @@ EOF echo "Note: The admin password will require changing on first login." } -# Execute main function main "$@" diff --git a/machinekey/zitadel-admin-sa.token b/machinekey/zitadel-admin-sa.token new file mode 100644 index 0000000..20bcf96 --- /dev/null +++ b/machinekey/zitadel-admin-sa.token @@ -0,0 +1 @@ +tlqWhCwoB4bEoKhqlbw_mHr8qGxjuJVdjfYGH70-Tr1bwzGu0Q5FNUMiostaLXoAk5GvCXc diff --git a/management.json b/management.json new file mode 100644 index 0000000..23c39fb --- /dev/null +++ b/management.json @@ -0,0 +1,112 @@ +{ + "Stuns": [ + { + "Proto": "udp", + "URI": "stun:netbird.rozic-dev.com:3478", + "Username": "", + "Password": "" + } + ], + "TURNConfig": { + "TimeBasedCredentials": false, + "CredentialsTTL": "0s", + "Secret": "", + "Turns": [ + { + "Proto": "udp", + "URI": "turn:netbird.rozic-dev.com:3478", + "Username": "self", + "Password": "irxMo1xxkrpzLUBK4AkWHtJI2CHjqhD9RZEmXqSXLgU" + } + ] + }, + "Relay": { + "Addresses": [ + "rels://netbird.rozic-dev.com:443/relay" + ], + "CredentialsTTL": "24h0m0s", + "Secret": "WsMuriIhq7xs8vWFbWf59cOV1jcFlqkyb/cbEjmGNiQ" + }, + "Signal": { + "Proto": "https", + "URI": "netbird.rozic-dev.com:443", + "Username": "", + "Password": "" + }, + "Datadir": "/var/lib/netbird/", + "DataStoreEncryptionKey": "TOpiGD/r2qWq+UAjAsv53IyqABPsXJ3zGPZ9j6dk2bc=", + "HttpConfig": { + "LetsEncryptDomain": "", + "CertFile": "", + "CertKey": "", + "AuthAudience": "347529025415086083", + "AuthIssuer": "https://netbird.rozic-dev.com", + "AuthUserIDClaim": "", + "AuthKeysLocation": "https://netbird.rozic-dev.com/oauth/v2/keys", + "OIDCConfigEndpoint": "https://netbird.rozic-dev.com/.well-known/openid-configuration", + "IdpSignKeyRefreshEnabled": true, + "ExtraAuthAudience": "" + }, + "IdpManagerConfig": { + "ManagerType": "zitadel", + "ClientConfig": { + "Issuer": "https://netbird.rozic-dev.com", + "TokenEndpoint": "https://netbird.rozic-dev.com/oauth/v2/token", + "ClientID": "netbird-service-account", + "ClientSecret": "GncnVxtSadobmYR0jQINd8OWYk8Mwbi6MGvo4aPnaqDyBb8CILrXb5bVAH38CNFC", + "GrantType": "client_credentials" + }, + "ExtraConfig": { + "ManagementEndpoint": "https://netbird.rozic-dev.com/management/v1" + }, + "Auth0ClientCredentials": null, + "AzureClientCredentials": null, + "KeycloakClientCredentials": null, + "ZitadelClientCredentials": null + }, + "DeviceAuthorizationFlow": { + "Provider": "hosted", + "ProviderConfig": { + "ClientID": "347529027596124163", + "ClientSecret": "", + "Domain": "netbird.rozic-dev.com", + "Audience": "347529027596124163", + "TokenEndpoint": "https://netbird.rozic-dev.com/oauth/v2/token", + "DeviceAuthEndpoint": "https://netbird.rozic-dev.com/oauth/v2/device_authorization", + "AuthorizationEndpoint": "", + "Scope": "openid", + "UseIDToken": false, + "RedirectURLs": null, + "DisablePromptLogin": false, + "LoginFlag": 0 + } + }, + "PKCEAuthorizationFlow": { + "ProviderConfig": { + "ClientID": "347529027596124163", + "ClientSecret": "", + "Domain": "", + "Audience": "347529027596124163", + "TokenEndpoint": "https://netbird.rozic-dev.com/oauth/v2/token", + "DeviceAuthEndpoint": "", + "AuthorizationEndpoint": "https://netbird.rozic-dev.com/oauth/v2/authorize", + "Scope": "openid profile email offline_access", + "UseIDToken": false, + "RedirectURLs": [ + "http://localhost:53000/", + "http://localhost:54000/" + ], + "DisablePromptLogin": false, + "LoginFlag": 0 + } + }, + "StoreConfig": { + "Engine": "" + }, + "ReverseProxy": { + "TrustedHTTPProxies": null, + "TrustedHTTPProxiesCount": 0, + "TrustedPeers": null + }, + "DisableDefaultPolicy": false +} \ No newline at end of file diff --git a/readme.md b/readme.md index 352f400..d9969f3 100644 --- a/readme.md +++ b/readme.md @@ -13,7 +13,7 @@ cd netbird-traefik --- -## ⚙️ Configuration +## ⚙️ Configurations Before running the script, edit the following variables inside `install-netbird-traefik.sh`: diff --git a/reinit.sh b/reinit.sh new file mode 100755 index 0000000..baa0c6d --- /dev/null +++ b/reinit.sh @@ -0,0 +1,17 @@ +docker compose down -v +rm -rf machinekey/ docker-compose.yml *.env management.json turnserver.conf relay.env +#rm -rf machinekey/ +./install-netbird-traefik.sh +# Kill the hanging script with Ctrl+C if it's still running + +# Start the rest of the NetBird services manually +docker compose up -d management dashboard signal relay coturn + +# Wait 10 seconds +sleep 10 + +# DONE! Your NetBird is fully working +echo "🎉 SUCCESS! Dashboard: https://netbird.rozic-dev.com" +echo "Login with:" +echo "Username: admin@netbird.rozic-dev.com" +echo "Password: $(grep Password .env | cut -d' ' -f2-)" # prints the password from the .env file the script created earlier diff --git a/reinit1.sh b/reinit1.sh new file mode 100755 index 0000000..90a4a82 --- /dev/null +++ b/reinit1.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +echo "🧹 Cleaning old data..." +docker compose down -v + +docker compose down -v || true + +rm -rf machinekey +rm -f docker-compose.yml zitadel.env zdb.env dashboard.env management.json relay.env turnserver.conf + +rm -rf machinekey/ docker-compose.yml *.env management.json turnserver.conf relay.env dashboard.env || true + +echo "🚀 Starting fresh installation..." +# Run the config part – ignore the false health-check failure at the end +./install-netbird-traefik.sh || true + +echo "🔧 Starting the real NetBird services (bypassing the dumb health check)..." +docker compose up -d management dashboard signal relay coturn + +echo "⏳ Waiting 15 seconds for everything to settle..." +sleep 15 + +echo "" +echo "🎉🎉🎉 ALL DONE – YOUR NETBIRD IS READY! 🎉🎉🎉" +echo "" +echo "Dashboard → https://netbird.rozic-dev.com" +echo "" +echo "Login with:" +echo " Username: admin@netbird.rozic-dev.com" +if [ -f .env ]; then + PASSWORD=$(grep "^Password:" .env | cut -d' ' -f2-) + echo " Password: $PASSWORD" +else + echo " Password: (no .env – just click 'Sign up' on the page and create any user – first user = admin)" +fi +echo "" +echo "Zitadel console (optional): https://netbird.rozic-dev.com/ui/console" +echo "" +echo "Enjoy your fully private, self-hosted WireGuard mesh! 🚀" \ No newline at end of file diff --git a/relay.env b/relay.env new file mode 100644 index 0000000..d990e9c --- /dev/null +++ b/relay.env @@ -0,0 +1,5 @@ +NB_LOG_LEVEL=info +NB_LISTEN_ADDRESS=:33080 +NB_EXPOSED_ADDRESS=rels://netbird.rozic-dev.com:443/relay +NB_AUTH_SECRET=WsMuriIhq7xs8vWFbWf59cOV1jcFlqkyb/cbEjmGNiQ +NETBIRD_DOMAIN=netbird.rozic-dev.com diff --git a/turnserver.conf b/turnserver.conf new file mode 100644 index 0000000..d639469 --- /dev/null +++ b/turnserver.conf @@ -0,0 +1,15 @@ +listening-port=3478 +external-ip=5.75.240.238 +tls-listening-port=5349 +min-port=49152 +max-port=65535 +fingerprint +lt-cred-mech +user=self:irxMo1xxkrpzLUBK4AkWHtJI2CHjqhD9RZEmXqSXLgU +realm=wiretrustee.com +cert=/etc/coturn/certs/cert.pem +pkey=/etc/coturn/private/privkey.pem +log-file=stdout +no-software-attribute +pidfile="/var/tmp/turnserver.pid" +no-cli diff --git a/zdb.env b/zdb.env new file mode 100644 index 0000000..4e44d07 --- /dev/null +++ b/zdb.env @@ -0,0 +1,2 @@ +POSTGRES_USER=root +POSTGRES_PASSWORD=WuNCCFl9IrF4x1YOvUb5g0297qf4CE5qKPqlwY3HFn0@ diff --git a/zitadel.env b/zitadel.env new file mode 100644 index 0000000..33fdfa9 --- /dev/null +++ b/zitadel.env @@ -0,0 +1,21 @@ +ZITADEL_LOG_LEVEL=debug +ZITADEL_MASTERKEY=IuuoiDADifLLM7enKI/ia+s0sesaqF8w +ZITADEL_EXTERNALSECURE=true +ZITADEL_TLS_ENABLED=false +ZITADEL_EXTERNALPORT=443 +ZITADEL_EXTERNALDOMAIN=netbird.rozic-dev.com +ZITADEL_FIRSTINSTANCE_PATPATH=/machinekey/zitadel-admin-sa.token +ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_USERNAME=zitadel-admin-sa +ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_NAME=Admin +ZITADEL_FIRSTINSTANCE_ORG_MACHINE_PAT_SCOPES=openid +ZITADEL_FIRSTINSTANCE_ORG_MACHINE_PAT_EXPIRATIONDATE=2025-11-20T12:19:08Z +ZITADEL_DATABASE_POSTGRES_HOST=zdb +ZITADEL_DATABASE_POSTGRES_PORT=5432 +ZITADEL_DATABASE_POSTGRES_DATABASE=zitadel +ZITADEL_DATABASE_POSTGRES_USER_USERNAME=zitadel +ZITADEL_DATABASE_POSTGRES_USER_PASSWORD=g798XBwToxA9Ehu2SYGbz+CE5MxAuC/BVoxSlxhPHTU@ +ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE=disable +ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME=root +ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD=WuNCCFl9IrF4x1YOvUb5g0297qf4CE5qKPqlwY3HFn0@ +ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE=disable +NETBIRD_DOMAIN=netbird.rozic-dev.com diff --git a/zitadel_api.log b/zitadel_api.log new file mode 100644 index 0000000..2e3759b --- /dev/null +++ b/zitadel_api.log @@ -0,0 +1,11 @@ +{"id":"347529023250759683","details":{"sequence":"2","creationDate":"2025-11-20T11:50:39.824071Z","resourceOwner":"347528924584017923"}} +{"appId":"347529025415020547","details":{"sequence":"4","creationDate":"2025-11-20T11:50:41.111831Z","resourceOwner":"347528924584017923"},"clientId":"347529025415086083"} +{"appId":"347529027596058627","details":{"sequence":"6","creationDate":"2025-11-20T11:50:42.406343Z","resourceOwner":"347528924584017923"},"clientId":"347529027596124163","noneCompliant":true,"complianceProblems":[{"key":"Application.OIDC.V1.NotCompliant","localizedMessage":"Your configuration is not compliant and differs from OIDC 1.0 standard."},{"key":"Application.OIDC.V1.NotCompliant","localizedMessage":"Your configuration is not compliant and differs from OIDC 1.0 standard."},{"key":"Application.OIDC.V1.Code.RedirectUris.HttpOnlyForWeb","localizedMessage":"Grant type code only allowed http redirect uris for apptype web."}]} +{"userId":"347529029743542275","details":{"sequence":"1","changeDate":"2025-11-20T11:50:43.737489Z","resourceOwner":"347528924584017923"}} +{"clientId":"netbird-service-account","clientSecret":"GncnVxtSadobmYR0jQINd8OWYk8Mwbi6MGvo4aPnaqDyBb8CILrXb5bVAH38CNFC","details":{"sequence":"2","creationDate":"2025-11-20T11:50:45.080684Z","resourceOwner":"347528924584017923"}} +{"details":{"sequence":"7","creationDate":"2025-11-20T11:50:47.361844Z","resourceOwner":"347528924584017923"}} +{"userId":"347529038249590787","details":{"sequence":"2","creationDate":"2025-11-20T11:50:49.908386Z","resourceOwner":"347528924584017923"}} +{"details":{"sequence":"107","creationDate":"2025-11-20T11:50:51.199567Z","resourceOwner":"347528924583952387"}} +{"user":{"id":"347528924584542211","details":{"sequence":"1","creationDate":"2025-11-20T11:49:43.050553Z","changeDate":"2025-11-20T11:49:43.050553Z","resourceOwner":"347528924584017923"},"state":"USER_STATE_ACTIVE","userName":"zitadel-admin-sa","loginNames":["zitadel-admin-sa"],"preferredLoginName":"zitadel-admin-sa","machine":{"name":"Admin"}}} +{"details":{"sequence":"108","changeDate":"2025-11-20T11:50:53.738309Z","resourceOwner":"347528924583952387"}} +{"details":{"sequence":"8","changeDate":"2025-11-20T11:50:54.996326Z","resourceOwner":"347528924584017923"}}