#!/bin/bash # Tailscale DERP 服务器一键安装脚本 (支持自签名IP证书) # 本脚本适用于 Ubuntu/Debian 系统 set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 日志函数 log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } # 检查是否为root用户 check_root() { if [[ $EUID -ne 0 ]]; then log_error "此脚本需要以root权限运行" exit 1 fi } # 检查系统类型 check_system() { if [[ ! -f /etc/os-release ]]; then log_error "无法检测系统类型" exit 1 fi source /etc/os-release case $ID in ubuntu|debian) PACKAGE_MANAGER="apt" ;; centos|rhel|fedora) PACKAGE_MANAGER="yum" ;; *) log_error "不支持的系统类型: $ID" exit 1 ;; esac log_info "检测到系统: $PRETTY_NAME" } # 安装依赖 install_dependencies() { log_info "安装依赖包..." case $PACKAGE_MANAGER in apt) apt update apt install -y curl wget git build-essential openssl nginx ;; yum) yum update -y yum install -y curl wget git gcc make openssl nginx ;; esac log_info "依赖包安装完成" } # 启用BBR优化 enable_bbr() { log_info "启用BBR网络优化..." # 检查内核版本 KERNEL_VERSION=$(uname -r | cut -d. -f1-2) KERNEL_MAJOR=$(echo $KERNEL_VERSION | cut -d. -f1) KERNEL_MINOR=$(echo $KERNEL_VERSION | cut -d. -f2) if [[ $KERNEL_MAJOR -lt 4 ]] || [[ $KERNEL_MAJOR -eq 4 && $KERNEL_MINOR -lt 9 ]]; then log_warn "内核版本过低 ($KERNEL_VERSION),BBR需要4.9+版本" return fi # 检查BBR是否已启用 if sysctl net.ipv4.tcp_congestion_control | grep -q bbr; then log_info "BBR已经启用" return fi # 备份原始配置 cp /etc/sysctl.conf /etc/sysctl.conf.backup.$(date +%Y%m%d_%H%M%S) # 添加BBR配置 cat >> /etc/sysctl.conf << EOF # BBR网络优化配置 net.core.default_qdisc = fq net.ipv4.tcp_congestion_control = bbr # 其他网络优化参数 net.ipv4.tcp_rmem = 8192 262144 536870912 net.ipv4.tcp_wmem = 4096 16384 536870912 net.core.rmem_max = 536870912 net.core.wmem_max = 536870912 net.core.netdev_max_backlog = 30000 net.ipv4.tcp_slow_start_after_idle = 0 EOF # 应用配置 sysctl -p # 验证BBR是否启用成功 if sysctl net.ipv4.tcp_congestion_control | grep -q bbr; then log_info "BBR启用成功" else log_error "BBR启用失败" fi # 显示当前拥塞控制算法 log_info "当前拥塞控制算法: $(sysctl -n net.ipv4.tcp_congestion_control)" } # 安装Go环境 install_go() { log_info "安装Go环境..." # 检查Go是否已安装 if command -v go &> /dev/null; then log_info "Go已经安装,版本: $(go version)" # 设置Go代理 export GOPROXY=https://goproxy.cn,direct echo 'export GOPROXY=https://goproxy.cn,direct' >> /etc/profile return fi # 下载并安装Go GO_VERSION="1.23.3" wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz tar -zxvf go${GO_VERSION}.linux-amd64.tar.gz rm go${GO_VERSION}.linux-amd64.tar.gz # 移动到正确位置 mv go /usr/local/ # 设置环境变量 export PATH=$PATH:/usr/local/go/bin echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile # 设置Go代理 export GOPROXY=https://goproxy.cn,direct echo 'export GOPROXY=https://goproxy.cn,direct' >> /etc/profile log_info "Go环境安装完成" } # 编译DERP服务器 compile_derp() { log_info "编译DERP服务器..." # 创建工作目录 mkdir -p /home/derp cd /home/derp # 设置Go代理 export GOPROXY=https://goproxy.cn,direct # 直接从源码构建derper二进制文件 log_info "从源码构建derper二进制文件..." /usr/local/go/bin/go install tailscale.com/cmd/derper@main # 查找编译后的二进制文件 GOPATH=$(go env GOPATH) if [[ -z "$GOPATH" ]]; then GOPATH="$HOME/go" fi # 复制到工作目录 if [[ -f "$GOPATH/bin/derper" ]]; then cp "$GOPATH/bin/derper" /home/derp/ else log_error "未找到编译后的derper二进制文件" exit 1 fi # 验证编译结果 if [[ ! -f /home/derp/derper ]]; then log_error "DERP服务器编译失败" exit 1 fi log_info "DERP服务器编译完成" } # 配置用户提供的证书 configure_user_cert() { log_info "配置用户提供的证书..." # 获取服务器IP地址 read -p "请输入服务器IP地址: " SERVER_IP if [[ -z "$SERVER_IP" ]]; then log_error "IP地址不能为空" exit 1 fi # 获取证书文件路径 read -p "请输入证书文件路径 (.crt/.pem文件): " CERT_PATH if [[ -z "$CERT_PATH" ]]; then log_error "证书文件路径不能为空" exit 1 fi if [[ ! -f "$CERT_PATH" ]]; then log_error "证书文件不存在: $CERT_PATH" exit 1 fi # 获取私钥文件路径 read -p "请输入私钥文件路径 (.key文件): " KEY_PATH if [[ -z "$KEY_PATH" ]]; then log_error "私钥文件路径不能为空" exit 1 fi if [[ ! -f "$KEY_PATH" ]]; then log_error "私钥文件不存在: $KEY_PATH" exit 1 fi # 创建证书目录 mkdir -p /etc/derper/certs # 复制证书文件 cp "$CERT_PATH" /etc/derper/certs/server.crt cp "$KEY_PATH" /etc/derper/certs/server.key # 设置证书权限 chmod 600 /etc/derper/certs/server.key chmod 644 /etc/derper/certs/server.crt # 验证证书 if ! openssl x509 -in /etc/derper/certs/server.crt -noout -text > /dev/null 2>&1; then log_error "证书文件格式无效" exit 1 fi # 验证私钥 if ! openssl rsa -in /etc/derper/certs/server.key -check -noout > /dev/null 2>&1; then log_error "私钥文件格式无效" exit 1 fi # 计算证书哈希 CERT_HASH=$(openssl x509 -in /etc/derper/certs/server.crt -noout -fingerprint -sha256 | cut -d= -f2 | tr -d : | tr '[:upper:]' '[:lower:]') log_info "证书配置完成" log_info "证书SHA256哈希: $CERT_HASH" # 保存配置信息 echo "SERVER_IP=$SERVER_IP" > /etc/derper/config echo "CERT_HASH=$CERT_HASH" >> /etc/derper/config echo "CERT_PATH=$CERT_PATH" >> /etc/derper/config echo "KEY_PATH=$KEY_PATH" >> /etc/derper/config } # 安装Tailscale客户端 install_tailscale() { log_info "安装Tailscale客户端..." # 使用xEdge镜像安装Tailscale (国内优化) log_info "使用xEdge镜像安装Tailscale..." curl -fsSL https://ts-mirror.xedge.cc/install.sh | sh # 如果xEdge镜像失败,回退到官方安装 if ! command -v tailscale &> /dev/null; then log_warn "xEdge镜像安装失败,尝试官方镜像..." curl -fsSL https://tailscale.com/install.sh | sh fi log_info "Tailscale客户端安装完成" log_warn "请运行 'tailscale up' 来连接到您的Tailnet网络" } # 创建systemd服务 create_systemd_service() { log_info "创建systemd服务..." # 读取配置 source /etc/derper/config # 显示运行模式选择 echo "请选择DERP服务器运行模式:" echo "1. 直接使用自签名证书的HTTPS模式 (推荐)" echo "2. Nginx反向代理模式" echo "3. 自定义配置" read -p "请输入选择 (1-3): " DERP_MODE case $DERP_MODE in 1) # 直接HTTPS模式,使用自签名证书 DERP_CMD="/home/derp/derper -hostname $SERVER_IP -certmode manual -certdir /etc/derper/certs -a :443 -http-port 80 -stun-port 3478 -verify-clients" ;; 2) # Nginx反向代理模式 DERP_CMD="/home/derp/derper -hostname $SERVER_IP -a :8080 -stun-port 3478 -verify-clients" ;; 3) # 自定义配置 read -p "请输入自定义命令参数: " CUSTOM_ARGS DERP_CMD="/home/derp/derper -hostname $SERVER_IP $CUSTOM_ARGS" ;; *) log_warn "无效选择,使用默认配置" DERP_CMD="/home/derp/derper -hostname $SERVER_IP -a :8080 -stun-port 3478 -verify-clients" ;; esac cat > /etc/systemd/system/derper.service << EOF [Unit] Description=Tailscale DERP Server After=network.target [Service] Type=simple User=root WorkingDirectory=/home/derp ExecStart=$DERP_CMD Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF # 保存运行模式到配置文件 echo "DERP_MODE=$DERP_MODE" >> /etc/derper/config echo "DERP_CMD='$DERP_CMD'" >> /etc/derper/config # 重载systemd并启动服务 systemctl daemon-reload systemctl enable derper log_info "systemd服务创建完成" } # 配置Nginx反向代理 configure_nginx() { # 读取配置 source /etc/derper/config # 只有在Nginx反向代理模式下才配置Nginx if [[ "$DERP_MODE" != "2" ]]; then log_info "跳过Nginx配置(不需要反向代理)" return fi log_info "配置Nginx反向代理..." # 创建Nginx配置 cat > /etc/nginx/sites-available/derper << EOF server { listen 80; server_name $SERVER_IP; location /derp { proxy_pass http://127.0.0.1:8080; 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支持 proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "upgrade"; } } server { listen 443 ssl; server_name $SERVER_IP; ssl_certificate /etc/derper/certs/server.crt; ssl_certificate_key /etc/derper/certs/server.key; location /derp { proxy_pass http://127.0.0.1:8080; 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支持 proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "upgrade"; } } EOF # 启用站点 ln -sf /etc/nginx/sites-available/derper /etc/nginx/sites-enabled/ # 测试配置并重启Nginx nginx -t && systemctl restart nginx log_info "Nginx配置完成" } # 生成官方Tailscale配置信息 generate_tailscale_config() { log_info "生成官方Tailscale配置信息..." # 读取配置 source /etc/derper/config # 创建配置信息文件 mkdir -p /etc/derper # 生成官方Tailscale ACL配置示例 # 读取运行模式 DERP_PORT=443 if [[ "$DERP_MODE" == "2" ]]; then DERP_PORT=443 # Nginx反向代理会处理HTTPS fi cat > /etc/derper/tailscale_acl.json << EOF { "derpMap": { "Regions": { "900": { "RegionID": 900, "RegionCode": "custom", "RegionName": "Custom DERP Server", "Nodes": [ { "Name": "derp-$SERVER_IP", "RegionID": 900, "HostName": "$SERVER_IP", "IPv4": "$SERVER_IP", "CertName": "sha256:$CERT_HASH", "DERPPort": $DERP_PORT, "STUNPort": 3478 } ] } } } } EOF log_info "官方Tailscale配置信息已生成: /etc/derper/tailscale_acl.json" } # 启动服务 start_services() { log_info "启动服务..." # 启动DERP服务 systemctl start derper # 检查服务状态 if systemctl is-active --quiet derper; then log_info "DERP服务启动成功" else log_error "DERP服务启动失败" systemctl status derper exit 1 fi # 只有在Nginx反向代理模式下才启动Nginx if [[ "$DERP_MODE" == "2" ]]; then systemctl enable nginx systemctl start nginx log_info "Nginx服务启动完成" fi log_info "所有服务启动完成" } # 显示配置信息 show_config_info() { log_info "安装完成!" # 读取配置 source /etc/derper/config echo echo "=================================" echo "DERP服务器配置信息" echo "=================================" echo "服务器IP: $SERVER_IP" echo "证书SHA256: $CERT_HASH" echo "运行模式: $DERP_MODE" case $DERP_MODE in 1) echo "访问方式: 直接HTTPS访问" echo "DERP端口: 443 (HTTPS)" echo "HTTP端口: 80 (自动跳转到HTTPS)" echo "STUN端口: 3478" ;; 2) echo "访问方式: Nginx反向代理" echo "DERP端口: 443 (HTTPS, 通过Nginx)" echo "内部端口: 8080 (HTTP)" echo "STUN端口: 3478" ;; 3) echo "访问方式: 自定义配置" echo "命令参数: $DERP_CMD" ;; esac echo "配置文件: /etc/derper/tailscale_acl.json" echo echo "服务管理命令:" echo " 启动: systemctl start derper" echo " 停止: systemctl stop derper" echo " 重启: systemctl restart derper" echo " 状态: systemctl status derper" echo echo "测试连接:" echo " curl https://$SERVER_IP/derp" echo echo "官方Tailscale配置:" echo " 配置文件: /etc/derper/tailscale_acl.json" echo " 在Tailscale Admin Console中添加自定DERP服务器" echo " 访问: https://login.tailscale.com/admin/dns" echo echo "重要提示:" echo " 1. 请确保防火墙开放443端口" echo " 2. 运行 'tailscale up' 连接到Tailnet" echo " 3. 在Tailscale Admin Console中添加DERP服务器" echo " 4. 使用自签名证书需要在ACL中指定证书哈希" echo "=================================" } # 主函数 main() { log_info "开始安装Tailscale DERP服务器..." check_root check_system install_dependencies enable_bbr install_go compile_derp configure_user_cert install_tailscale create_systemd_service configure_nginx generate_tailscale_config start_services show_config_info log_info "安装完成!" } # 运行主函数 main "$@"