Files
PrivyDrop/docker/scripts/generate-config.sh
T

974 lines
30 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Import environment detection script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/detect-environment.sh"
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging helpers
log_info() {
echo -e "${BLUE}$1${NC}"
}
log_success() {
echo -e "${GREEN}$1${NC}"
}
log_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
log_error() {
echo -e "${RED}$1${NC}"
}
# Defaults and global parameters
WITH_TURN="${WITH_TURN:-false}"
TURN_EXTERNAL_IP_OVERRIDE=""
TURN_MIN_PORT_DEFAULT=49152
TURN_MAX_PORT_DEFAULT=49252
TURN_MIN_PORT="$TURN_MIN_PORT_DEFAULT"
TURN_MAX_PORT="$TURN_MAX_PORT_DEFAULT"
ENABLE_SNI443="${ENABLE_SNI443:-}"
parse_turn_port_range() {
local range="$1"
if [[ -z "$range" ]]; then
return 0
fi
if [[ ! "$range" =~ ^([0-9]{2,5})-([0-9]{2,5})$ ]]; then
log_error "--turn-port-range must be MIN-MAX, e.g., 49152-49252"
exit 1
fi
local min="${BASH_REMATCH[1]}"
local max="${BASH_REMATCH[2]}"
if (( min < 1 || max > 65535 || min >= max )); then
log_error "Invalid port range: $min-$max; must be within 1-65535 and MIN<MAX"
exit 1
fi
TURN_MIN_PORT="$min"
TURN_MAX_PORT="$max"
}
NO_CLEAN=false
RESET_SSL=false
cleanup_previous_artifacts() {
if [[ "$NO_CLEAN" == "true" ]]; then
log_info "Skipping cleanup of previous artifacts (--no-clean)"
return 0
fi
log_warning "Cleaning previous generated artifacts (keeping SSL certificates)..."
rm -f .env 2>/dev/null || true
rm -f docker/nginx/nginx.conf 2>/dev/null || true
rm -f docker/nginx/conf.d/*.conf 2>/dev/null || true
rm -f docker/coturn/turnserver.conf 2>/dev/null || true
# Do not clean docker/ssl by default unless --reset-ssl is set
if [[ "$RESET_SSL" == "true" ]]; then
log_warning "Resetting SSL directory as requested: docker/ssl/*"
rm -f docker/ssl/* 2>/dev/null || true
fi
}
# Show help
show_help() {
cat << 'EOF'
PrivyDrop Config Generator (Docker)
Usage: bash docker/scripts/generate-config.sh [options]
Options:
--mode MODE Generation mode: private|basic|public|full
private/basic: Intranet HTTP; TURN disabled by default; frontend talks directly to backend
public: Public HTTP + TURN enabled (works without domain; TURN host prefers public IP)
full: Full HTTPS + TURN enabled (domain recommended; frontend via domain HTTPS)
--with-turn Enable TURN in any mode (including private/basic). Default external-ip=LOCAL_IP
--turn-external-ip IP Explicit TURN external-ip; if not set, use PUBLIC_IP, otherwise fallback to LOCAL_IP
--turn-port-range R TURN UDP port range, format MIN-MAX; default 49152-49252
--domain DOMAIN Domain (for Nginx/certs/TURN realm, e.g., turn.DOMAIN)
--local-ip IP Local intranet IP (auto-detected if omitted)
--enable-sni443 Enable 443 SNI split (turn.DOMAIN → coturn:5349, others → web:8443)
--no-sni443 Disable 443 SNI split (HTTPS listens directly on 443)
--help Show this help
--no-clean Skip cleaning previous outputs (useful for regeneration without wiping SSL)
--reset-ssl Force clean docker/ssl/* (not cleaned by default)
--ssl-mode MODE Cert mode: letsencrypt|self-signed|provided
- full defaults to letsencrypt; private/public default to self-signed
Environment variables (optional):
PUBLIC_IP Explicit public IP; only used in public/full.
TURN external-ip prefers PUBLIC_IP,
fallback to LOCAL_IP (LAN-only; NAT traversal limited).
Outputs (with key variables set automatically):
- .env Core env vars (including NEXT_PUBLIC_API_URL/CORS)
- docker/nginx/* Nginx reverse proxy configs (HTTP also generated for private/basic)
- docker/ssl/* Self-signed certs (generated for private/basic/public; replace with real certs for full)
- docker/coturn/turnserver.conf Generated/overwritten in public/full or when --with-turn is set
Notes:
- TURN external-ip is set as external-ip=${PUBLIC_IP:-${LOCAL_IP}}
i.e., prefer PUBLIC_IP, otherwise fallback to LOCAL_IP.
- private/basic does not overwrite docker/coturn/turnserver.conf;
if TURN was generated before, that file may retain a previous external-ip.
Examples:
# 1) Pure intranet (recommended for dev/quick LAN testing)
bash docker/scripts/generate-config.sh --mode private [--local-ip 192.168.0.113]
# 2) Intranet + TURN (default external-ip=LOCAL_IP, ports=49152-49252)
bash docker/scripts/generate-config.sh --mode private --with-turn [--local-ip 192.168.0.113]
# 3) Intranet + TURN (custom port range / explicit external-ip)
bash docker/scripts/generate-config.sh --mode private --with-turn \
--turn-port-range 56000-56100 --turn-external-ip 192.168.0.113 \
[--local-ip 192.168.0.113]
# 4) Public HTTP + TURN (auto-detect public IP; inject NEXT_PUBLIC_API_URL)
bash docker/scripts/generate-config.sh --mode public --local-ip 192.168.0.113
# 5) Public HTTP + TURN (explicit public IP; avoid external detection)
PUBLIC_IP=1.2.3.4 bash docker/scripts/generate-config.sh --mode public --local-ip 192.168.0.113
# 6) HTTPS + TURN (with domain)
bash docker/scripts/generate-config.sh --mode full --domain example.com --local-ip 192.168.0.113
Intranet with TURN quick tip (minimal changes):
A) One-step (recommended):
bash docker/scripts/generate-config.sh --mode private --with-turn --local-ip 192.168.0.113
then bash ./deploy.sh --mode private --with-turn
B) Step-by-step:
Generate private for web/backend first, then docker compose up -d coturn
EOF
}
# Generate environment variables file
generate_env_file() {
log_info "Generating environment variable config..."
local env_file=".env"
# Read existing config to keep user-defined fields (e.g., proxy, TURN)
declare -A existing_env=()
if [[ -f "$env_file" ]]; then
while IFS= read -r line; do
[[ -z "$line" || "${line:0:1}" == "#" ]] && continue
if [[ "$line" == *=* ]]; then
local key="${line%%=*}"
local value="${line#*=}"
existing_env[$key]="$value"
fi
done < "$env_file"
fi
# Generate a random password (also saved globally for TURN configuration later)
local turn_password="${existing_env[TURN_PASSWORD]}"
if [[ -z "$turn_password" ]]; then
turn_password=$(openssl rand -base64 32 2>/dev/null || echo "privydrop$(date +%s)")
fi
# Compute access endpoints for different deployment modes
# Support both localhost and host IP for browser access; helpful for Docker direct access or local debugging
local cors_origin="http://${LOCAL_IP}:3002,http://localhost:3002"
local api_url="http://${LOCAL_IP}:3001"
local ssl_mode="self-signed"
local turn_enabled="false"
local turn_host_value=""
local turn_realm_value="${existing_env[TURN_REALM]:-turn.local}"
local turn_username_value="${existing_env[TURN_USERNAME]:-privydrop}"
local next_public_turn_host=""
local next_public_turn_username=""
local next_public_turn_password=""
if [[ "$DEPLOYMENT_MODE" == "public" ]]; then
# Public without domain: frontend connects directly to backend; use PUBLIC_IP (fallback LOCAL_IP)
local effective_public_host="${PUBLIC_IP:-$LOCAL_IP}"
cors_origin="http://${effective_public_host}:3002,http://localhost:3002"
api_url="http://${effective_public_host}:3001"
turn_enabled="true"
elif [[ "$DEPLOYMENT_MODE" == "full" ]]; then
# With domain + HTTPS: both frontend and backend via domain; Nginx proxies /api
cors_origin="https://${DOMAIN_NAME:-$LOCAL_IP}"
api_url="https://${DOMAIN_NAME:-$LOCAL_IP}"
ssl_mode="letsencrypt"
turn_enabled="true"
fi
# If TURN explicitly enabled, override mode defaults
if [[ "$WITH_TURN" == "true" ]]; then
turn_enabled="true"
fi
if [[ "$turn_enabled" == "true" ]]; then
if [[ -n "$DOMAIN_NAME" ]]; then
turn_host_value="turn.${DOMAIN_NAME}"
turn_realm_value="turn.${DOMAIN_NAME}"
else
# Without domain: prefer PUBLIC_IP; fallback to LOCAL_IP
turn_host_value="${PUBLIC_IP:-$LOCAL_IP}"
turn_realm_value="turn.local"
fi
next_public_turn_host="$turn_host_value"
next_public_turn_username="$turn_username_value"
next_public_turn_password="$turn_password"
fi
# Port range (default 49152-49252; overridable via --turn-port-range)
local turn_min_port_value="${TURN_MIN_PORT:-$TURN_MIN_PORT_DEFAULT}"
local turn_max_port_value="${TURN_MAX_PORT:-$TURN_MAX_PORT_DEFAULT}"
local default_no_proxy="localhost,127.0.0.1,backend,frontend,redis,coturn"
local http_proxy_value="${HTTP_PROXY:-${existing_env[HTTP_PROXY]}}"
local https_proxy_value="${HTTPS_PROXY:-${existing_env[HTTPS_PROXY]}}"
local no_proxy_value="${NO_PROXY:-${existing_env[NO_PROXY]:-$default_no_proxy}}"
# Expose key TURN parameters to later steps
TURN_ENABLED="$turn_enabled"
TURN_USERNAME="$turn_username_value"
TURN_PASSWORD="$turn_password"
TURN_REALM="$turn_realm_value"
TURN_HOST="$turn_host_value"
TURN_MIN_PORT="$turn_min_port_value"
TURN_MAX_PORT="$turn_max_port_value"
cat > "$env_file" << EOF
# PrivyDrop Docker configuration
# Generated at: $(date)
# Network mode: $NETWORK_MODE
# Deployment mode: $DEPLOYMENT_MODE
# =============================================================================
# Network config
# =============================================================================
CORS_ORIGIN=${cors_origin}
NEXT_PUBLIC_API_URL=${api_url}
NEXT_PUBLIC_TURN_HOST=${next_public_turn_host}
NEXT_PUBLIC_TURN_USERNAME=${next_public_turn_username}
NEXT_PUBLIC_TURN_PASSWORD=${next_public_turn_password}
# =============================================================================
# Port config
# =============================================================================
FRONTEND_PORT=3002
BACKEND_PORT=3001
HTTP_PORT=80
HTTPS_PORT=443
# =============================================================================
# Redis config
# =============================================================================
REDIS_HOST=redis
REDIS_PORT=6379
# =============================================================================
# Deployment config
# =============================================================================
DEPLOYMENT_MODE=${DEPLOYMENT_MODE}
NETWORK_MODE=${NETWORK_MODE}
LOCAL_IP=${LOCAL_IP}
PUBLIC_IP=${PUBLIC_IP:-}
# =============================================================================
# SSL config
# =============================================================================
SSL_MODE=${ssl_mode}
DOMAIN_NAME=${DOMAIN_NAME:-}
# =============================================================================
# TURN server config (optional)
# =============================================================================
TURN_ENABLED=${turn_enabled}
TURN_USERNAME=${turn_username_value}
TURN_PASSWORD=${turn_password}
TURN_REALM=${turn_realm_value}
TURN_MIN_PORT=${turn_min_port_value}
TURN_MAX_PORT=${turn_max_port_value}
# =============================================================================
# Nginx config
# =============================================================================
NGINX_SERVER_NAME=${DOMAIN_NAME:-${LOCAL_IP}}
# =============================================================================
# Logging config
# =============================================================================
LOG_LEVEL=info
# =============================================================================
# Proxy config (optional)
# =============================================================================
HTTP_PROXY=${http_proxy_value}
HTTPS_PROXY=${https_proxy_value}
NO_PROXY=${no_proxy_value}
EOF
log_success "Environment variable config generated: $env_file"
}
# Generate Nginx config
generate_nginx_config() {
log_info "Generating Nginx config..."
mkdir -p docker/nginx/conf.d
local server_name="${DOMAIN_NAME:-${LOCAL_IP} localhost}"
local upstream_backend="backend:3001"
local upstream_frontend="frontend:3002"
# Generate main Nginx config
cat > docker/nginx/nginx.conf << 'EOF'
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Log format
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# Basic settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
# Client settings
client_max_body_size 100M;
client_header_timeout 60s;
client_body_timeout 60s;
# Gzip settings
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/json;
# Include site configs
include /etc/nginx/conf.d/*.conf;
}
EOF
# Generate site config
mkdir -p docker/letsencrypt-www
cat > docker/nginx/conf.d/default.conf << EOF
# Upstream definitions
upstream backend {
server ${upstream_backend};
keepalive 32;
}
upstream frontend {
server ${upstream_frontend};
keepalive 32;
}
# HTTP server config
server {
listen 80;
server_name ${server_name};
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
# ACME upstream for Let's Encrypt issuance/renewal
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Health check endpoint
location /nginx-health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# Backend API proxy
location /api/ {
proxy_pass http://backend/api/;
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_cache_bypass \$http_upgrade;
# Timeout settings
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Backend health-check proxy
location /health {
proxy_pass http://backend/health;
proxy_http_version 1.1;
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;
}
# Socket.IO proxy
location /socket.io/ {
proxy_pass http://backend;
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;
# WebSocket-specific settings
proxy_buffering off;
proxy_cache off;
}
# Frontend app proxy
location / {
proxy_pass http://frontend;
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_cache_bypass \$http_upgrade;
# Next.js-specific settings
proxy_buffering off;
}
}
EOF
log_success "Nginx config generated"
echo " Main config: docker/nginx/nginx.conf"
echo " Site config: docker/nginx/conf.d/default.conf"
}
# Generate SSL certificates
generate_ssl_certificates() {
if [[ "$SSL_MODE" == "self-signed" ]] || [[ "$NETWORK_MODE" == "private" ]]; then
log_info "Generating self-signed SSL certificates..."
mkdir -p docker/ssl
# Generate CA private key
openssl genrsa -out docker/ssl/ca-key.pem 4096 2>/dev/null
# Generate CA certificate
openssl req -new -x509 -days 365 -key docker/ssl/ca-key.pem \
-out docker/ssl/ca-cert.pem \
-subj "/C=CN/ST=Local/L=Local/O=PrivyDrop/CN=PrivyDrop-CA" 2>/dev/null
# Generate server private key
openssl genrsa -out docker/ssl/server-key.pem 4096 2>/dev/null
# Generate server CSR
openssl req -new -key docker/ssl/server-key.pem \
-out docker/ssl/server.csr \
-subj "/C=CN/ST=Local/L=Local/O=PrivyDrop/CN=${LOCAL_IP}" 2>/dev/null
# Create extensions config
cat > docker/ssl/server.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = *.local
DNS.3 = ${DOMAIN_NAME:-privydrop.local}
IP.1 = ${LOCAL_IP}
IP.2 = 127.0.0.1
EOF
# Sign server certificate
openssl x509 -req -days 365 -in docker/ssl/server.csr \
-CA docker/ssl/ca-cert.pem -CAkey docker/ssl/ca-key.pem \
-out docker/ssl/server-cert.pem -CAcreateserial \
-extensions v3_req -extfile docker/ssl/server.ext 2>/dev/null
# Clean temporary files
rm -f docker/ssl/server.csr docker/ssl/server.ext docker/ssl/ca-cert.srl
# Set permissions
chmod 600 docker/ssl/*-key.pem
chmod 644 docker/ssl/*-cert.pem
log_success "SSL certificates generated: docker/ssl/"
log_info "To trust the cert, import the CA cert: docker/ssl/ca-cert.pem"
# For self-signed, generate 443 config immediately
if [[ "$DEPLOYMENT_MODE" != "basic" ]]; then
generate_https_nginx_config
fi
fi
}
# Generate HTTPS Nginx config
generate_https_nginx_config() {
log_info "Generating HTTPS Nginx config..."
local https_port="443"
if [[ "$ENABLE_SNI443" == "true" ]]; then
https_port="8443"
fi
cat >> docker/nginx/conf.d/default.conf << EOF
# HTTPS server config
server {
listen ${https_port} ssl http2;
server_name ${DOMAIN_NAME:-${LOCAL_IP}};
# SSL settings
ssl_certificate /etc/nginx/ssl/server-cert.pem;
ssl_certificate_key /etc/nginx/ssl/server-key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
# Health check endpoint
location /nginx-health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# Backend API proxy
location /api/ {
proxy_pass http://backend/api/;
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 https;
proxy_cache_bypass \$http_upgrade;
}
# Backend health-check proxy
location /health {
proxy_pass http://backend/health;
proxy_http_version 1.1;
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 https;
}
# Socket.IO proxy
location /socket.io/ {
proxy_pass http://backend;
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 https;
proxy_buffering off;
proxy_cache off;
}
# Frontend app proxy
location / {
proxy_pass http://frontend;
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 https;
proxy_cache_bypass \$http_upgrade;
proxy_buffering off;
}
}
EOF
log_success "HTTPS config added"
}
# Generate Nginx stream SNI split (443)
generate_stream_sni443() {
if [[ "$ENABLE_SNI443" != "true" ]]; then
return 0
fi
if [[ -z "$DOMAIN_NAME" ]]; then
log_warning "SNI 443 requires a domain; none specified, skipping stream config"
return 0
fi
# Avoid duplicate appends
if grep -q "## SNI 443 stream" docker/nginx/nginx.conf 2>/dev/null; then
log_info "SNI 443 stream config already exists; skipping"
return 0
fi
log_info "Append SNI 443 stream config to nginx.conf"
cat >> docker/nginx/nginx.conf << EOF
## SNI 443 stream
stream {
map \$ssl_preread_server_name \$sni_upstream {
~^turn\.(${DOMAIN_NAME})$ coturn;
default web;
}
upstream coturn { server coturn:5349; }
upstream web { server 127.0.0.1:8443; }
server {
listen 443 reuseport;
proxy_pass \$sni_upstream;
ssl_preread on;
}
}
EOF
}
# Enable 443 only when certs exist (for letsencrypt/provided)
enable_https_if_cert_present() {
if [[ -f "docker/ssl/server-cert.pem" && -f "docker/ssl/server-key.pem" ]]; then
# With SNI enabled, append stream split first, then generate HTTPS on 8443/443
if [[ "$ENABLE_SNI443" == "true" && -n "$DOMAIN_NAME" ]]; then
generate_stream_sni443
fi
# If HTTPS server is not present in default.conf, append it (port depends on SNI flag)
local expected="listen 443 ssl"
[[ "$ENABLE_SNI443" == "true" ]] && expected="listen 8443 ssl"
if ! grep -q "$expected" docker/nginx/conf.d/default.conf 2>/dev/null; then
generate_https_nginx_config
else
log_info "Existing HTTPS (${ENABLE_SNI443:+SNI=on}) config detected; skipping"
fi
else
log_warning "No certificates detected (docker/ssl/server-*.pem); 443 config not enabled yet"
fi
}
# Generate Coturn config
generate_coturn_config() {
if [[ "$TURN_ENABLED" == "true" ]]; then
log_info "Generating Coturn TURN server config..."
mkdir -p docker/coturn
# Compute external-ip: prefer --turn-external-ip, then PUBLIC_IP, then LOCAL_IP
local external_ip_value
if [[ -n "$TURN_EXTERNAL_IP_OVERRIDE" ]]; then
external_ip_value="$TURN_EXTERNAL_IP_OVERRIDE"
elif [[ -n "$PUBLIC_IP" ]]; then
external_ip_value="$PUBLIC_IP"
else
external_ip_value="$LOCAL_IP"
fi
local min_port_value="${TURN_MIN_PORT:-$TURN_MIN_PORT_DEFAULT}"
local max_port_value="${TURN_MAX_PORT:-$TURN_MAX_PORT_DEFAULT}"
cat > docker/coturn/turnserver.conf << EOF
# PrivyDrop TURN server configuration
# Generated at: $(date)
# Listen ports
listening-port=3478
tls-listening-port=5349
# Listen IPs
listening-ip=0.0.0.0
relay-ip=0.0.0.0
# External IP (for NAT)
external-ip=${external_ip_value}
# Server domain
realm=${TURN_REALM}
server-name=${TURN_REALM}
# Authentication method
lt-cred-mech
# User authentication
user=${TURN_USERNAME}:${TURN_PASSWORD}
# SSL certificates (if TLS enabled)
cert=/etc/ssl/certs/server-cert.pem
pkey=/etc/ssl/certs/server-key.pem
# Logging configuration
no-stdout-log
log-file=/var/log/turnserver.log
verbose
# Security settings
no-cli
no-loopback-peers
no-multicast-peers
# Performance settings
min-port=${min_port_value}
max-port=${max_port_value}
# Database (optional)
# userdb=/var/lib/turn/turndb
# Miscellaneous
mobility
no-tlsv1
no-tlsv1_1
EOF
log_success "Coturn config generated: docker/coturn/turnserver.conf"
log_info "TURN server username: ${TURN_USERNAME}"
log_warning "TURN server password saved in .env"
fi
}
# Generate Docker ignore files
generate_dockerignore() {
log_info "Generating Docker ignore files..."
# Backend .dockerignore
cat > backend/.dockerignore << EOF
node_modules
npm-debug.log*
.npm
.env*
.git
.gitignore
README.md
Dockerfile
.dockerignore
coverage
.nyc_output
logs
*.log
EOF
# Frontend .dockerignore
cat > frontend/.dockerignore << EOF
node_modules
.next
.git
.gitignore
README.md
Dockerfile
.dockerignore
.env*
npm-debug.log*
.npm
coverage
.nyc_output
*.log
public/sw.js
public/workbox-*.js
EOF
log_success "Docker ignore files generated"
}
# Create log directories
create_log_directories() {
log_info "Creating log directories..."
mkdir -p logs/{nginx,backend,frontend,coturn}
# Set permissions
chmod 755 logs
chmod 755 logs/*
log_success "Log directories created: logs/"
}
# Main function
main() {
echo -e "${BLUE}=== PrivyDrop Config Generation ===${NC}"
echo ""
# Parse arguments (consistent with the environment detection script)
while [[ $# -gt 0 ]]; do
case $1 in
--domain)
DOMAIN_NAME="$2"
shift 2
;;
--mode)
DEPLOYMENT_MODE="$2"
if [[ "$2" == "private" || "$2" == "basic" ]]; then
FORCED_MODE="private"
elif [[ "$2" == "public" || "$2" == "full" ]]; then
FORCED_MODE="public"
fi
shift 2
;;
--local-ip)
LOCAL_IP_OVERRIDE="$2"
shift 2
;;
--with-turn)
WITH_TURN="true"
shift
;;
--turn-external-ip)
TURN_EXTERNAL_IP_OVERRIDE="$2"
shift 2
;;
--turn-port-range)
parse_turn_port_range "$2"
shift 2
;;
--enable-sni443)
ENABLE_SNI443=true
shift
;;
--no-sni443)
ENABLE_SNI443=false
shift
;;
--no-clean)
NO_CLEAN=true
shift
;;
--reset-ssl)
RESET_SSL=true
shift
;;
--ssl-mode)
SSL_MODE="$2"
shift 2
;;
--help)
show_help
exit 0
;;
*)
shift
;;
esac
done
# Clean previous outputs first (avoid stale leftovers)
cleanup_previous_artifacts
# Run environment detection first
if ! detect_network_environment; then
log_error "Environment detection failed"
exit 1
fi
if ! check_system_resources; then
log_error "System resource check failed"
exit 1
fi
detect_deployment_mode
echo ""
# Generate all configuration files
generate_env_file
echo ""
generate_nginx_config
echo ""
# Certificate generation policy:
# - private/public use self-signed; full uses letsencrypt (issued/copied by deploy script)
if [[ -z "$SSL_MODE" ]]; then
if [[ "$DEPLOYMENT_MODE" == "full" ]]; then
SSL_MODE="letsencrypt"
else
SSL_MODE="self-signed"
fi
fi
# SNI on 443 enabled by default: full mode with domain, unless --no-sni443
if [[ -z "$ENABLE_SNI443" ]]; then
if [[ "$DEPLOYMENT_MODE" == "full" && -n "$DOMAIN_NAME" ]]; then
ENABLE_SNI443=true
else
ENABLE_SNI443=false
fi
fi
generate_ssl_certificates
echo ""
# full/provided/letsencrypt: enable 443 only when certs are ready
if [[ "$DEPLOYMENT_MODE" == "full" ]]; then
enable_https_if_cert_present
echo ""
fi
generate_coturn_config
echo ""
generate_dockerignore
echo ""
create_log_directories
echo ""
log_success "🎉 All configuration files generated!"
echo ""
echo -e "${BLUE}Generated files:${NC}"
echo " .env - Environment variables"
echo " docker/nginx/ - Nginx config"
echo " docker/ssl/ - SSL certificates"
[[ "$TURN_ENABLED" == "true" ]] && echo " docker/coturn/ - TURN server config"
echo " logs/ - Log directories"
echo ""
echo -e "${BLUE}Next steps:${NC}"
echo " Run './deploy.sh' to start deployment"
}
# If the script is executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi