Compare commits

...

10 commits

Author SHA1 Message Date
Dejan R. 947a1b016e first commitđ 2025-11-23 18:38:45 +00:00
yblis 4084a08b32
Update label traefik websocket for RDP & SSH 2025-10-07 16:04:45 +02:00
yblis 8a16ce0b71
Update install-netbird-traefik.sh 2025-09-08 10:59:14 +02:00
yblis 31a7207d39
Update uninstall-netbird-traefik.sh 2025-09-07 17:57:52 +02:00
yblis 9c358de605
Update install-netbird-traefik.sh 2025-09-07 17:54:17 +02:00
yblis d9e514ec3e
Create uninstall-netbird-traefik.sh 2025-09-07 17:51:41 +02:00
yblis a6a1151c2c
Update readme.md 2025-09-07 15:52:28 +02:00
yblis 93e62529ed
Update traefik.toml 2025-09-07 15:49:00 +02:00
yblis 5364c40610
Create readme.md 2025-09-07 15:43:46 +02:00
yblis de674d7891
Create acme.json 2025-09-07 15:37:34 +02:00
19 changed files with 831 additions and 107 deletions

4
.env Normal file
View file

@ -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

Binary file not shown.

12
dashboard.env Normal file
View file

@ -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

290
docker-compose.yml Normal file
View file

@ -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

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

@ -2,13 +2,17 @@
set -e set -e
# Configuration # ============================================
# CONFIG
# ============================================
TRAEFIK_NETWORK="traefik_traefik" TRAEFIK_NETWORK="traefik_traefik"
TRAEFIK_CERTRESOLVER="webssl" TRAEFIK_CERTRESOLVER="letsencrypt"
NETBIRD_DOMAIN="netbird.domain.com" NETBIRD_DOMAIN="netbird.rozic-dev.com"
export NETBIRD_DOMAIN export NETBIRD_DOMAIN
# Error handling functions # ============================================
# ERROR HANDLING HELPERS
# ============================================
handle_request_command_status() { handle_request_command_status() {
PARSED_RESPONSE=$1 PARSED_RESPONSE=$1
FUNCTION_NAME=$2 FUNCTION_NAME=$2
@ -30,7 +34,9 @@ handle_zitadel_request_response() {
sleep 1 sleep 1
} }
# Dependency checks # ============================================
# DEPENDENCY CHECKS
# ============================================
check_jq() { check_jq() {
if ! command -v jq &> /dev/null if ! command -v jq &> /dev/null
then then
@ -50,12 +56,13 @@ check_docker_compose() {
echo "docker compose" echo "docker compose"
return return
fi 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 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 exit 1
} }
# Wait functions # ============================================
# WAIT HELPERS
# ============================================
wait_pat() { wait_pat() {
PAT_PATH=$1 PAT_PATH=$1
set +e set +e
@ -77,40 +84,50 @@ wait_api() {
counter=1 counter=1
while true; do while true; do
FLAGS="-s" FLAGS="-s"
if [[ $counter -eq 45 ]]; then if [[ $counter -eq 120 ]]; then
FLAGS="-v" FLAGS="-v"
echo "" echo ""
fi fi
curl $FLAGS --connect-timeout 5 -o /dev/null \
curl $FLAGS --fail --connect-timeout 1 -o /dev/null "$INSTANCE_URL/auth/v1/users/me" -H "Authorization: Bearer $PAT" http://localhost:8085/auth/v1/users/me \
-H "Authorization: Bearer $PAT" 2>/dev/null
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
break break
fi fi
if [[ $counter -eq 45 ]]; then if [[ $counter -eq 120 ]]; then
echo "" echo "Zitadel still not ready via http://localhost:8085 after 120s checking logs..."
echo "Unable to connect to Zitadel for more than 45s, please check the output above, your firewall rules and container logs" docker logs netbird-compose-zitadel-1 --tail 50
exit 1 exit 1
fi fi
echo -n " ." echo -n " ."
sleep 1 sleep 2
counter=$((counter + 1)) counter=$((counter + 1))
done done
echo " done" echo " done"
set -e set -e
} }
# Zitadel API functions # ============================================
# ZITADEL API HELPERS
# ============================================
create_new_project() { create_new_project() {
INSTANCE_URL=$1 INSTANCE_URL=$1
PAT=$2 PAT=$2
PROJECT_NAME="NETBIRD" PROJECT_NAME="NETBIRD"
RESPONSE=$( RESPONSE=$(
curl -sS -X POST "$INSTANCE_URL/management/v1/projects" \ curl -sS -X POST "$INSTANCE_URL/management/v1/projects" \
-H "Authorization: Bearer $PAT" \ -H "Authorization: Bearer $PAT" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"name": "'"$PROJECT_NAME"'"}' -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') PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.id')
handle_zitadel_request_response "$PARSED_RESPONSE" "create_new_project" "$RESPONSE" handle_zitadel_request_response "$PARSED_RESPONSE" "create_new_project" "$RESPONSE"
echo "$PARSED_RESPONSE" echo "$PARSED_RESPONSE"
@ -119,12 +136,13 @@ create_new_project() {
create_new_application() { create_new_application() {
INSTANCE_URL=$1 INSTANCE_URL=$1
PAT=$2 PAT=$2
APPLICATION_NAME=$3 PROJECT_ID=$3
BASE_REDIRECT_URL1=$4 APPLICATION_NAME=$4
BASE_REDIRECT_URL2=$5 BASE_REDIRECT_URL1=$5
LOGOUT_URL=$6 BASE_REDIRECT_URL2=$6
ZITADEL_DEV_MODE=$7 LOGOUT_URL=$7
DEVICE_CODE=$8 ZITADEL_DEV_MODE=$8
DEVICE_CODE=$9
if [[ $DEVICE_CODE == "true" ]]; then if [[ $DEVICE_CODE == "true" ]]; then
GRANT_TYPES='["OIDC_GRANT_TYPE_AUTHORIZATION_CODE","OIDC_GRANT_TYPE_DEVICE_CODE","OIDC_GRANT_TYPE_REFRESH_TOKEN"]' GRANT_TYPES='["OIDC_GRANT_TYPE_AUTHORIZATION_CODE","OIDC_GRANT_TYPE_DEVICE_CODE","OIDC_GRANT_TYPE_REFRESH_TOKEN"]'
@ -159,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') PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.clientId')
handle_zitadel_request_response "$PARSED_RESPONSE" "create_new_application" "$RESPONSE" handle_zitadel_request_response "$PARSED_RESPONSE" "create_new_application" "$RESPONSE"
echo "$PARSED_RESPONSE" echo "$PARSED_RESPONSE"
@ -167,7 +195,6 @@ create_new_application() {
create_service_user() { create_service_user() {
INSTANCE_URL=$1 INSTANCE_URL=$1
PAT=$2 PAT=$2
RESPONSE=$( RESPONSE=$(
curl -sS -X POST "$INSTANCE_URL/management/v1/users/machine" \ curl -sS -X POST "$INSTANCE_URL/management/v1/users/machine" \
-H "Authorization: Bearer $PAT" \ -H "Authorization: Bearer $PAT" \
@ -179,6 +206,14 @@ create_service_user() {
"accessTokenType": "ACCESS_TOKEN_TYPE_JWT" "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') PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.userId')
handle_zitadel_request_response "$PARSED_RESPONSE" "create_service_user" "$RESPONSE" handle_zitadel_request_response "$PARSED_RESPONSE" "create_service_user" "$RESPONSE"
echo "$PARSED_RESPONSE" echo "$PARSED_RESPONSE"
@ -188,13 +223,20 @@ create_service_user_secret() {
INSTANCE_URL=$1 INSTANCE_URL=$1
PAT=$2 PAT=$2
USER_ID=$3 USER_ID=$3
RESPONSE=$( RESPONSE=$(
curl -sS -X PUT "$INSTANCE_URL/management/v1/users/$USER_ID/secret" \ curl -sS -X PUT "$INSTANCE_URL/management/v1/users/$USER_ID/secret" \
-H "Authorization: Bearer $PAT" \ -H "Authorization: Bearer $PAT" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{}' -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') SERVICE_USER_CLIENT_ID=$(echo "$RESPONSE" | jq -r '.clientId')
handle_zitadel_request_response "$SERVICE_USER_CLIENT_ID" "create_service_user_secret_id" "$RESPONSE" handle_zitadel_request_response "$SERVICE_USER_CLIENT_ID" "create_service_user_secret_id" "$RESPONSE"
SERVICE_USER_CLIENT_SECRET=$(echo "$RESPONSE" | jq -r '.clientSecret') SERVICE_USER_CLIENT_SECRET=$(echo "$RESPONSE" | jq -r '.clientSecret')
@ -205,7 +247,6 @@ add_organization_user_manager() {
INSTANCE_URL=$1 INSTANCE_URL=$1
PAT=$2 PAT=$2
USER_ID=$3 USER_ID=$3
RESPONSE=$( RESPONSE=$(
curl -sS -X POST "$INSTANCE_URL/management/v1/orgs/me/members" \ curl -sS -X POST "$INSTANCE_URL/management/v1/orgs/me/members" \
-H "Authorization: Bearer $PAT" \ -H "Authorization: Bearer $PAT" \
@ -217,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') PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.details.creationDate')
handle_zitadel_request_response "$PARSED_RESPONSE" "add_organization_user_manager" "$RESPONSE" handle_zitadel_request_response "$PARSED_RESPONSE" "add_organization_user_manager" "$RESPONSE"
echo "$PARSED_RESPONSE" echo "$PARSED_RESPONSE"
@ -245,6 +294,14 @@ create_admin_user() {
"passwordChangeRequired": true "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') PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.userId')
handle_zitadel_request_response "$PARSED_RESPONSE" "create_admin_user" "$RESPONSE" handle_zitadel_request_response "$PARSED_RESPONSE" "create_admin_user" "$RESPONSE"
echo "$PARSED_RESPONSE" echo "$PARSED_RESPONSE"
@ -254,7 +311,6 @@ add_instance_admin() {
INSTANCE_URL=$1 INSTANCE_URL=$1
PAT=$2 PAT=$2
USER_ID=$3 USER_ID=$3
RESPONSE=$( RESPONSE=$(
curl -sS -X POST "$INSTANCE_URL/admin/v1/members" \ curl -sS -X POST "$INSTANCE_URL/admin/v1/members" \
-H "Authorization: Bearer $PAT" \ -H "Authorization: Bearer $PAT" \
@ -266,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') PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.details.creationDate')
handle_zitadel_request_response "$PARSED_RESPONSE" "add_instance_admin" "$RESPONSE" handle_zitadel_request_response "$PARSED_RESPONSE" "add_instance_admin" "$RESPONSE"
echo "$PARSED_RESPONSE" echo "$PARSED_RESPONSE"
@ -274,34 +338,57 @@ add_instance_admin() {
delete_auto_service_user() { delete_auto_service_user() {
INSTANCE_URL=$1 INSTANCE_URL=$1
PAT=$2 PAT=$2
RESPONSE=$( RESPONSE=$(
curl -sS -X GET "$INSTANCE_URL/auth/v1/users/me" \ curl -sS -X GET "$INSTANCE_URL/auth/v1/users/me" \
-H "Authorization: Bearer $PAT" \ -H "Authorization: Bearer $PAT" \
-H "Content-Type: application/json" \ -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') USER_ID=$(echo "$RESPONSE" | jq -r '.user.id')
handle_zitadel_request_response "$USER_ID" "delete_auto_service_user_get_user" "$RESPONSE" handle_zitadel_request_response "$USER_ID" "delete_auto_service_user_get_user" "$RESPONSE"
RESPONSE=$( RESPONSE=$(
curl -sS -X DELETE "$INSTANCE_URL/admin/v1/members/$USER_ID" \ curl -sS -X DELETE "$INSTANCE_URL/admin/v1/members/$USER_ID" \
-H "Authorization: Bearer $PAT" \ -H "Authorization: Bearer $PAT" \
-H "Content-Type: application/json" \ -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') PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.details.changeDate')
handle_zitadel_request_response "$PARSED_RESPONSE" "delete_auto_service_user_remove_instance_permissions" "$RESPONSE" handle_zitadel_request_response "$PARSED_RESPONSE" "delete_auto_service_user_remove_instance_permissions" "$RESPONSE"
RESPONSE=$( RESPONSE=$(
curl -sS -X DELETE "$INSTANCE_URL/management/v1/orgs/me/members/$USER_ID" \ curl -sS -X DELETE "$INSTANCE_URL/management/v1/orgs/me/members/$USER_ID" \
-H "Authorization: Bearer $PAT" \ -H "Authorization: Bearer $PAT" \
-H "Content-Type: application/json" \ -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') PARSED_RESPONSE=$(echo "$RESPONSE" | jq -r '.details.changeDate')
handle_zitadel_request_response "$PARSED_RESPONSE" "delete_auto_service_user_remove_org_permissions" "$RESPONSE" handle_zitadel_request_response "$PARSED_RESPONSE" "delete_auto_service_user_remove_org_permissions" "$RESPONSE"
echo "$PARSED_RESPONSE" echo "$PARSED_RESPONSE"
} }
# Get external IP for TURN server # ============================================
# TURN IP
# ============================================
get_turn_external_ip() { get_turn_external_ip() {
TURN_EXTERNAL_IP_CONFIG="#external-ip=" TURN_EXTERNAL_IP_CONFIG="#external-ip="
IP=$(curl -s -4 https://jsonip.com | jq -r '.ip') IP=$(curl -s -4 https://jsonip.com | jq -r '.ip')
@ -311,15 +398,18 @@ get_turn_external_ip() {
echo "$TURN_EXTERNAL_IP_CONFIG" echo "$TURN_EXTERNAL_IP_CONFIG"
} }
# Main initialization function # ============================================
# MAIN
# ============================================
main() { main() {
# Clear log file
> zitadel_api.log
echo "Initializing NetBird with Traefik..." echo "Initializing NetBird with Traefik..."
# Check dependencies
check_jq check_jq
DOCKER_COMPOSE_COMMAND=$(check_docker_compose) DOCKER_COMPOSE_COMMAND=$(check_docker_compose)
# Check if files already exist
if [ -f zitadel.env ]; then if [ -f zitadel.env ]; then
echo "Generated files already exist, if you want to reinitialize the environment, please remove them first." echo "Generated files already exist, if you want to reinitialize the environment, please remove them first."
echo "You can use the following commands:" echo "You can use the following commands:"
@ -329,7 +419,7 @@ main() {
exit 1 exit 1
fi fi
# Generate passwords and secrets # === Generate passwords & secrets ===
ZITADEL_MASTERKEY="$(openssl rand -base64 32 | head -c 32)" ZITADEL_MASTERKEY="$(openssl rand -base64 32 | head -c 32)"
POSTGRES_ROOT_PASSWORD="$(openssl rand -base64 32 | sed 's/=//g')@" POSTGRES_ROOT_PASSWORD="$(openssl rand -base64 32 | sed 's/=//g')@"
POSTGRES_ZITADEL_PASSWORD="$(openssl rand -base64 32 | sed 's/=//g')@" POSTGRES_ZITADEL_PASSWORD="$(openssl rand -base64 32 | sed 's/=//g')@"
@ -347,7 +437,7 @@ main() {
echo "Generating configuration files..." echo "Generating configuration files..."
# Generate zitadel.env # === zitadel.env ===
cat > zitadel.env <<EOF cat > zitadel.env <<EOF
ZITADEL_LOG_LEVEL=debug ZITADEL_LOG_LEVEL=debug
ZITADEL_MASTERKEY=$ZITADEL_MASTERKEY ZITADEL_MASTERKEY=$ZITADEL_MASTERKEY
@ -372,13 +462,13 @@ ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE=disable
NETBIRD_DOMAIN=$NETBIRD_DOMAIN NETBIRD_DOMAIN=$NETBIRD_DOMAIN
EOF EOF
# Generate zdb.env # === zdb.env ===
cat > zdb.env <<EOF cat > zdb.env <<EOF
POSTGRES_USER=root POSTGRES_USER=root
POSTGRES_PASSWORD=$POSTGRES_ROOT_PASSWORD POSTGRES_PASSWORD=$POSTGRES_ROOT_PASSWORD
EOF EOF
# Generate turnserver.conf # === turnserver.conf ===
cat > turnserver.conf <<EOF cat > turnserver.conf <<EOF
listening-port=3478 listening-port=3478
$TURN_EXTERNAL_IP_CONFIG $TURN_EXTERNAL_IP_CONFIG
@ -397,7 +487,7 @@ pidfile="/var/tmp/turnserver.pid"
no-cli no-cli
EOF EOF
# Generate relay.env # === relay.env ===
cat > relay.env <<EOF cat > relay.env <<EOF
NB_LOG_LEVEL=info NB_LOG_LEVEL=info
NB_LISTEN_ADDRESS=:33080 NB_LISTEN_ADDRESS=:33080
@ -406,13 +496,11 @@ NB_AUTH_SECRET=$NETBIRD_RELAY_AUTH_SECRET
NETBIRD_DOMAIN=$NETBIRD_DOMAIN NETBIRD_DOMAIN=$NETBIRD_DOMAIN
EOF EOF
# Temporary empty files
# Create temporary empty files
echo "" > dashboard.env echo "" > dashboard.env
echo "" > management.json echo "" > management.json
# Generate docker-compose.yml # === docker-compose.yml template ===
cat > docker-compose.yml <<'EOF' cat > docker-compose.yml <<'EOF'
services: services:
# UI dashboard # UI dashboard
@ -426,10 +514,10 @@ services:
- ./dashboard.env - ./dashboard.env
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.docker.network=traefik_traefik - traefik.docker.network=traefik_default
- traefik.http.services.netbird-dashboard.loadbalancer.server.port=80 - traefik.http.services.netbird-dashboard.loadbalancer.server.port=80
- traefik.http.routers.netbird-dashboard.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) - 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=true
- traefik.http.routers.netbird-dashboard.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.netbird-dashboard.tls.certresolver=NETBIRD_TRAEFIK_SSL
- traefik.http.routers.netbird-dashboard.priority=50 - traefik.http.routers.netbird-dashboard.priority=50
@ -448,14 +536,23 @@ services:
- traefik_traefik - traefik_traefik
labels: labels:
- traefik.enable=true - 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.port=10000
- traefik.http.services.netbird-signal.loadbalancer.server.scheme=h2c - 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.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=true
- traefik.http.routers.netbird-signal.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.netbird-signal.tls.certresolver=NETBIRD_TRAEFIK_SSL
- traefik.http.routers.netbird-signal.priority=200 - 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: logging:
driver: "json-file" driver: "json-file"
options: options:
@ -473,10 +570,10 @@ services:
- ./relay.env - ./relay.env
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.docker.network=traefik_traefik - traefik.docker.network=traefik_default
- traefik.http.services.netbird-relay.loadbalancer.server.port=33080 - 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.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=true
- traefik.http.routers.netbird-relay.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.netbird-relay.tls.certresolver=NETBIRD_TRAEFIK_SSL
- traefik.http.routers.netbird-relay.priority=200 - traefik.http.routers.netbird-relay.priority=200
@ -507,24 +604,31 @@ services:
] ]
labels: labels:
- traefik.enable=true - 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.loadbalancer.server.port=80
- traefik.http.services.netbird-management-grpc.loadbalancer.server.port=80 - traefik.http.services.netbird-management-grpc.loadbalancer.server.port=80
- traefik.http.services.netbird-management-grpc.loadbalancer.server.scheme=h2c - traefik.http.services.netbird-management-grpc.loadbalancer.server.scheme=h2c
# REST API # REST API
- traefik.http.routers.netbird-api.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/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.service=netbird-management
- traefik.http.routers.netbird-api.tls=true - traefik.http.routers.netbird-api.tls=true
- traefik.http.routers.netbird-api.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.netbird-api.tls.certresolver=NETBIRD_TRAEFIK_SSL
- traefik.http.routers.netbird-api.priority=200 - traefik.http.routers.netbird-api.priority=200
# gRPC # gRPC
- traefik.http.routers.netbird-management-grpc.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/management.ManagementService/`) - 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.service=netbird-management-grpc
- traefik.http.routers.netbird-management-grpc.tls=true - 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.tls.certresolver=NETBIRD_TRAEFIK_SSL
- traefik.http.routers.netbird-management-grpc.priority=200 - 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: logging:
driver: "json-file" driver: "json-file"
options: options:
@ -549,10 +653,18 @@ services:
# Zitadel - identity provider # Zitadel - identity provider
zitadel: zitadel:
restart: 'always' 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' command: 'start-from-init --masterkeyFromEnv --tlsMode external'
ports:
- "8085:8080" # <-- added
env_file: env_file:
- ./zitadel.env - ./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: depends_on:
zdb: zdb:
condition: 'service_healthy' condition: 'service_healthy'
@ -564,85 +676,84 @@ services:
- traefik_traefik - traefik_traefik
labels: labels:
- traefik.enable=true - 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.port=8080
- traefik.http.services.zitadel.loadbalancer.server.scheme=h2c
# OIDC wellknown # OIDC wellknown
- traefik.http.routers.zitadel-wellknown.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/.well-known`) - 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.service=zitadel
- traefik.http.routers.zitadel-wellknown.priority=300 - traefik.http.routers.zitadel-wellknown.priority=300
- traefik.http.routers.zitadel-wellknown.tls=true - traefik.http.routers.zitadel-wellknown.tls=true
- traefik.http.routers.zitadel-wellknown.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.zitadel-wellknown.tls.certresolver=NETBIRD_TRAEFIK_SSL
# OAuth # OAuth
- traefik.http.routers.zitadel-oauth.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/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.service=zitadel
- traefik.http.routers.zitadel-oauth.priority=300 - traefik.http.routers.zitadel-oauth.priority=300
- traefik.http.routers.zitadel-oauth.tls=true - traefik.http.routers.zitadel-oauth.tls=true
- traefik.http.routers.zitadel-oauth.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.zitadel-oauth.tls.certresolver=NETBIRD_TRAEFIK_SSL
# OIDC # OIDC
- traefik.http.routers.zitadel-oidc.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/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.service=zitadel
- traefik.http.routers.zitadel-oidc.priority=300 - traefik.http.routers.zitadel-oidc.priority=300
- traefik.http.routers.zitadel-oidc.tls=true - traefik.http.routers.zitadel-oidc.tls=true
- traefik.http.routers.zitadel-oidc.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.zitadel-oidc.tls.certresolver=NETBIRD_TRAEFIK_SSL
# UI Console # UI Console
- traefik.http.routers.zitadel-ui.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/ui`) - 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.service=zitadel
- traefik.http.routers.zitadel-ui.priority=300 - traefik.http.routers.zitadel-ui.priority=300
- traefik.http.routers.zitadel-ui.tls=true - traefik.http.routers.zitadel-ui.tls=true
- traefik.http.routers.zitadel-ui.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.zitadel-ui.tls.certresolver=NETBIRD_TRAEFIK_SSL
# Device flow # Device flow
- traefik.http.routers.zitadel-device.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/device`) - 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.service=zitadel
- traefik.http.routers.zitadel-device.priority=300 - traefik.http.routers.zitadel-device.priority=300
- traefik.http.routers.zitadel-device.tls=true - traefik.http.routers.zitadel-device.tls=true
- traefik.http.routers.zitadel-device.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.zitadel-device.tls.certresolver=NETBIRD_TRAEFIK_SSL
# Management API # Management API
- traefik.http.routers.zitadel-mgmt.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/management/v1`) - 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.service=zitadel
- traefik.http.routers.zitadel-mgmt.priority=300 - traefik.http.routers.zitadel-mgmt.priority=300
- traefik.http.routers.zitadel-mgmt.tls=true - traefik.http.routers.zitadel-mgmt.tls=true
- traefik.http.routers.zitadel-mgmt.tls.certresolver=NETBIRD_TRAEFIK_SSL - 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 # Admin API
- traefik.http.routers.zitadel-admin.rule=Host(`NETBIRD_DOMAIN_PLACEHOLDER`) && PathPrefix(`/admin/v1`) - 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.service=zitadel
- traefik.http.routers.zitadel-admin.priority=300 - traefik.http.routers.zitadel-admin.priority=300
- traefik.http.routers.zitadel-admin.tls=true - traefik.http.routers.zitadel-admin.tls=true
- traefik.http.routers.zitadel-admin.tls.certresolver=NETBIRD_TRAEFIK_SSL - traefik.http.routers.zitadel-admin.tls.certresolver=NETBIRD_TRAEFIK_SSL
# Endpoints gRPC Zitadel # gRPC endpoints
- traefik.http.routers.zitadel-grpc-auth.rule=Host(`netbird.yblis.fr`) && PathPrefix(`/zitadel.auth.v1.AuthService/`) - 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.service=zitadel
- traefik.http.routers.zitadel-grpc-auth.priority=400 - traefik.http.routers.zitadel-grpc-auth.priority=400
- traefik.http.routers.zitadel-grpc-auth.tls=true - traefik.http.routers.zitadel-grpc-auth.tls=true
- traefik.http.routers.zitadel-grpc-auth.tls.certresolver=webssl - 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.rule=Host(`netbird.yblis.fr`) && PathPrefix(`/zitadel.admin.v1.AdminService/`) - traefik.http.routers.zitadel-grpc-admin.entrypoints=websecure
- traefik.http.routers.zitadel-grpc-admin.entrypoints=https
- traefik.http.routers.zitadel-grpc-admin.service=zitadel - traefik.http.routers.zitadel-grpc-admin.service=zitadel
- traefik.http.routers.zitadel-grpc-admin.priority=400 - traefik.http.routers.zitadel-grpc-admin.priority=400
- traefik.http.routers.zitadel-grpc-admin.tls=true - traefik.http.routers.zitadel-grpc-admin.tls=true
- traefik.http.routers.zitadel-grpc-admin.tls.certresolver=webssl - 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.rule=Host(`netbird.yblis.fr`) && PathPrefix(`/zitadel.management.v1.ManagementService/`) - traefik.http.routers.zitadel-grpc-mgmt.entrypoints=websecure
- traefik.http.routers.zitadel-grpc-mgmt.entrypoints=https
- traefik.http.routers.zitadel-grpc-mgmt.service=zitadel - traefik.http.routers.zitadel-grpc-mgmt.service=zitadel
- traefik.http.routers.zitadel-grpc-mgmt.priority=400 - traefik.http.routers.zitadel-grpc-mgmt.priority=400
- traefik.http.routers.zitadel-grpc-mgmt.tls=true - traefik.http.routers.zitadel-grpc-mgmt.tls=true
- traefik.http.routers.zitadel-grpc-mgmt.tls.certresolver=webssl - 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: logging:
driver: "json-file" driver: "json-file"
options: options:
@ -653,13 +764,13 @@ services:
zdb: zdb:
restart: 'always' restart: 'always'
networks: [netbird] networks: [netbird]
image: 'postgres:16-alpine' image: 'postgres:15-alpine'
env_file: env_file:
- ./zdb.env - ./zdb.env
volumes: volumes:
- netbird_zdb_data:/var/lib/postgresql/data:rw - netbird_zdb_data:/var/lib/postgresql/data:rw
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"] test: ["CMD-SHELL", "pg_isready -U root"]
interval: 5s interval: 5s
timeout: 60s timeout: 60s
retries: 10 retries: 10
@ -680,10 +791,13 @@ networks:
driver: bridge driver: bridge
traefik_traefik: traefik_traefik:
external: true external: true
name: traefik_default
EOF EOF
# Replace placeholders
sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/${TRAEFIK_CERTRESOLVER}/g" docker-compose.yml sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/${TRAEFIK_CERTRESOLVER}/g" docker-compose.yml
# Create machinekey directory # Create machinekey dir
mkdir -p machinekey mkdir -p machinekey
chmod 777 machinekey chmod 777 machinekey
@ -699,7 +813,6 @@ sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/
echo "Waiting for Zitadel to initialize..." echo "Waiting for Zitadel to initialize..."
sleep 60 sleep 60
# Configuration automatique de Zitadel
echo "Configuring Zitadel applications..." echo "Configuring Zitadel applications..."
INSTANCE_URL="https://$NETBIRD_DOMAIN" INSTANCE_URL="https://$NETBIRD_DOMAIN"
@ -709,7 +822,7 @@ sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/
wait_pat "$TOKEN_PATH" wait_pat "$TOKEN_PATH"
echo "Reading Zitadel PAT" echo "Reading Zitadel PAT"
PAT=$(cat $TOKEN_PATH) PAT=$(cat "$TOKEN_PATH")
if [ "$PAT" = "null" ]; then if [ "$PAT" = "null" ]; then
echo "Failed getting Zitadel PAT" echo "Failed getting Zitadel PAT"
exit 1 exit 1
@ -718,29 +831,24 @@ sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/
echo -n "Waiting for Zitadel to become ready " echo -n "Waiting for Zitadel to become ready "
wait_api "$INSTANCE_URL" "$PAT" wait_api "$INSTANCE_URL" "$PAT"
# Create project
echo "Creating Zitadel project" echo "Creating Zitadel project"
PROJECT_ID=$(create_new_project "$INSTANCE_URL" "$PAT") PROJECT_ID=$(create_new_project "$INSTANCE_URL" "$PAT")
# Create applications
echo "Creating Dashboard application" 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" 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" echo "Creating service user"
MACHINE_USER_ID=$(create_service_user "$INSTANCE_URL" "$PAT") MACHINE_USER_ID=$(create_service_user "$INSTANCE_URL" "$PAT")
create_service_user_secret "$INSTANCE_URL" "$PAT" "$MACHINE_USER_ID" create_service_user_secret "$INSTANCE_URL" "$PAT" "$MACHINE_USER_ID"
add_organization_user_manager "$INSTANCE_URL" "$PAT" "$MACHINE_USER_ID" add_organization_user_manager "$INSTANCE_URL" "$PAT" "$MACHINE_USER_ID"
# Create admin user
echo "Creating admin user" echo "Creating admin user"
HUMAN_USER_ID=$(create_admin_user "$INSTANCE_URL" "$PAT" "$ZITADEL_ADMIN_USERNAME" "$ZITADEL_ADMIN_PASSWORD") HUMAN_USER_ID=$(create_admin_user "$INSTANCE_URL" "$PAT" "$ZITADEL_ADMIN_USERNAME" "$ZITADEL_ADMIN_PASSWORD")
add_instance_admin "$INSTANCE_URL" "$PAT" "$HUMAN_USER_ID" add_instance_admin "$INSTANCE_URL" "$PAT" "$HUMAN_USER_ID"
# Clean up auto service user
echo "Cleaning up auto service user" echo "Cleaning up auto service user"
DATE=$(delete_auto_service_user "$INSTANCE_URL" "$PAT") DATE=$(delete_auto_service_user "$INSTANCE_URL" "$PAT")
if [ "$DATE" = "null" ]; then if [ "$DATE" = "null" ]; then
@ -748,7 +856,6 @@ sed -i "s/NETBIRD_DOMAIN_PLACEHOLDER/${NETBIRD_DOMAIN}/g; s/NETBIRD_TRAEFIK_SSL/
echo "Please remove it manually" echo "Please remove it manually"
fi fi
# Generate NetBird configuration
echo "Generating NetBird configuration..." echo "Generating NetBird configuration..."
# dashboard.env # dashboard.env
@ -849,5 +956,4 @@ EOF
echo "Note: The admin password will require changing on first login." echo "Note: The admin password will require changing on first login."
} }
# Execute main function
main "$@" main "$@"

View file

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

112
management.json Normal file
View file

@ -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
}

View file

@ -1,4 +1,4 @@
# 🚀 Deploy Netbird with Traefik # 🚀 Deploy Netbird & Zitadel 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. 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
--- ---
## ⚙️ Configuration ## ⚙️ Configurations
Before running the script, edit the following variables inside `install-netbird-traefik.sh`: Before running the script, edit the following variables inside `install-netbird-traefik.sh`:

17
reinit.sh Executable file
View file

@ -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

40
reinit1.sh Executable file
View file

@ -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! 🚀"

5
relay.env Normal file
View file

@ -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

View file

@ -0,0 +1 @@

View file

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

89
traefik-stack/readme.md Normal file
View file

@ -0,0 +1,89 @@
# 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)

15
turnserver.conf Normal file
View file

@ -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

View file

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

2
zdb.env Normal file
View file

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

21
zitadel.env Normal file
View file

@ -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

11
zitadel_api.log Normal file
View file

@ -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"}}