Compare commits

..

No commits in common. "947a1b016ed0b0b31d46c4c773993838ad2a5ac0" and "1c95aa4769169e462280f4806134f1d9b20d4063" have entirely different histories.

19 changed files with 107 additions and 831 deletions

4
.env
View file

@ -1,4 +0,0 @@
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

Binary file not shown.

View file

@ -1,12 +0,0 @@
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

View file

@ -1,290 +0,0 @@
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

292
install-netbird-traefik.sh Executable file → Normal file
View file

@ -2,17 +2,13 @@
set -e
# ============================================
# CONFIG
# ============================================
# Configuration
TRAEFIK_NETWORK="traefik_traefik"
TRAEFIK_CERTRESOLVER="letsencrypt"
NETBIRD_DOMAIN="netbird.rozic-dev.com"
TRAEFIK_CERTRESOLVER="webssl"
NETBIRD_DOMAIN="netbird.domain.com"
export NETBIRD_DOMAIN
# ============================================
# ERROR HANDLING HELPERS
# ============================================
# Error handling functions
handle_request_command_status() {
PARSED_RESPONSE=$1
FUNCTION_NAME=$2
@ -34,9 +30,7 @@ handle_zitadel_request_response() {
sleep 1
}
# ============================================
# DEPENDENCY CHECKS
# ============================================
# Dependency checks
check_jq() {
if ! command -v jq &> /dev/null
then
@ -56,13 +50,12 @@ 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 HELPERS
# ============================================
# Wait functions
wait_pat() {
PAT_PATH=$1
set +e
@ -84,50 +77,40 @@ wait_api() {
counter=1
while true; do
FLAGS="-s"
if [[ $counter -eq 120 ]]; then
if [[ $counter -eq 45 ]]; then
FLAGS="-v"
echo ""
fi
curl $FLAGS --connect-timeout 5 -o /dev/null \
http://localhost:8085/auth/v1/users/me \
-H "Authorization: Bearer $PAT" 2>/dev/null
curl $FLAGS --fail --connect-timeout 1 -o /dev/null "$INSTANCE_URL/auth/v1/users/me" -H "Authorization: Bearer $PAT"
if [[ $? -eq 0 ]]; then
break
fi
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
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"
exit 1
fi
echo -n " ."
sleep 2
sleep 1
counter=$((counter + 1))
done
echo " done"
set -e
}
# ============================================
# ZITADEL API HELPERS
# ============================================
# Zitadel API functions
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"
@ -136,13 +119,12 @@ create_new_project() {
create_new_application() {
INSTANCE_URL=$1
PAT=$2
PROJECT_ID=$3
APPLICATION_NAME=$4
BASE_REDIRECT_URL1=$5
BASE_REDIRECT_URL2=$6
LOGOUT_URL=$7
ZITADEL_DEV_MODE=$8
DEVICE_CODE=$9
APPLICATION_NAME=$3
BASE_REDIRECT_URL1=$4
BASE_REDIRECT_URL2=$5
LOGOUT_URL=$6
ZITADEL_DEV_MODE=$7
DEVICE_CODE=$8
if [[ $DEVICE_CODE == "true" ]]; then
GRANT_TYPES='["OIDC_GRANT_TYPE_AUTHORIZATION_CODE","OIDC_GRANT_TYPE_DEVICE_CODE","OIDC_GRANT_TYPE_REFRESH_TOKEN"]'
@ -177,16 +159,6 @@ 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"
@ -195,6 +167,7 @@ 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" \
@ -206,14 +179,6 @@ 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"
@ -223,20 +188,13 @@ 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')
@ -247,6 +205,7 @@ 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" \
@ -258,14 +217,6 @@ 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"
@ -294,14 +245,6 @@ 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"
@ -311,6 +254,7 @@ 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" \
@ -322,14 +266,6 @@ 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"
@ -338,57 +274,34 @@ 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"
}
# ============================================
# TURN IP
# ============================================
# Get external IP for TURN server
get_turn_external_ip() {
TURN_EXTERNAL_IP_CONFIG="#external-ip="
IP=$(curl -s -4 https://jsonip.com | jq -r '.ip')
@ -398,18 +311,15 @@ get_turn_external_ip() {
echo "$TURN_EXTERNAL_IP_CONFIG"
}
# ============================================
# MAIN
# ============================================
# Main initialization function
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:"
@ -419,7 +329,7 @@ main() {
exit 1
fi
# === Generate passwords & secrets ===
# Generate passwords and 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')@"
@ -437,7 +347,7 @@ main() {
echo "Generating configuration files..."
# === zitadel.env ===
# Generate zitadel.env
cat > zitadel.env <<EOF
ZITADEL_LOG_LEVEL=debug
ZITADEL_MASTERKEY=$ZITADEL_MASTERKEY
@ -462,13 +372,13 @@ ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE=disable
NETBIRD_DOMAIN=$NETBIRD_DOMAIN
EOF
# === zdb.env ===
# Generate zdb.env
cat > zdb.env <<EOF
POSTGRES_USER=root
POSTGRES_PASSWORD=$POSTGRES_ROOT_PASSWORD
EOF
# === turnserver.conf ===
# Generate turnserver.conf
cat > turnserver.conf <<EOF
listening-port=3478
$TURN_EXTERNAL_IP_CONFIG
@ -487,7 +397,7 @@ pidfile="/var/tmp/turnserver.pid"
no-cli
EOF
# === relay.env ===
# Generate relay.env
cat > relay.env <<EOF
NB_LOG_LEVEL=info
NB_LISTEN_ADDRESS=:33080
@ -496,11 +406,13 @@ NB_AUTH_SECRET=$NETBIRD_RELAY_AUTH_SECRET
NETBIRD_DOMAIN=$NETBIRD_DOMAIN
EOF
# Temporary empty files
# Create temporary empty files
echo "" > dashboard.env
echo "" > management.json
# === docker-compose.yml template ===
# Generate docker-compose.yml
cat > docker-compose.yml <<'EOF'
services:
# UI dashboard
@ -514,10 +426,10 @@ services:
- ./dashboard.env
labels:
- traefik.enable=true
- traefik.docker.network=traefik_default
- traefik.docker.network=traefik_traefik
- 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=websecure
- traefik.http.routers.netbird-dashboard.entrypoints=https
- traefik.http.routers.netbird-dashboard.tls=true
- traefik.http.routers.netbird-dashboard.tls.certresolver=NETBIRD_TRAEFIK_SSL
- traefik.http.routers.netbird-dashboard.priority=50
@ -536,23 +448,14 @@ services:
- traefik_traefik
labels:
- traefik.enable=true
- traefik.docker.network=traefik_default
- traefik.docker.network=traefik_traefik
- 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=websecure
- traefik.http.routers.netbird-signal.service=netbird-signal
- traefik.http.routers.netbird-signal.entrypoints=https
- traefik.http.routers.netbird-signal.tls=true
- traefik.http.routers.netbird-signal.tls.certresolver=NETBIRD_TRAEFIK_SSL
- 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_DOMAIN_PLACEHOLDER`) && 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=NETBIRD_TRAEFIK_SSL
- traefik.http.routers.netbird-signal-ws.priority=300
logging:
driver: "json-file"
options:
@ -570,10 +473,10 @@ services:
- ./relay.env
labels:
- traefik.enable=true
- traefik.docker.network=traefik_default
- traefik.docker.network=traefik_traefik
- 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=websecure
- traefik.http.routers.netbird-relay.entrypoints=https
- traefik.http.routers.netbird-relay.tls=true
- traefik.http.routers.netbird-relay.tls.certresolver=NETBIRD_TRAEFIK_SSL
- traefik.http.routers.netbird-relay.priority=200
@ -604,31 +507,24 @@ services:
]
labels:
- traefik.enable=true
- traefik.docker.network=traefik_default
- traefik.docker.network=traefik_traefik
- 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=websecure
- traefik.http.routers.netbird-api.entrypoints=https
- 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=websecure
- traefik.http.routers.netbird-management-grpc.entrypoints=https
- 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
- traefik.http.routers.netbird-management-ws.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && 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=NETBIRD_TRAEFIK_SSL
- traefik.http.routers.netbird-management-ws.priority=300
logging:
driver: "json-file"
options:
@ -653,18 +549,10 @@ services:
# Zitadel - identity provider
zitadel:
restart: 'always'
image: 'ghcr.io/zitadel/zitadel:v2.59.3'
image: 'ghcr.io/zitadel/zitadel:v2.64.1'
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'
@ -676,84 +564,85 @@ services:
- traefik_traefik
labels:
- traefik.enable=true
- traefik.docker.network=traefik_default
- traefik.docker.network=traefik_traefik
- 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=websecure
- traefik.http.routers.zitadel-wellknown.entrypoints=https
- 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=websecure
- traefik.http.routers.zitadel-oauth.entrypoints=https
- 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=websecure
- traefik.http.routers.zitadel-oidc.entrypoints=https
- 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=websecure
- traefik.http.routers.zitadel-ui.entrypoints=https
- 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=websecure
- traefik.http.routers.zitadel-device.entrypoints=https
- 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=websecure
- traefik.http.routers.zitadel-mgmt.entrypoints=https
- 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=websecure
- traefik.http.routers.zitadel-admin.entrypoints=https
- 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
# 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=websecure
# Endpoints gRPC Zitadel
- traefik.http.routers.zitadel-grpc-auth.rule=Host(`netbird.yblis.fr`) && PathPrefix(`/zitadel.auth.v1.AuthService/`)
- traefik.http.routers.zitadel-grpc-auth.entrypoints=https
- 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=websecure
- traefik.http.routers.zitadel-grpc-auth.tls.certresolver=webssl
- traefik.http.routers.zitadel-grpc-admin.rule=Host(`netbird.yblis.fr`) && PathPrefix(`/zitadel.admin.v1.AdminService/`)
- traefik.http.routers.zitadel-grpc-admin.entrypoints=https
- 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=websecure
- traefik.http.routers.zitadel-grpc-admin.tls.certresolver=webssl
- traefik.http.routers.zitadel-grpc-mgmt.rule=Host(`netbird.yblis.fr`) && PathPrefix(`/zitadel.management.v1.ManagementService/`)
- traefik.http.routers.zitadel-grpc-mgmt.entrypoints=https
- 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
- traefik.http.routers.zitadel-grpc-mgmt.tls.certresolver=webssl
logging:
driver: "json-file"
options:
@ -764,13 +653,13 @@ services:
zdb:
restart: 'always'
networks: [netbird]
image: 'postgres:15-alpine'
image: 'postgres:16-alpine'
env_file:
- ./zdb.env
volumes:
- netbird_zdb_data:/var/lib/postgresql/data:rw
healthcheck:
test: ["CMD-SHELL", "pg_isready -U root"]
test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"]
interval: 5s
timeout: 60s
retries: 10
@ -791,13 +680,10 @@ networks:
driver: bridge
traefik_traefik:
external: true
name: traefik_default
EOF
# Replace placeholders
sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/${TRAEFIK_CERTRESOLVER}/g" docker-compose.yml
# Create machinekey dir
# Create machinekey directory
mkdir -p machinekey
chmod 777 machinekey
@ -813,6 +699,7 @@ EOF
echo "Waiting for Zitadel to initialize..."
sleep 60
# Configuration automatique de Zitadel
echo "Configuring Zitadel applications..."
INSTANCE_URL="https://$NETBIRD_DOMAIN"
@ -822,7 +709,7 @@ EOF
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
@ -831,24 +718,29 @@ EOF
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" "$PROJECT_ID" "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" "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" "$PROJECT_ID" "Cli" "http://localhost:53000/" "http://localhost:54000/" "http://localhost:53000/" "true" "true")
CLI_APPLICATION_CLIENT_ID=$(create_new_application "$INSTANCE_URL" "$PAT" "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
@ -856,6 +748,7 @@ EOF
echo "Please remove it manually"
fi
# Generate NetBird configuration
echo "Generating NetBird configuration..."
# dashboard.env
@ -956,4 +849,5 @@ EOF
echo "Note: The admin password will require changing on first login."
}
# Execute main function
main "$@"

View file

@ -1 +0,0 @@
tlqWhCwoB4bEoKhqlbw_mHr8qGxjuJVdjfYGH70-Tr1bwzGu0Q5FNUMiostaLXoAk5GvCXc

View file

@ -1,112 +0,0 @@
{
"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
}

View file

@ -1,4 +1,4 @@
# 🚀 Deploy Netbird & Zitadel with Traefik
# 🚀 Deploy Netbird with Traefik
This script helps you deploy **Netbird** behind a **Traefik** reverse proxy. It's designed for users who already have a working Traefik stack. The integration relies solely on Traefik labels—no need to modify your existing setup.
@ -13,7 +13,7 @@ cd netbird-traefik
---
## ⚙️ Configurations
## ⚙️ Configuration
Before running the script, edit the following variables inside `install-netbird-traefik.sh`:

View file

@ -1,17 +0,0 @@
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

View file

@ -1,40 +0,0 @@
#!/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! 🚀"

View file

@ -1,5 +0,0 @@
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

View file

@ -1 +0,0 @@

View file

@ -1,3 +1,6 @@
# Supprimez la ligne defaultEntryPoints qui est obsolète dans Traefik v2+
# defaultEntryPoints = ["http", "https", "ws", "wss"]
[entryPoints]
[entryPoints.http]
address = ":80"
@ -26,6 +29,6 @@
permanent = true
sslRedirect = true
# Autorize TSL backend
# [serversTransport]
# insecureSkipVerify = true
# Autorise les backend TLS
[serversTransport]
insecureSkipVerify = true

View file

@ -1,89 +0,0 @@
# Traefik Reverse Proxy Setup
A simple Docker Compose setup for Traefik reverse proxy with automatic HTTPS certificates.
## Quick Start
1. **Clone and configure**
```bash
git clone https://github.com/yblis/netbird-traefik
cd netbird-traefil/traefik-setup
```
2. **Edit configuration files**
- In `docker-compose.yml`: Replace `traefik.domain.com` with your domain
- In `data/traefik.toml`: Replace `admin@domain.com` with your email
- In `docker-compose.yml`: Replace `YOURHASHBASICPASSWORD` with your hashed password
3. **Set up DNS**
- Create an A record pointing your domain to your server IP
- Example: `traefik.yourdomain.com``your.server.ip`
4. **Create required files**
```bash
mkdir -p data
touch data/acme.json
chmod 600 data/acme.json
```
5. **Start Traefik**
```bash
docker-compose up -d
```
## Configuration Details
### Domain Setup
Replace these values in the configuration files:
- `traefik.domain.com` → Your actual domain
- `admin@domain.com` → Your email for Let's Encrypt
- `YOURHASHBASICPASSWORD` → Your hashed password (see below)
### Generate Password Hash
```bash
echo $(htpasswd -nb admin yourpassword) | sed -e s/\\$/\\$\\$/g
```
### Access Points
- **Traefik Dashboard**: `https://traefik.yourdomain.com`
- **HTTP**: Port 80 (redirects to HTTPS)
- **HTTPS**: Port 443
- **Dashboard Direct**: Port 8001 (HTTP only)
## Features
- ✅ Automatic HTTPS with Let's Encrypt
- ✅ HTTP to HTTPS redirection
- ✅ Basic authentication for dashboard
- ✅ Docker integration
- ✅ File-based service configuration
## File Structure
```
.
├── docker-compose.yml
└── data/
├── traefik.toml
├── services.toml
└── acme.json
```
## Adding Services
To add a new service behind Traefik, add these labels to your service in docker-compose:
```yaml
labels:
- "traefik.http.routers.myapp.rule=Host(`myapp.yourdomain.com`)"
- "traefik.http.routers.myapp.entrypoints=https"
- "traefik.http.routers.myapp.tls.certresolver=webssl"
- "traefik.http.services.myapp.loadbalancer.server.port=80"
```
## Troubleshooting
- Check logs: `docker-compose logs traefik`
- Verify DNS resolution: `nslookup traefik.yourdomain.com`
- Ensure ports 80 and 443 are open
- Check acme.json permissions: `ls -la data/acme.json` (should be 600)

View file

@ -1,15 +0,0 @@
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

View file

@ -1 +0,0 @@
sudo docker-compose down -v && sudo rm -R machinekey/ .env *.env *.yml *.json *.conf

View file

@ -1,2 +0,0 @@
POSTGRES_USER=root
POSTGRES_PASSWORD=WuNCCFl9IrF4x1YOvUb5g0297qf4CE5qKPqlwY3HFn0@

View file

@ -1,21 +0,0 @@
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

View file

@ -1,11 +0,0 @@
{"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"}}