Files
PrivyDrop/docker/scripts/detect-environment.sh
T
david_bai 8590eda2c2 refactor(scripts): simplify modes and harden cert automation
- New modes: lan-http, lan-tls (self-signed), public, full
- Add flags: --no-sni443, --enable-web-https (lan-tls), --test-renewal
- generate-config: lan-tls HTTPS on 8443 only when explicitly enabled; HSTS only in full; SNI 443 default in full
- detect-environment: remove interactive prompt; adjust public description to 'HTTP + TURN'
- deploy.sh: pass new flags, add certbot scheduler (systemd timer or cron fallback), add dry-run renewal test
- Docs (EN/zh-CN): update quick start, modes overview, LAN TLS guidance, LE auto-issue/renew section
2025-10-08 23:17:19 +08:00

488 lines
12 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
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Global variables
NETWORK_MODE=""
LOCAL_IP=""
PUBLIC_IP=""
DEPLOYMENT_MODE="basic"
FORCED_MODE=""
LOCAL_IP_OVERRIDE=""
declare -a IP_CANDIDATES=()
declare -A __SEEN_IPS=()
add_ip_candidate() {
local ip="$1"
[[ -z "$ip" ]] && return
[[ "$ip" == "127."* ]] && return
[[ "$ip" == "0.0.0.0" ]] && return
if [[ -z "${__SEEN_IPS[$ip]}" ]]; then
IP_CANDIDATES+=("$ip")
__SEEN_IPS[$ip]=1
fi
}
is_rfc1918_ip() {
local ip="$1"
case "$ip" in
10.*|192.168.*|172.1[6-9].*|172.2[0-9].*|172.3[0-1].*)
return 0
;;
*)
return 1
;;
esac
}
is_cgnat_ip() {
local ip="$1"
case "$ip" in
100.*)
return 0
;;
*)
return 1
;;
esac
}
is_reserved_benchmark_ip() {
local ip="$1"
case "$ip" in
198.18.*|198.19.*)
return 0
;;
*)
return 1
;;
esac
}
is_link_local_ip() {
local ip="$1"
case "$ip" in
169.254.*)
return 0
;;
*)
return 1
;;
esac
}
is_routable_public_ip() {
local ip="$1"
if [[ -z "$ip" ]]; then
return 1
fi
if is_rfc1918_ip "$ip"; then
return 1
fi
if is_cgnat_ip "$ip"; then
return 1
fi
if is_reserved_benchmark_ip "$ip"; then
return 1
fi
case "$ip" in
127.*|169.254.*)
return 1
;;
*)
return 0
;;
esac
}
collect_ip_candidates() {
IP_CANDIDATES=()
unset __SEEN_IPS
declare -A __SEEN_IPS=()
if command -v hostname >/dev/null 2>&1; then
local host_ips
host_ips=$(hostname -I 2>/dev/null || true)
for ip in $host_ips; do
add_ip_candidate "$ip"
done
fi
if command -v ip >/dev/null 2>&1; then
while IFS= read -r ip; do
add_ip_candidate "$ip"
done < <(ip -o -4 addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1)
fi
if command -v ifconfig >/dev/null 2>&1; then
while IFS= read -r ip; do
add_ip_candidate "$ip"
done < <(ifconfig 2>/dev/null | awk '/inet / {print $2}' | grep -E '^[0-9]+(\.[0-9]+){3}$')
fi
if command -v ip >/dev/null 2>&1; then
local route_ip
route_ip=$(ip route get 1.1.1.1 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}')
add_ip_candidate "$route_ip"
fi
if [[ ${#IP_CANDIDATES[@]} -eq 0 ]]; then
local fallback
fallback=$(hostname -I 2>/dev/null | awk '{print $1}')
add_ip_candidate "$fallback"
fi
}
resolve_local_ip() {
if [[ -n "$LOCAL_IP_OVERRIDE" ]]; then
LOCAL_IP="$LOCAL_IP_OVERRIDE"
return
fi
collect_ip_candidates
if [[ ${#IP_CANDIDATES[@]} -eq 0 ]]; then
LOCAL_IP=""
return
fi
local ip
for ip in "${IP_CANDIDATES[@]}"; do
if is_rfc1918_ip "$ip"; then
LOCAL_IP="$ip"
return
fi
done
for ip in "${IP_CANDIDATES[@]}"; do
if is_cgnat_ip "$ip"; then
LOCAL_IP="$ip"
return
fi
done
for ip in "${IP_CANDIDATES[@]}"; do
if is_reserved_benchmark_ip "$ip"; then
continue
fi
if is_link_local_ip "$ip"; then
continue
fi
LOCAL_IP="$ip"
return
done
LOCAL_IP="${IP_CANDIDATES[0]}"
}
# 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}"
}
# Detect network environment
detect_network_environment() {
log_info "Detecting network environment..."
resolve_local_ip
if [[ -z "$LOCAL_IP" ]]; then
LOCAL_IP="127.0.0.1"
log_warning "Unable to detect host IP; using default: $LOCAL_IP"
fi
if [[ "$FORCED_MODE" == "private" ]]; then
NETWORK_MODE="private"
PUBLIC_IP=""
log_info "Network mode set via parameters: $NETWORK_MODE"
echo " Local IP: $LOCAL_IP"
return 0
fi
local mode_guess="private"
local printed_prompt_info="false"
PUBLIC_IP=""
if curl -s --connect-timeout 5 --max-time 10 ifconfig.me > /dev/null 2>&1; then
PUBLIC_IP=$(curl -s --connect-timeout 5 --max-time 10 ifconfig.me 2>/dev/null || echo "")
if [[ -n "$PUBLIC_IP" ]]; then
if is_routable_public_ip "$PUBLIC_IP"; then
mode_guess="public"
else
log_warning "Public IP is test/reserved range; treating as private"
fi
else
log_warning "Public connectivity unstable; treating as private"
fi
fi
if [[ -z "$FORCED_MODE" ]]; then
if [[ "$mode_guess" == "public" ]]; then
NETWORK_MODE="public"
else
NETWORK_MODE="private"
fi
else
NETWORK_MODE="$FORCED_MODE"
if [[ "$FORCED_MODE" == "public" && -z "$PUBLIC_IP" ]]; then
log_warning "Could not detect public IP; continuing as public mode. Please verify network config"
fi
fi
if [[ "$NETWORK_MODE" != "public" ]]; then
PUBLIC_IP=""
fi
if [[ "$FORCED_MODE" == "public" ]]; then
log_info "Network mode set via parameters: $NETWORK_MODE"
elif [[ "$NETWORK_MODE" == "public" ]]; then
log_success "Public network detected"
else
log_success "Private network detected"
fi
if [[ "$printed_prompt_info" == "false" ]]; then
echo " Local IP: $LOCAL_IP"
if [[ "$NETWORK_MODE" == "public" && -n "$PUBLIC_IP" ]]; then
echo " Public IP: $PUBLIC_IP"
fi
fi
}
# Check system resources
check_system_resources() {
log_info "Checking system resources..."
local warnings=0
# Check memory
if command -v free >/dev/null 2>&1; then
TOTAL_MEM=$(free -m | awk 'NR==2{print $2}')
if [[ $TOTAL_MEM -lt 512 ]]; then
log_error "Insufficient memory: ${TOTAL_MEM}MB (512MB+ recommended)"
return 1
elif [[ $TOTAL_MEM -lt 1024 ]]; then
log_warning "Low memory: ${TOTAL_MEM}MB (1GB+ recommended)"
warnings=$((warnings + 1))
else
log_success "Memory OK: ${TOTAL_MEM}MB"
fi
else
log_warning "Unable to read memory usage"
warnings=$((warnings + 1))
fi
# Check disk usage
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | sed 's/%//')
if [[ $DISK_USAGE -gt 95 ]]; then
log_error "Insufficient disk space: ${DISK_USAGE}% used"
return 1
elif [[ $DISK_USAGE -gt 80 ]]; then
log_warning "Disk space tight: ${DISK_USAGE}% used"
warnings=$((warnings + 1))
else
log_success "Disk space OK: ${DISK_USAGE}% used"
fi
# Check available disk space
AVAILABLE_SPACE=$(df -BG / | awk 'NR==2{print $4}' | sed 's/G//')
if [[ $AVAILABLE_SPACE -lt 2 ]]; then
log_error "Not enough free disk space: ${AVAILABLE_SPACE}GB (2GB+ recommended)"
return 1
fi
if [[ $warnings -gt 0 ]]; then
log_warning "System resource check passed with $warnings warning(s)"
else
log_success "System resource check passed"
fi
return 0
}
# Validate Docker environment
verify_docker_installation() {
log_info "Checking Docker environment..."
if ! command -v docker &> /dev/null; then
log_error "Docker is not installed"
echo "Please install Docker: https://docs.docker.com/get-docker/"
return 1
fi
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
log_error "Docker Compose is not installed"
echo "Please install Docker Compose: https://docs.docker.com/compose/install/"
return 1
fi
# Check Docker service status
if ! docker info &> /dev/null; then
log_error "Docker service is not running"
echo "Please start the Docker service"
return 1
fi
# Check Docker version
DOCKER_VERSION=$(docker --version | grep -oE '[0-9]+\.[0-9]+' | head -1)
log_success "Docker version: $DOCKER_VERSION"
# Check Docker Compose version
if command -v docker-compose &> /dev/null; then
COMPOSE_VERSION=$(docker-compose --version | grep -oE '[0-9]+\.[0-9]+' | head -1)
log_success "Docker Compose version: $COMPOSE_VERSION"
else
COMPOSE_VERSION=$(docker compose version --short 2>/dev/null || echo "built-in")
log_success "Docker Compose version: $COMPOSE_VERSION"
fi
return 0
}
# Check port usage
check_port_availability() {
local ports="$1"
log_info "Checking port usage..."
local occupied_ports=()
IFS=',' read -ra PORT_ARRAY <<< "$ports"
for port in "${PORT_ARRAY[@]}"; do
port=$(echo "$port" | xargs) # Trim spaces
if command -v ss >/dev/null 2>&1; then
if ss -tuln | grep -q ":$port "; then
occupied_ports+=("$port")
fi
elif command -v netstat >/dev/null 2>&1; then
if netstat -tuln 2>/dev/null | grep -q ":$port "; then
occupied_ports+=("$port")
fi
else
log_warning "Unable to check port usage (missing ss and netstat)"
return 0
fi
done
if [[ ${#occupied_ports[@]} -gt 0 ]]; then
log_warning "Ports in use: ${occupied_ports[*]}"
log_info "Change ports in .env, or run './deploy.sh --clean' / 'docker-compose down' to clean old containers"
else
log_success "All ports available"
fi
}
# Detect deployment mode
detect_deployment_mode() {
log_info "Determining deployment mode..."
if [[ "$NETWORK_MODE" == "public" ]] && [[ -n "$DOMAIN_NAME" ]]; then
DEPLOYMENT_MODE="full"
log_success "Deployment mode: full (HTTPS + TURN server)"
elif [[ "$NETWORK_MODE" == "public" ]]; then
DEPLOYMENT_MODE="public"
log_success "Deployment mode: public (HTTP + TURN)"
else
DEPLOYMENT_MODE="basic"
log_success "Deployment mode: basic (intranet HTTP)"
fi
}
# Main function
main() {
echo -e "${BLUE}=== PrivyDrop Docker Environment Check ===${NC}\n"
# Parse command-line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--domain)
DOMAIN_NAME="$2"
shift 2
;;
--mode)
DEPLOYMENT_MODE="$2"
case "$2" in
private|basic)
FORCED_MODE="private"
;;
public|full)
FORCED_MODE="public"
;;
*)
FORCED_MODE=""
;;
esac
shift 2
;;
--local-ip)
LOCAL_IP_OVERRIDE="$2"
shift 2
;;
*)
shift
;;
esac
done
# Run checks
detect_network_environment
echo ""
if ! check_system_resources; then
log_error "System resource check failed; resolve resource issues and retry"
exit 1
fi
echo ""
if ! verify_docker_installation; then
log_error "Docker environment check failed; please install and start Docker"
exit 1
fi
echo ""
check_port_availability "80,443,3002,3001,3478,5349,6379"
echo ""
detect_deployment_mode
echo ""
log_success "Environment check complete!"
echo -e "${BLUE}Results:${NC}"
echo " Network mode: $NETWORK_MODE"
echo " Local IP: $LOCAL_IP"
[[ -n "$PUBLIC_IP" ]] && echo " Public IP: $PUBLIC_IP"
echo " Deployment mode: $DEPLOYMENT_MODE"
# Export env vars for other scripts
export NETWORK_MODE
export LOCAL_IP
export PUBLIC_IP
export DEPLOYMENT_MODE
export DOMAIN_NAME
export LOCAL_IP_OVERRIDE
return 0
}
# If the script is executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi