mirror of
https://github.com/netbirdio/netbird.git
synced 2026-03-31 06:34:14 -04:00
1381 lines
44 KiB
Bash
Executable File
1381 lines
44 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -e
|
|
|
|
# NetBird Getting Started with Embedded IdP (Dex)
|
|
# This script sets up NetBird with the embedded Dex identity provider
|
|
# No separate Dex container or reverse proxy needed - IdP is built into management server
|
|
|
|
# Sed pattern to strip base64 padding characters
|
|
SED_STRIP_PADDING='s/=//g'
|
|
|
|
# Constants for repeated string literals
|
|
readonly MSG_STARTING_SERVICES="\nStarting NetBird services\n"
|
|
readonly MSG_DONE="\nDone!\n"
|
|
readonly MSG_NEXT_STEPS="Next steps:"
|
|
readonly MSG_SEPARATOR="=========================================="
|
|
|
|
############################################
|
|
# Utility Functions
|
|
############################################
|
|
|
|
check_docker_compose() {
|
|
if command -v docker-compose &> /dev/null
|
|
then
|
|
echo "docker-compose"
|
|
return
|
|
fi
|
|
if docker compose --help &> /dev/null
|
|
then
|
|
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
|
|
}
|
|
|
|
check_jq() {
|
|
if ! command -v jq &> /dev/null
|
|
then
|
|
echo "jq is not installed or not in PATH, please install with your package manager. e.g. sudo apt install jq" > /dev/stderr
|
|
exit 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
get_main_ip_address() {
|
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
interface=$(route -n get default | grep 'interface:' | awk '{print $2}')
|
|
ip_address=$(ifconfig "$interface" | grep 'inet ' | awk '{print $2}')
|
|
else
|
|
interface=$(ip route | grep default | awk '{print $5}' | head -n 1)
|
|
ip_address=$(ip addr show "$interface" | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1)
|
|
fi
|
|
|
|
echo "$ip_address"
|
|
return 0
|
|
}
|
|
|
|
check_nb_domain() {
|
|
DOMAIN=$1
|
|
if [[ "$DOMAIN-x" == "-x" ]]; then
|
|
echo "The NETBIRD_DOMAIN variable cannot be empty." > /dev/stderr
|
|
return 1
|
|
fi
|
|
|
|
if [[ "$DOMAIN" == "netbird.example.com" ]]; then
|
|
echo "The NETBIRD_DOMAIN cannot be netbird.example.com" > /dev/stderr
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
read_nb_domain() {
|
|
READ_NETBIRD_DOMAIN=""
|
|
echo -n "Enter the domain you want to use for NetBird (e.g. netbird.my-domain.com): " > /dev/stderr
|
|
read -r READ_NETBIRD_DOMAIN < /dev/tty
|
|
if ! check_nb_domain "$READ_NETBIRD_DOMAIN"; then
|
|
read_nb_domain
|
|
fi
|
|
echo "$READ_NETBIRD_DOMAIN"
|
|
return 0
|
|
}
|
|
|
|
read_reverse_proxy_type() {
|
|
echo "" > /dev/stderr
|
|
echo "Which reverse proxy will you use?" > /dev/stderr
|
|
echo " [0] Traefik (recommended - automatic TLS, included in Docker Compose)" > /dev/stderr
|
|
echo " [1] Existing Traefik (labels for external Traefik instance)" > /dev/stderr
|
|
echo " [2] Nginx (generates config template)" > /dev/stderr
|
|
echo " [3] Nginx Proxy Manager (generates config + instructions)" > /dev/stderr
|
|
echo " [4] External Caddy (generates Caddyfile snippet)" > /dev/stderr
|
|
echo " [5] Other/Manual (displays setup documentation)" > /dev/stderr
|
|
echo "" > /dev/stderr
|
|
echo -n "Enter choice [0-5] (default: 0): " > /dev/stderr
|
|
read -r CHOICE < /dev/tty
|
|
|
|
if [[ -z "$CHOICE" ]]; then
|
|
CHOICE="0"
|
|
fi
|
|
|
|
if [[ ! "$CHOICE" =~ ^[0-5]$ ]]; then
|
|
echo "Invalid choice. Please enter a number between 0 and 5." > /dev/stderr
|
|
read_reverse_proxy_type
|
|
return
|
|
fi
|
|
|
|
echo "$CHOICE"
|
|
return 0
|
|
}
|
|
|
|
read_traefik_network() {
|
|
echo "" > /dev/stderr
|
|
echo "If you have an existing Traefik instance, enter its external network name." > /dev/stderr
|
|
echo -n "External network (leave empty to create 'netbird' network): " > /dev/stderr
|
|
read -r NETWORK < /dev/tty
|
|
echo "$NETWORK"
|
|
return 0
|
|
}
|
|
|
|
read_traefik_entrypoint() {
|
|
echo "" > /dev/stderr
|
|
echo "Enter the name of your Traefik HTTPS entrypoint." > /dev/stderr
|
|
echo -n "HTTPS entrypoint name (default: websecure): " > /dev/stderr
|
|
read -r ENTRYPOINT < /dev/tty
|
|
if [[ -z "$ENTRYPOINT" ]]; then
|
|
ENTRYPOINT="websecure"
|
|
fi
|
|
echo "$ENTRYPOINT"
|
|
return 0
|
|
}
|
|
|
|
read_traefik_certresolver() {
|
|
echo "" > /dev/stderr
|
|
echo "Enter the name of your Traefik certificate resolver (for automatic TLS)." > /dev/stderr
|
|
echo "Leave empty if you handle TLS termination elsewhere or use a wildcard cert." > /dev/stderr
|
|
echo -n "Certificate resolver name (e.g., letsencrypt): " > /dev/stderr
|
|
read -r RESOLVER < /dev/tty
|
|
echo "$RESOLVER"
|
|
return 0
|
|
}
|
|
|
|
read_port_binding_preference() {
|
|
echo "" > /dev/stderr
|
|
echo "Should container ports be bound to localhost only (127.0.0.1)?" > /dev/stderr
|
|
echo "Choose 'yes' if your reverse proxy runs on the same host (more secure)." > /dev/stderr
|
|
echo -n "Bind to localhost only? [Y/n]: " > /dev/stderr
|
|
read -r CHOICE < /dev/tty
|
|
|
|
if [[ "$CHOICE" =~ ^[Nn]$ ]]; then
|
|
echo "false"
|
|
else
|
|
echo "true"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
read_proxy_docker_network() {
|
|
local proxy_name="$1"
|
|
echo "" > /dev/stderr
|
|
echo "Is ${proxy_name} running in Docker?" > /dev/stderr
|
|
echo "If yes, enter the Docker network ${proxy_name} is on (NetBird will join it)." > /dev/stderr
|
|
echo -n "Docker network (leave empty if not in Docker): " > /dev/stderr
|
|
read -r NETWORK < /dev/tty
|
|
echo "$NETWORK"
|
|
return 0
|
|
}
|
|
|
|
read_enable_proxy() {
|
|
echo "" > /dev/stderr
|
|
echo "Do you want to enable the NetBird Proxy service?" > /dev/stderr
|
|
echo "The proxy allows you to selectively expose internal NetBird network resources" > /dev/stderr
|
|
echo "to the internet. You control which resources are exposed through the dashboard." > /dev/stderr
|
|
echo -n "Enable proxy? [y/N]: " > /dev/stderr
|
|
read -r CHOICE < /dev/tty
|
|
|
|
if [[ "$CHOICE" =~ ^[Yy]$ ]]; then
|
|
echo "true"
|
|
else
|
|
echo "false"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
read_traefik_acme_email() {
|
|
echo "" > /dev/stderr
|
|
echo "Enter your email for Let's Encrypt certificate notifications." > /dev/stderr
|
|
echo -n "Email address: " > /dev/stderr
|
|
read -r EMAIL < /dev/tty
|
|
if [[ -z "$EMAIL" ]]; then
|
|
echo "Email is required for Let's Encrypt." > /dev/stderr
|
|
read_traefik_acme_email
|
|
return
|
|
fi
|
|
echo "$EMAIL"
|
|
return 0
|
|
}
|
|
|
|
get_bind_address() {
|
|
if [[ "$BIND_LOCALHOST_ONLY" == "true" ]]; then
|
|
echo "127.0.0.1"
|
|
else
|
|
echo "0.0.0.0"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
get_upstream_host() {
|
|
# Always return 127.0.0.1 for health checks and upstream targets
|
|
# Cannot use 0.0.0.0 as a connection target
|
|
echo "127.0.0.1"
|
|
return 0
|
|
}
|
|
|
|
wait_management_proxy() {
|
|
local proxy_container="${1:-traefik}"
|
|
set +e
|
|
echo -n "Waiting for NetBird server to become ready"
|
|
counter=1
|
|
while true; do
|
|
# Check the embedded IdP endpoint through the reverse proxy
|
|
if curl -sk -f -o /dev/null "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/oauth2/.well-known/openid-configuration" 2>/dev/null; then
|
|
break
|
|
fi
|
|
if [[ $counter -eq 60 ]]; then
|
|
echo ""
|
|
echo "Taking too long. Checking logs..."
|
|
$DOCKER_COMPOSE_COMMAND logs --tail=20 "$proxy_container"
|
|
$DOCKER_COMPOSE_COMMAND logs --tail=20 netbird-server
|
|
fi
|
|
echo -n " ."
|
|
sleep 2
|
|
counter=$((counter + 1))
|
|
done
|
|
echo " done"
|
|
set -e
|
|
return 0
|
|
}
|
|
|
|
wait_management_direct() {
|
|
set +e
|
|
local upstream_host=$(get_upstream_host)
|
|
echo -n "Waiting for NetBird server to become ready"
|
|
counter=1
|
|
while true; do
|
|
# Check the embedded IdP endpoint directly (no reverse proxy)
|
|
if curl -sk -f -o /dev/null "http://${upstream_host}:${MANAGEMENT_HOST_PORT}/oauth2/.well-known/openid-configuration" 2>/dev/null; then
|
|
break
|
|
fi
|
|
if [[ $counter -eq 60 ]]; then
|
|
echo ""
|
|
echo "Taking too long. Checking logs..."
|
|
$DOCKER_COMPOSE_COMMAND logs --tail=20 netbird-server
|
|
fi
|
|
echo -n " ."
|
|
sleep 2
|
|
counter=$((counter + 1))
|
|
done
|
|
echo " done"
|
|
set -e
|
|
return 0
|
|
}
|
|
|
|
############################################
|
|
# Initialization and Configuration
|
|
############################################
|
|
|
|
initialize_default_values() {
|
|
NETBIRD_PORT=80
|
|
NETBIRD_HTTP_PROTOCOL="http"
|
|
NETBIRD_RELAY_PROTO="rel"
|
|
NETBIRD_RELAY_AUTH_SECRET=$(openssl rand -base64 32 | sed "$SED_STRIP_PADDING")
|
|
# Note: DataStoreEncryptionKey must keep base64 padding (=) for Go's base64.StdEncoding
|
|
DATASTORE_ENCRYPTION_KEY=$(openssl rand -base64 32)
|
|
NETBIRD_STUN_PORT=3478
|
|
|
|
# Docker images
|
|
DASHBOARD_IMAGE="netbirdio/dashboard:latest"
|
|
# Combined server replaces separate signal, relay, and management containers
|
|
NETBIRD_SERVER_IMAGE="netbirdio/netbird-server:latest"
|
|
NETBIRD_PROXY_IMAGE="netbirdio/reverse-proxy:latest"
|
|
|
|
# Reverse proxy configuration
|
|
REVERSE_PROXY_TYPE="0"
|
|
TRAEFIK_EXTERNAL_NETWORK=""
|
|
TRAEFIK_ENTRYPOINT="websecure"
|
|
TRAEFIK_CERTRESOLVER=""
|
|
TRAEFIK_ACME_EMAIL=""
|
|
DASHBOARD_HOST_PORT="8080"
|
|
MANAGEMENT_HOST_PORT="8081" # Combined server port (management + signal + relay)
|
|
BIND_LOCALHOST_ONLY="true"
|
|
EXTERNAL_PROXY_NETWORK=""
|
|
|
|
# Traefik static IP within the internal bridge network
|
|
TRAEFIK_IP="172.30.0.10"
|
|
|
|
# NetBird Proxy configuration
|
|
ENABLE_PROXY="false"
|
|
PROXY_TOKEN=""
|
|
return 0
|
|
}
|
|
|
|
configure_domain() {
|
|
if ! check_nb_domain "$NETBIRD_DOMAIN"; then
|
|
NETBIRD_DOMAIN=$(read_nb_domain)
|
|
fi
|
|
|
|
if [[ "$NETBIRD_DOMAIN" == "use-ip" ]]; then
|
|
NETBIRD_DOMAIN=$(get_main_ip_address)
|
|
BASE_DOMAIN=$NETBIRD_DOMAIN
|
|
else
|
|
NETBIRD_PORT=443
|
|
NETBIRD_HTTP_PROTOCOL="https"
|
|
NETBIRD_RELAY_PROTO="rels"
|
|
BASE_DOMAIN=$(echo $NETBIRD_DOMAIN | sed -E 's/^[^.]+\.//')
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
configure_reverse_proxy() {
|
|
# Prompt for reverse proxy type
|
|
REVERSE_PROXY_TYPE=$(read_reverse_proxy_type)
|
|
|
|
# Handle built-in Traefik prompts (option 0)
|
|
if [[ "$REVERSE_PROXY_TYPE" == "0" ]]; then
|
|
TRAEFIK_ACME_EMAIL=$(read_traefik_acme_email)
|
|
ENABLE_PROXY=$(read_enable_proxy)
|
|
fi
|
|
|
|
# Handle external Traefik-specific prompts (option 1)
|
|
if [[ "$REVERSE_PROXY_TYPE" == "1" ]]; then
|
|
TRAEFIK_EXTERNAL_NETWORK=$(read_traefik_network)
|
|
TRAEFIK_ENTRYPOINT=$(read_traefik_entrypoint)
|
|
TRAEFIK_CERTRESOLVER=$(read_traefik_certresolver)
|
|
fi
|
|
|
|
# Handle port binding for external proxy options (2-5)
|
|
if [[ "$REVERSE_PROXY_TYPE" -ge 2 ]]; then
|
|
BIND_LOCALHOST_ONLY=$(read_port_binding_preference)
|
|
fi
|
|
|
|
# Handle Docker network prompts for external proxies (options 2-4)
|
|
case "$REVERSE_PROXY_TYPE" in
|
|
2) EXTERNAL_PROXY_NETWORK=$(read_proxy_docker_network "Nginx") ;;
|
|
3) EXTERNAL_PROXY_NETWORK=$(read_proxy_docker_network "Nginx Proxy Manager") ;;
|
|
4) EXTERNAL_PROXY_NETWORK=$(read_proxy_docker_network "Caddy") ;;
|
|
*) ;; # No network prompt for other options
|
|
esac
|
|
return 0
|
|
}
|
|
|
|
check_existing_installation() {
|
|
if [[ -f config.yaml ]]; then
|
|
echo "Generated files already exist, if you want to reinitialize the environment, please remove them first."
|
|
echo "You can use the following commands:"
|
|
echo " $DOCKER_COMPOSE_COMMAND down --volumes # to remove all containers and volumes"
|
|
echo " rm -f docker-compose.yml dashboard.env config.yaml proxy.env traefik-dynamic.yaml nginx-netbird.conf caddyfile-netbird.txt npm-advanced-config.txt"
|
|
echo "Be aware that this will remove all data from the database, and you will have to reconfigure the dashboard."
|
|
exit 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
generate_configuration_files() {
|
|
echo Rendering initial files...
|
|
|
|
# Render docker-compose and proxy config based on selection
|
|
case "$REVERSE_PROXY_TYPE" in
|
|
0)
|
|
render_docker_compose_traefik_builtin > docker-compose.yml
|
|
if [[ "$ENABLE_PROXY" == "true" ]]; then
|
|
# Create placeholder proxy.env so docker-compose can validate
|
|
# This will be overwritten with the actual token after netbird-server starts
|
|
echo "# Placeholder - will be updated with token after netbird-server starts" > proxy.env
|
|
echo "NB_PROXY_TOKEN=placeholder" >> proxy.env
|
|
# TCP ServersTransport for PROXY protocol v2 to the proxy backend
|
|
render_traefik_dynamic > traefik-dynamic.yaml
|
|
fi
|
|
;;
|
|
1)
|
|
render_docker_compose_traefik > docker-compose.yml
|
|
;;
|
|
2)
|
|
render_docker_compose_exposed_ports > docker-compose.yml
|
|
render_nginx_conf > nginx-netbird.conf
|
|
;;
|
|
3)
|
|
render_docker_compose_exposed_ports > docker-compose.yml
|
|
render_npm_advanced_config > npm-advanced-config.txt
|
|
;;
|
|
4)
|
|
render_docker_compose_exposed_ports > docker-compose.yml
|
|
render_external_caddyfile > caddyfile-netbird.txt
|
|
;;
|
|
5)
|
|
render_docker_compose_exposed_ports > docker-compose.yml
|
|
;;
|
|
*)
|
|
echo "Invalid reverse proxy type: $REVERSE_PROXY_TYPE" > /dev/stderr
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Common files for all configurations
|
|
render_dashboard_env > dashboard.env
|
|
render_combined_yaml > config.yaml
|
|
return 0
|
|
}
|
|
|
|
start_services_and_show_instructions() {
|
|
# For built-in Traefik, start containers immediately
|
|
# For NPM, start containers first (NPM needs services running to create proxy)
|
|
# For other external proxies, show instructions first and wait for user confirmation
|
|
if [[ "$REVERSE_PROXY_TYPE" == "0" ]]; then
|
|
# Built-in Traefik - two-phase startup if proxy is enabled
|
|
echo -e "$MSG_STARTING_SERVICES"
|
|
|
|
if [[ "$ENABLE_PROXY" == "true" ]]; then
|
|
# Phase 1: Start core services (without proxy)
|
|
echo "Starting core services..."
|
|
$DOCKER_COMPOSE_COMMAND up -d traefik dashboard netbird-server
|
|
|
|
sleep 3
|
|
wait_management_proxy traefik
|
|
|
|
# Phase 2: Create proxy token and start proxy
|
|
echo ""
|
|
echo "Creating proxy access token..."
|
|
# Use docker exec with bash to run the token command directly
|
|
PROXY_TOKEN=$($DOCKER_COMPOSE_COMMAND exec -T netbird-server \
|
|
/go/bin/netbird-server token create --name "default-proxy" --config /etc/netbird/config.yaml 2>/dev/null | grep "^Token:" | awk '{print $2}')
|
|
|
|
if [[ -z "$PROXY_TOKEN" ]]; then
|
|
echo "ERROR: Failed to create proxy token. Check netbird-server logs." > /dev/stderr
|
|
$DOCKER_COMPOSE_COMMAND logs --tail=20 netbird-server
|
|
exit 1
|
|
fi
|
|
|
|
echo "Proxy token created successfully."
|
|
|
|
# Generate proxy.env with the token
|
|
render_proxy_env > proxy.env
|
|
|
|
# Start proxy service
|
|
echo "Starting proxy service..."
|
|
$DOCKER_COMPOSE_COMMAND up -d proxy
|
|
else
|
|
# No proxy - start all services at once
|
|
$DOCKER_COMPOSE_COMMAND up -d
|
|
|
|
sleep 3
|
|
wait_management_proxy traefik
|
|
fi
|
|
|
|
echo -e "$MSG_DONE"
|
|
print_post_setup_instructions
|
|
elif [[ "$REVERSE_PROXY_TYPE" == "1" ]]; then
|
|
# External Traefik - start containers, then show instructions
|
|
# Traefik discovers services via Docker labels, so containers must be running
|
|
echo -e "$MSG_STARTING_SERVICES"
|
|
$DOCKER_COMPOSE_COMMAND up -d
|
|
|
|
sleep 3
|
|
wait_management_direct
|
|
|
|
echo -e "$MSG_DONE"
|
|
print_post_setup_instructions
|
|
echo ""
|
|
echo "NetBird containers are running. Once Traefik is connected, access the dashboard at:"
|
|
echo " $NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN"
|
|
elif [[ "$REVERSE_PROXY_TYPE" == "3" ]]; then
|
|
# NPM - start containers first, then show instructions
|
|
# NPM requires backend services to be running before creating proxy hosts
|
|
echo -e "$MSG_STARTING_SERVICES"
|
|
$DOCKER_COMPOSE_COMMAND up -d
|
|
|
|
sleep 3
|
|
wait_management_direct
|
|
|
|
echo -e "$MSG_DONE"
|
|
print_post_setup_instructions
|
|
echo ""
|
|
echo "NetBird containers are running. Configure NPM as shown above, then access:"
|
|
echo " $NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN"
|
|
else
|
|
# External proxies (nginx, external Caddy, other) - need manual config first
|
|
print_post_setup_instructions
|
|
|
|
echo ""
|
|
echo -n "Press Enter when your reverse proxy is configured (or Ctrl+C to exit)... "
|
|
read -r < /dev/tty
|
|
|
|
echo -e "$MSG_STARTING_SERVICES"
|
|
$DOCKER_COMPOSE_COMMAND up -d
|
|
|
|
sleep 3
|
|
wait_management_direct
|
|
|
|
echo -e "$MSG_DONE"
|
|
echo "NetBird is now running. Access the dashboard at:"
|
|
echo " $NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
init_environment() {
|
|
initialize_default_values
|
|
configure_domain
|
|
configure_reverse_proxy
|
|
|
|
check_jq
|
|
DOCKER_COMPOSE_COMMAND=$(check_docker_compose)
|
|
|
|
check_existing_installation
|
|
generate_configuration_files
|
|
start_services_and_show_instructions
|
|
return 0
|
|
}
|
|
|
|
############################################
|
|
# Configuration File Renderers
|
|
############################################
|
|
|
|
render_docker_compose_traefik_builtin() {
|
|
# Generate proxy service section and Traefik dynamic config if enabled
|
|
local proxy_service=""
|
|
local proxy_volumes=""
|
|
local traefik_file_provider=""
|
|
local traefik_dynamic_volume=""
|
|
if [[ "$ENABLE_PROXY" == "true" ]]; then
|
|
traefik_file_provider=' - "--providers.file.filename=/etc/traefik/dynamic.yaml"'
|
|
traefik_dynamic_volume=" - ./traefik-dynamic.yaml:/etc/traefik/dynamic.yaml:ro"
|
|
proxy_service="
|
|
# NetBird Proxy - exposes internal resources to the internet
|
|
proxy:
|
|
image: $NETBIRD_PROXY_IMAGE
|
|
container_name: netbird-proxy
|
|
ports:
|
|
- 51820:51820/udp
|
|
restart: unless-stopped
|
|
networks: [netbird]
|
|
depends_on:
|
|
- netbird-server
|
|
env_file:
|
|
- ./proxy.env
|
|
volumes:
|
|
- netbird_proxy_certs:/certs
|
|
labels:
|
|
# TCP passthrough for any unmatched domain (proxy handles its own TLS)
|
|
- traefik.enable=true
|
|
- traefik.tcp.routers.proxy-passthrough.entrypoints=websecure
|
|
- traefik.tcp.routers.proxy-passthrough.rule=HostSNI(\`*\`)
|
|
- traefik.tcp.routers.proxy-passthrough.tls.passthrough=true
|
|
- traefik.tcp.routers.proxy-passthrough.service=proxy-tls
|
|
- traefik.tcp.routers.proxy-passthrough.priority=1
|
|
- traefik.tcp.services.proxy-tls.loadbalancer.server.port=8443
|
|
- traefik.tcp.services.proxy-tls.loadbalancer.serverstransport=pp-v2@file
|
|
logging:
|
|
driver: \"json-file\"
|
|
options:
|
|
max-size: \"500m\"
|
|
max-file: \"2\"
|
|
"
|
|
proxy_volumes="
|
|
netbird_proxy_certs:"
|
|
fi
|
|
|
|
cat <<EOF
|
|
services:
|
|
# Traefik reverse proxy (automatic TLS via Let's Encrypt)
|
|
traefik:
|
|
image: traefik:v3.6
|
|
container_name: netbird-traefik
|
|
restart: unless-stopped
|
|
networks:
|
|
netbird:
|
|
ipv4_address: $TRAEFIK_IP
|
|
command:
|
|
# Logging
|
|
- "--log.level=INFO"
|
|
- "--accesslog=true"
|
|
# Docker provider
|
|
- "--providers.docker=true"
|
|
- "--providers.docker.exposedbydefault=false"
|
|
- "--providers.docker.network=netbird"
|
|
# Entrypoints
|
|
- "--entrypoints.web.address=:80"
|
|
- "--entrypoints.websecure.address=:443"
|
|
- "--entrypoints.websecure.allowACMEByPass=true"
|
|
# Disable timeouts for long-lived gRPC streams
|
|
- "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0"
|
|
- "--entrypoints.websecure.transport.respondingTimeouts.writeTimeout=0"
|
|
- "--entrypoints.websecure.transport.respondingTimeouts.idleTimeout=0"
|
|
# HTTP to HTTPS redirect
|
|
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
|
|
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
|
|
# Let's Encrypt ACME
|
|
- "--certificatesresolvers.letsencrypt.acme.email=$TRAEFIK_ACME_EMAIL"
|
|
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
|
|
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
|
|
# gRPC transport settings
|
|
- "--serverstransport.forwardingtimeouts.responseheadertimeout=0s"
|
|
- "--serverstransport.forwardingtimeouts.idleconntimeout=0s"
|
|
$traefik_file_provider
|
|
ports:
|
|
- '443:443'
|
|
- '80:80'
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
- netbird_traefik_letsencrypt:/letsencrypt
|
|
$traefik_dynamic_volume
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "500m"
|
|
max-file: "2"
|
|
|
|
# UI dashboard
|
|
dashboard:
|
|
image: $DASHBOARD_IMAGE
|
|
container_name: netbird-dashboard
|
|
restart: unless-stopped
|
|
networks: [netbird]
|
|
env_file:
|
|
- ./dashboard.env
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.http.routers.netbird-dashboard.rule=Host(\`$NETBIRD_DOMAIN\`)
|
|
- 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.service=dashboard
|
|
- traefik.http.routers.netbird-dashboard.priority=1
|
|
- traefik.http.services.dashboard.loadbalancer.server.port=80
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "500m"
|
|
max-file: "2"
|
|
|
|
# Combined server (Management + Signal + Relay + STUN)
|
|
netbird-server:
|
|
image: $NETBIRD_SERVER_IMAGE
|
|
container_name: netbird-server
|
|
restart: unless-stopped
|
|
networks: [netbird]
|
|
ports:
|
|
- '$NETBIRD_STUN_PORT:$NETBIRD_STUN_PORT/udp'
|
|
volumes:
|
|
- netbird_data:/var/lib/netbird
|
|
- ./config.yaml:/etc/netbird/config.yaml
|
|
command: ["--config", "/etc/netbird/config.yaml"]
|
|
labels:
|
|
- traefik.enable=true
|
|
# gRPC router (needs h2c backend for HTTP/2 cleartext)
|
|
- traefik.http.routers.netbird-grpc.rule=Host(\`$NETBIRD_DOMAIN\`) && (PathPrefix(\`/signalexchange.SignalExchange/\`) || PathPrefix(\`/management.ManagementService/\`))
|
|
- traefik.http.routers.netbird-grpc.entrypoints=websecure
|
|
- traefik.http.routers.netbird-grpc.tls=true
|
|
- traefik.http.routers.netbird-grpc.tls.certresolver=letsencrypt
|
|
- traefik.http.routers.netbird-grpc.service=netbird-server-h2c
|
|
- traefik.http.routers.netbird-grpc.priority=100
|
|
# Backend router (relay, WebSocket, API, OAuth2)
|
|
- traefik.http.routers.netbird-backend.rule=Host(\`$NETBIRD_DOMAIN\`) && (PathPrefix(\`/relay\`) || PathPrefix(\`/ws-proxy/\`) || PathPrefix(\`/api\`) || PathPrefix(\`/oauth2\`))
|
|
- traefik.http.routers.netbird-backend.entrypoints=websecure
|
|
- traefik.http.routers.netbird-backend.tls=true
|
|
- traefik.http.routers.netbird-backend.tls.certresolver=letsencrypt
|
|
- traefik.http.routers.netbird-backend.service=netbird-server
|
|
- traefik.http.routers.netbird-backend.priority=100
|
|
# Services
|
|
- traefik.http.services.netbird-server.loadbalancer.server.port=80
|
|
- traefik.http.services.netbird-server-h2c.loadbalancer.server.port=80
|
|
- traefik.http.services.netbird-server-h2c.loadbalancer.server.scheme=h2c
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "500m"
|
|
max-file: "2"
|
|
${proxy_service}
|
|
volumes:
|
|
netbird_data:
|
|
netbird_traefik_letsencrypt:${proxy_volumes}
|
|
|
|
networks:
|
|
netbird:
|
|
driver: bridge
|
|
ipam:
|
|
config:
|
|
- subnet: 172.30.0.0/24
|
|
gateway: 172.30.0.1
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
render_combined_yaml() {
|
|
cat <<EOF
|
|
# Combined NetBird Server Configuration (Simplified)
|
|
# Generated by getting-started.sh
|
|
|
|
server:
|
|
listenAddress: ":80"
|
|
exposedAddress: "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT"
|
|
stunPorts:
|
|
- $NETBIRD_STUN_PORT
|
|
metricsPort: 9090
|
|
healthcheckAddress: ":9000"
|
|
logLevel: "info"
|
|
logFile: "console"
|
|
|
|
authSecret: "$NETBIRD_RELAY_AUTH_SECRET"
|
|
dataDir: "/var/lib/netbird"
|
|
|
|
auth:
|
|
issuer: "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/oauth2"
|
|
signKeyRefreshEnabled: true
|
|
dashboardRedirectURIs:
|
|
- "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/nb-auth"
|
|
- "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/nb-silent-auth"
|
|
cliRedirectURIs:
|
|
- "http://localhost:53000/"
|
|
|
|
reverseProxy:
|
|
trustedHTTPProxies:
|
|
- "$TRAEFIK_IP/32"
|
|
|
|
store:
|
|
engine: "sqlite"
|
|
encryptionKey: "$DATASTORE_ENCRYPTION_KEY"
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
render_dashboard_env() {
|
|
cat <<EOF
|
|
# Endpoints
|
|
NETBIRD_MGMT_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
|
|
NETBIRD_MGMT_GRPC_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
|
|
# OIDC - using embedded IdP
|
|
AUTH_AUDIENCE=netbird-dashboard
|
|
AUTH_CLIENT_ID=netbird-dashboard
|
|
AUTH_CLIENT_SECRET=
|
|
AUTH_AUTHORITY=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/oauth2
|
|
USE_AUTH0=false
|
|
AUTH_SUPPORTED_SCOPES=openid profile email groups
|
|
AUTH_REDIRECT_URI=/nb-auth
|
|
AUTH_SILENT_REDIRECT_URI=/nb-silent-auth
|
|
# SSL
|
|
NGINX_SSL_PORT=443
|
|
# Letsencrypt
|
|
LETSENCRYPT_DOMAIN=none
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
render_traefik_dynamic() {
|
|
cat <<'EOF'
|
|
tcp:
|
|
serversTransports:
|
|
pp-v2:
|
|
proxyProtocol:
|
|
version: 2
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
render_proxy_env() {
|
|
cat <<EOF
|
|
# NetBird Proxy Configuration
|
|
NB_PROXY_DEBUG_LOGS=false
|
|
# Use internal Docker network to connect to management (avoids hairpin NAT issues)
|
|
NB_PROXY_MANAGEMENT_ADDRESS=http://netbird-server:80
|
|
# Allow insecure gRPC connection to management (required for internal Docker network)
|
|
NB_PROXY_ALLOW_INSECURE=true
|
|
# Public URL where this proxy is reachable (used for cluster registration)
|
|
NB_PROXY_DOMAIN=$NETBIRD_DOMAIN
|
|
NB_PROXY_ADDRESS=:8443
|
|
NB_PROXY_TOKEN=$PROXY_TOKEN
|
|
NB_PROXY_CERTIFICATE_DIRECTORY=/certs
|
|
NB_PROXY_ACME_CERTIFICATES=true
|
|
NB_PROXY_ACME_CHALLENGE_TYPE=tls-alpn-01
|
|
NB_PROXY_FORWARDED_PROTO=https
|
|
# Enable PROXY protocol to preserve client IPs through L4 proxies (Traefik TCP passthrough)
|
|
NB_PROXY_PROXY_PROTOCOL=true
|
|
# Trust Traefik's IP for PROXY protocol headers
|
|
NB_PROXY_TRUSTED_PROXIES=$TRAEFIK_IP
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
render_docker_compose_traefik() {
|
|
local network_name="${TRAEFIK_EXTERNAL_NETWORK:-netbird}"
|
|
local network_config=""
|
|
if [[ -n "$TRAEFIK_EXTERNAL_NETWORK" ]]; then
|
|
network_config=" external: true"
|
|
fi
|
|
|
|
# Build TLS labels - certresolver is optional
|
|
local tls_labels=""
|
|
if [[ -n "$TRAEFIK_CERTRESOLVER" ]]; then
|
|
tls_labels="tls.certresolver=${TRAEFIK_CERTRESOLVER}"
|
|
fi
|
|
|
|
cat <<EOF
|
|
services:
|
|
# UI dashboard
|
|
dashboard:
|
|
image: $DASHBOARD_IMAGE
|
|
container_name: netbird-dashboard
|
|
restart: unless-stopped
|
|
networks: [$network_name]
|
|
env_file:
|
|
- ./dashboard.env
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.http.routers.netbird-dashboard.rule=Host(\`$NETBIRD_DOMAIN\`)
|
|
- traefik.http.routers.netbird-dashboard.entrypoints=$TRAEFIK_ENTRYPOINT
|
|
- traefik.http.routers.netbird-dashboard.tls=true
|
|
$(if [[ -n "$tls_labels" ]]; then echo " - traefik.http.routers.netbird-dashboard.${tls_labels}"; fi)
|
|
- traefik.http.routers.netbird-dashboard.priority=1
|
|
- traefik.http.services.netbird-dashboard.loadbalancer.server.port=80
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "500m"
|
|
max-file: "2"
|
|
|
|
# Combined server (Management + Signal + Relay + STUN)
|
|
netbird-server:
|
|
image: $NETBIRD_SERVER_IMAGE
|
|
container_name: netbird-server
|
|
restart: unless-stopped
|
|
networks: [$network_name]
|
|
ports:
|
|
- '$NETBIRD_STUN_PORT:$NETBIRD_STUN_PORT/udp'
|
|
volumes:
|
|
- netbird_data:/var/lib/netbird
|
|
- ./config.yaml:/etc/netbird/config.yaml
|
|
command: ["--config", "/etc/netbird/config.yaml"]
|
|
labels:
|
|
- traefik.enable=true
|
|
# gRPC router (needs h2c backend for HTTP/2 cleartext)
|
|
- traefik.http.routers.netbird-grpc.rule=Host(\`$NETBIRD_DOMAIN\`) && (PathPrefix(\`/signalexchange.SignalExchange/\`) || PathPrefix(\`/management.ManagementService/\`))
|
|
- traefik.http.routers.netbird-grpc.entrypoints=$TRAEFIK_ENTRYPOINT
|
|
- traefik.http.routers.netbird-grpc.tls=true
|
|
$(if [[ -n "$tls_labels" ]]; then echo " - traefik.http.routers.netbird-grpc.${tls_labels}"; fi)
|
|
- traefik.http.routers.netbird-grpc.service=netbird-server-h2c
|
|
# Backend router (relay, WebSocket, API, OAuth2)
|
|
- traefik.http.routers.netbird-backend.rule=Host(\`$NETBIRD_DOMAIN\`) && (PathPrefix(\`/relay\`) || PathPrefix(\`/ws-proxy/\`) || PathPrefix(\`/api\`) || PathPrefix(\`/oauth2\`))
|
|
- traefik.http.routers.netbird-backend.entrypoints=$TRAEFIK_ENTRYPOINT
|
|
- traefik.http.routers.netbird-backend.tls=true
|
|
$(if [[ -n "$tls_labels" ]]; then echo " - traefik.http.routers.netbird-backend.${tls_labels}"; fi)
|
|
- traefik.http.routers.netbird-backend.service=netbird-server
|
|
# Services
|
|
- traefik.http.services.netbird-server.loadbalancer.server.port=80
|
|
- traefik.http.services.netbird-server-h2c.loadbalancer.server.port=80
|
|
- traefik.http.services.netbird-server-h2c.loadbalancer.server.scheme=h2c
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "500m"
|
|
max-file: "2"
|
|
|
|
volumes:
|
|
netbird_data:
|
|
|
|
networks:
|
|
$network_name:
|
|
$network_config
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
render_docker_compose_exposed_ports() {
|
|
local bind_addr=$(get_bind_address)
|
|
local networks="[netbird]"
|
|
local networks_config="networks:
|
|
netbird:"
|
|
|
|
# If an external network is specified, add it and include in service networks
|
|
if [[ -n "$EXTERNAL_PROXY_NETWORK" ]]; then
|
|
networks="[netbird, $EXTERNAL_PROXY_NETWORK]"
|
|
networks_config="networks:
|
|
netbird:
|
|
$EXTERNAL_PROXY_NETWORK:
|
|
external: true"
|
|
fi
|
|
|
|
cat <<EOF
|
|
services:
|
|
# UI dashboard
|
|
dashboard:
|
|
image: $DASHBOARD_IMAGE
|
|
container_name: netbird-dashboard
|
|
restart: unless-stopped
|
|
networks: ${networks}
|
|
ports:
|
|
- '${bind_addr}:${DASHBOARD_HOST_PORT}:80'
|
|
env_file:
|
|
- ./dashboard.env
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "500m"
|
|
max-file: "2"
|
|
|
|
# Combined server (Management + Signal + Relay + STUN)
|
|
netbird-server:
|
|
image: $NETBIRD_SERVER_IMAGE
|
|
container_name: netbird-server
|
|
restart: unless-stopped
|
|
networks: ${networks}
|
|
ports:
|
|
- '${bind_addr}:${MANAGEMENT_HOST_PORT}:80'
|
|
- '$NETBIRD_STUN_PORT:$NETBIRD_STUN_PORT/udp'
|
|
volumes:
|
|
- netbird_data:/var/lib/netbird
|
|
- ./config.yaml:/etc/netbird/config.yaml
|
|
command: ["--config", "/etc/netbird/config.yaml"]
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "500m"
|
|
max-file: "2"
|
|
|
|
volumes:
|
|
netbird_data:
|
|
|
|
${networks_config}
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
render_nginx_conf() {
|
|
local upstream_host=$(get_upstream_host)
|
|
local dashboard_addr="${upstream_host}:${DASHBOARD_HOST_PORT}"
|
|
local server_addr="${upstream_host}:${MANAGEMENT_HOST_PORT}"
|
|
local install_note="# 1. Update SSL certificate paths below
|
|
# 2. Copy to your nginx config directory:
|
|
# Debian/Ubuntu: /etc/nginx/sites-available/netbird (then symlink to sites-enabled)
|
|
# RHEL/CentOS: /etc/nginx/conf.d/netbird.conf
|
|
# 3. Test and reload: nginx -t && systemctl reload nginx"
|
|
|
|
# If running in Docker network, use container names
|
|
if [[ -n "$EXTERNAL_PROXY_NETWORK" ]]; then
|
|
dashboard_addr="netbird-dashboard:80"
|
|
server_addr="netbird-server:80"
|
|
install_note="# This config uses container names since Nginx is on the same Docker network.
|
|
# Add this to your nginx.conf or include it from a separate file."
|
|
fi
|
|
|
|
cat <<EOF
|
|
# NetBird Nginx Configuration
|
|
# Generated by getting-started.sh
|
|
#
|
|
${install_note}
|
|
|
|
upstream netbird_dashboard {
|
|
server ${dashboard_addr};
|
|
keepalive 10;
|
|
}
|
|
upstream netbird_server {
|
|
server ${server_addr};
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
server_name $NETBIRD_DOMAIN;
|
|
|
|
location / {
|
|
return 301 https://\$host\$request_uri;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name $NETBIRD_DOMAIN;
|
|
|
|
# SSL/TLS Configuration
|
|
# Update these paths based on your certificate source:
|
|
#
|
|
# Let's Encrypt (certbot):
|
|
# ssl_certificate /etc/letsencrypt/live/$NETBIRD_DOMAIN/fullchain.pem;
|
|
# ssl_certificate_key /etc/letsencrypt/live/$NETBIRD_DOMAIN/privkey.pem;
|
|
#
|
|
# Let's Encrypt (acme.sh):
|
|
# ssl_certificate /root/.acme.sh/$NETBIRD_DOMAIN/fullchain.cer;
|
|
# ssl_certificate_key /root/.acme.sh/$NETBIRD_DOMAIN/$NETBIRD_DOMAIN.key;
|
|
#
|
|
# Custom certificates:
|
|
# ssl_certificate /etc/ssl/certs/$NETBIRD_DOMAIN.crt;
|
|
# ssl_certificate_key /etc/ssl/private/$NETBIRD_DOMAIN.key;
|
|
#
|
|
ssl_certificate /path/to/your/fullchain.pem;
|
|
ssl_certificate_key /path/to/your/privkey.pem;
|
|
|
|
# Recommended SSL settings
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_prefer_server_ciphers off;
|
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
|
|
|
# Required for long-lived gRPC connections
|
|
client_header_timeout 1d;
|
|
client_body_timeout 1d;
|
|
|
|
# Common proxy headers
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Scheme \$scheme;
|
|
proxy_set_header X-Forwarded-Proto https;
|
|
proxy_set_header X-Forwarded-Host \$host;
|
|
grpc_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
|
|
# WebSocket connections (relay, signal, management)
|
|
location ~ ^/(relay|ws-proxy/) {
|
|
proxy_pass http://netbird_server;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "Upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_read_timeout 1d;
|
|
}
|
|
|
|
# Native gRPC (signal + management)
|
|
location ~ ^/(signalexchange\.SignalExchange|management\.ManagementService)/ {
|
|
grpc_pass grpc://netbird_server;
|
|
grpc_read_timeout 1d;
|
|
grpc_send_timeout 1d;
|
|
grpc_socket_keepalive on;
|
|
}
|
|
|
|
# HTTP routes (API + OAuth2)
|
|
location ~ ^/(api|oauth2)/ {
|
|
proxy_pass http://netbird_server;
|
|
proxy_set_header Host \$host;
|
|
}
|
|
|
|
# Dashboard (catch-all)
|
|
location / {
|
|
proxy_pass http://netbird_dashboard;
|
|
}
|
|
}
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
render_external_caddyfile() {
|
|
local upstream_host=$(get_upstream_host)
|
|
local dashboard_addr="${upstream_host}:${DASHBOARD_HOST_PORT}"
|
|
local server_addr="${upstream_host}:${MANAGEMENT_HOST_PORT}"
|
|
local install_note="# Add this block to your existing Caddyfile and reload Caddy"
|
|
|
|
# If running in Docker network, use container names
|
|
if [[ -n "$EXTERNAL_PROXY_NETWORK" ]]; then
|
|
dashboard_addr="netbird-dashboard:80"
|
|
server_addr="netbird-server:80"
|
|
install_note="# This config uses container names since Caddy is on the same Docker network.
|
|
# Add this block to your Caddyfile and reload Caddy."
|
|
fi
|
|
|
|
cat <<EOF
|
|
# NetBird Caddyfile Snippet
|
|
# Generated by getting-started.sh
|
|
#
|
|
${install_note}
|
|
|
|
$NETBIRD_DOMAIN {
|
|
# Native gRPC (needs HTTP/2 cleartext to backend)
|
|
@grpc header Content-Type application/grpc*
|
|
reverse_proxy @grpc h2c://${server_addr}
|
|
|
|
# Combined server paths (relay, signal, management, OAuth2)
|
|
@backend path /relay* /ws-proxy/* /api/* /oauth2/*
|
|
reverse_proxy @backend ${server_addr}
|
|
|
|
# Dashboard (everything else)
|
|
reverse_proxy /* ${dashboard_addr}
|
|
}
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
render_npm_advanced_config() {
|
|
local upstream_host=$(get_upstream_host)
|
|
local server_addr="${upstream_host}:${MANAGEMENT_HOST_PORT}"
|
|
|
|
# If external network is specified, use container names instead of host addresses
|
|
if [[ -n "$EXTERNAL_PROXY_NETWORK" ]]; then
|
|
server_addr="netbird-server:80"
|
|
fi
|
|
|
|
cat <<EOF
|
|
# Advanced Configuration for Nginx Proxy Manager
|
|
# Paste this into the "Advanced" tab of your Proxy Host configuration
|
|
#
|
|
# IMPORTANT: Enable "HTTP/2 Support" in the SSL tab for gRPC to work!
|
|
|
|
# Required for long-lived connections (gRPC and WebSocket)
|
|
client_header_timeout 1d;
|
|
client_body_timeout 1d;
|
|
|
|
# WebSocket connections (relay, signal, management)
|
|
location ~ ^/(relay|ws-proxy/) {
|
|
proxy_pass http://${server_addr};
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
proxy_read_timeout 1d;
|
|
}
|
|
|
|
# Native gRPC (signal + management)
|
|
location ~ ^/(signalexchange\.SignalExchange|management\.ManagementService)/ {
|
|
grpc_pass grpc://${server_addr};
|
|
grpc_read_timeout 1d;
|
|
grpc_send_timeout 1d;
|
|
grpc_socket_keepalive on;
|
|
}
|
|
|
|
# HTTP routes (API + OAuth2)
|
|
location ~ ^/(api|oauth2)/ {
|
|
proxy_pass http://${server_addr};
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
############################################
|
|
# Post-Setup Instructions per Proxy Type
|
|
############################################
|
|
|
|
print_builtin_traefik_instructions() {
|
|
echo ""
|
|
echo "$MSG_SEPARATOR"
|
|
echo " NETBIRD SETUP COMPLETE"
|
|
echo "$MSG_SEPARATOR"
|
|
echo ""
|
|
echo "You can access the NetBird dashboard at:"
|
|
echo " $NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN"
|
|
echo ""
|
|
echo "Follow the onboarding steps to set up your NetBird instance."
|
|
echo ""
|
|
echo "Traefik is handling TLS certificates automatically via Let's Encrypt."
|
|
echo "If you see certificate warnings, wait a moment for certificate issuance to complete."
|
|
echo ""
|
|
echo "Open ports:"
|
|
echo " - 443/tcp (HTTPS - all NetBird services)"
|
|
echo " - 80/tcp (HTTP - redirects to HTTPS)"
|
|
echo " - $NETBIRD_STUN_PORT/udp (STUN - required for NAT traversal)"
|
|
if [[ "$ENABLE_PROXY" == "true" ]]; then
|
|
echo " - 51820/udp (WIREGUARD - (optional) for P2P proxy connections)"
|
|
echo ""
|
|
echo "NetBird Proxy:"
|
|
echo " The proxy service is enabled and running."
|
|
echo " Any domain NOT matching $NETBIRD_DOMAIN will be passed through to the proxy."
|
|
echo " The proxy handles its own TLS certificates via ACME TLS-ALPN-01 challenge."
|
|
echo " Point your proxy domain to this server's domain address like in the examples below:"
|
|
echo ""
|
|
echo " *.$NETBIRD_DOMAIN CNAME $NETBIRD_DOMAIN"
|
|
echo ""
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
print_traefik_instructions() {
|
|
echo ""
|
|
echo "$MSG_SEPARATOR"
|
|
echo " TRAEFIK SETUP"
|
|
echo "$MSG_SEPARATOR"
|
|
echo ""
|
|
echo "NetBird containers are configured with Traefik labels."
|
|
echo ""
|
|
echo "Configuration:"
|
|
echo " Entrypoint: $TRAEFIK_ENTRYPOINT"
|
|
if [[ -n "$TRAEFIK_CERTRESOLVER" ]]; then
|
|
echo " Certificate resolver: $TRAEFIK_CERTRESOLVER"
|
|
fi
|
|
if [[ -n "$TRAEFIK_EXTERNAL_NETWORK" ]]; then
|
|
echo " Network: $TRAEFIK_EXTERNAL_NETWORK (external)"
|
|
else
|
|
echo " Network: netbird"
|
|
fi
|
|
echo ""
|
|
echo "$MSG_NEXT_STEPS"
|
|
echo " - Ensure Traefik is running and configured"
|
|
if [[ -n "$TRAEFIK_EXTERNAL_NETWORK" ]]; then
|
|
echo " - Traefik must be on the '$TRAEFIK_EXTERNAL_NETWORK' network"
|
|
fi
|
|
echo " - Entrypoint '$TRAEFIK_ENTRYPOINT' must be defined"
|
|
if [[ -n "$TRAEFIK_CERTRESOLVER" ]]; then
|
|
echo " - Certificate resolver '$TRAEFIK_CERTRESOLVER' must be configured"
|
|
fi
|
|
echo " - Disable read timeout on the entrypoint for gRPC streams:"
|
|
echo " --entrypoints.$TRAEFIK_ENTRYPOINT.transport.respondingTimeouts.readTimeout=0"
|
|
echo " - HTTP to HTTPS redirect (recommended)"
|
|
return 0
|
|
}
|
|
|
|
print_nginx_instructions() {
|
|
local bind_addr=$(get_bind_address)
|
|
echo ""
|
|
echo "$MSG_SEPARATOR"
|
|
echo " NGINX SETUP"
|
|
echo "$MSG_SEPARATOR"
|
|
echo ""
|
|
echo "Generated: nginx-netbird.conf"
|
|
echo ""
|
|
echo "IMPORTANT: Nginx requires manual TLS certificate setup."
|
|
echo "You'll need to obtain SSL/TLS certificates and configure the paths in the"
|
|
echo "generated config file. The config includes examples for common certificate sources."
|
|
echo ""
|
|
if [[ -n "$EXTERNAL_PROXY_NETWORK" ]]; then
|
|
echo "NetBird containers have joined the '$EXTERNAL_PROXY_NETWORK' Docker network."
|
|
echo "The config uses container names for upstream servers."
|
|
echo ""
|
|
echo "$MSG_NEXT_STEPS"
|
|
echo " 1. Ensure your Nginx container has access to SSL certificates"
|
|
echo " (mount certificate directory as volume if needed)"
|
|
echo " 2. Edit nginx-netbird.conf and update SSL certificate paths"
|
|
echo " The config includes examples for certbot, acme.sh, and custom certs"
|
|
echo " 3. Include the config in your Nginx container's configuration"
|
|
echo " 4. Reload Nginx"
|
|
else
|
|
echo "$MSG_NEXT_STEPS"
|
|
echo " 1. Obtain SSL/TLS certificates (Let's Encrypt recommended)"
|
|
echo " 2. Edit nginx-netbird.conf and update certificate paths"
|
|
echo " 3. Install to /etc/nginx/sites-available/ (Debian) or /etc/nginx/conf.d/ (RHEL)"
|
|
echo " 4. Test and reload: nginx -t && systemctl reload nginx"
|
|
echo ""
|
|
echo "For detailed TLS setup instructions, see:"
|
|
echo "https://docs.netbird.io/selfhosted/reverse-proxy#tls-certificate-setup-for-nginx"
|
|
echo ""
|
|
echo "Container ports (bound to ${bind_addr}):"
|
|
echo " Dashboard: ${DASHBOARD_HOST_PORT}"
|
|
echo " NetBird Server: ${MANAGEMENT_HOST_PORT} (all services)"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
print_npm_instructions() {
|
|
local bind_addr=$(get_bind_address)
|
|
local upstream_host=$(get_upstream_host)
|
|
echo ""
|
|
echo "$MSG_SEPARATOR"
|
|
echo " NGINX PROXY MANAGER SETUP"
|
|
echo "$MSG_SEPARATOR"
|
|
echo ""
|
|
echo "Generated: npm-advanced-config.txt"
|
|
echo ""
|
|
if [[ -n "$EXTERNAL_PROXY_NETWORK" ]]; then
|
|
echo "NetBird containers have joined the '$EXTERNAL_PROXY_NETWORK' Docker network."
|
|
echo ""
|
|
echo "In NPM, create a Proxy Host:"
|
|
echo " Domain: $NETBIRD_DOMAIN"
|
|
echo " Forward Hostname: netbird-dashboard"
|
|
echo " Forward Port: 80"
|
|
echo " Block Common Exploits: enabled"
|
|
echo ""
|
|
echo " SSL tab:"
|
|
echo " - Request or select existing certificate"
|
|
echo " - Enable 'HTTP/2 Support' (REQUIRED for gRPC)"
|
|
echo ""
|
|
echo " Advanced tab:"
|
|
echo " - Paste contents of npm-advanced-config.txt"
|
|
else
|
|
echo "Container ports (bound to ${bind_addr}):"
|
|
echo " Dashboard: ${DASHBOARD_HOST_PORT}"
|
|
echo " NetBird Server: ${MANAGEMENT_HOST_PORT} (all services)"
|
|
echo ""
|
|
echo "In NPM, create a Proxy Host:"
|
|
echo " Domain: $NETBIRD_DOMAIN"
|
|
echo " Forward Hostname/IP: ${upstream_host}"
|
|
echo " Forward Port: ${DASHBOARD_HOST_PORT}"
|
|
echo " Block Common Exploits: enabled"
|
|
echo ""
|
|
echo " SSL tab:"
|
|
echo " - Request or select existing certificate"
|
|
echo " - Enable 'HTTP/2 Support' (REQUIRED for gRPC)"
|
|
echo ""
|
|
echo " Advanced tab:"
|
|
echo " - Paste contents of npm-advanced-config.txt"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
print_external_caddy_instructions() {
|
|
local bind_addr=$(get_bind_address)
|
|
echo ""
|
|
echo "$MSG_SEPARATOR"
|
|
echo " EXTERNAL CADDY SETUP"
|
|
echo "$MSG_SEPARATOR"
|
|
echo ""
|
|
echo "Generated: caddyfile-netbird.txt"
|
|
echo ""
|
|
if [[ -n "$EXTERNAL_PROXY_NETWORK" ]]; then
|
|
echo "NetBird containers have joined the '$EXTERNAL_PROXY_NETWORK' Docker network."
|
|
echo "The config uses container names for upstream servers."
|
|
echo ""
|
|
echo "$MSG_NEXT_STEPS"
|
|
echo " 1. Add the contents of caddyfile-netbird.txt to your Caddyfile"
|
|
echo " 2. Reload Caddy"
|
|
else
|
|
echo "$MSG_NEXT_STEPS"
|
|
echo " 1. Add the contents of caddyfile-netbird.txt to your Caddyfile"
|
|
echo " 2. Reload Caddy: caddy reload --config /path/to/Caddyfile"
|
|
echo ""
|
|
echo "Container ports (bound to ${bind_addr}):"
|
|
echo " Dashboard: ${DASHBOARD_HOST_PORT}"
|
|
echo " NetBird Server: ${MANAGEMENT_HOST_PORT} (all services)"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
print_manual_instructions() {
|
|
local bind_addr=$(get_bind_address)
|
|
local upstream_host=$(get_upstream_host)
|
|
echo ""
|
|
echo "$MSG_SEPARATOR"
|
|
echo " MANUAL REVERSE PROXY SETUP"
|
|
echo "$MSG_SEPARATOR"
|
|
echo ""
|
|
echo "Container ports (bound to ${bind_addr}):"
|
|
echo " Dashboard: ${DASHBOARD_HOST_PORT}"
|
|
echo " NetBird Server: ${MANAGEMENT_HOST_PORT} (all services: management, signal, relay)"
|
|
echo ""
|
|
echo "Configure your reverse proxy with these routes (all go to the same backend):"
|
|
echo ""
|
|
echo " WebSocket (relay, signal, management WS proxy):"
|
|
echo " /relay*, /ws-proxy/* -> ${upstream_host}:${MANAGEMENT_HOST_PORT}"
|
|
echo " (HTTP with WebSocket upgrade, extended timeout)"
|
|
echo ""
|
|
echo " Native gRPC (signal + management):"
|
|
echo " /signalexchange.SignalExchange/* -> ${upstream_host}:${MANAGEMENT_HOST_PORT}"
|
|
echo " /management.ManagementService/* -> ${upstream_host}:${MANAGEMENT_HOST_PORT}"
|
|
echo " (gRPC/h2c - plaintext HTTP/2)"
|
|
echo ""
|
|
echo " HTTP (API + embedded IdP):"
|
|
echo " /api/*, /oauth2/* -> ${upstream_host}:${MANAGEMENT_HOST_PORT}"
|
|
echo ""
|
|
echo " Dashboard (catch-all):"
|
|
echo " /* -> ${upstream_host}:${DASHBOARD_HOST_PORT}"
|
|
echo ""
|
|
echo "IMPORTANT: gRPC routes require HTTP/2 (h2c) upstream support."
|
|
echo "WebSocket and gRPC connections need extended timeouts (recommend 1 day)."
|
|
return 0
|
|
}
|
|
|
|
print_post_setup_instructions() {
|
|
case "$REVERSE_PROXY_TYPE" in
|
|
0)
|
|
print_builtin_traefik_instructions
|
|
;;
|
|
1)
|
|
print_traefik_instructions
|
|
;;
|
|
2)
|
|
print_nginx_instructions
|
|
;;
|
|
3)
|
|
print_npm_instructions
|
|
;;
|
|
4)
|
|
print_external_caddy_instructions
|
|
;;
|
|
5)
|
|
print_manual_instructions
|
|
;;
|
|
*)
|
|
echo "Unknown reverse proxy type: $REVERSE_PROXY_TYPE" > /dev/stderr
|
|
;;
|
|
esac
|
|
return 0
|
|
}
|
|
|
|
init_environment
|