mirror of
https://github.com/Gouryella/drip.git
synced 2026-02-26 22:31:35 +00:00
Updated client and server installation scripts to automatically fetch the latest release via the GitHub API and corrected binary download addresses. Removed the old "latest" version logic to ensure explicit version tags are always used for downloads. fix(readme): Fixed installation commands and license information in the README. Corrected the installation script link pointing to an incorrect URL in the README to the correct path raw.githubusercontent.com, and updated the open-source license information used by the project from MIT to BSD 3-Clause. ci(release): Added an automated GitHub Actions release workflow. Added a new CI workflow configuration file to trigger the build process when a tag is pushed. This workflow compiles binaries for multiple platforms, generates checksums, and creates a GitHub Release with attachments. Supports different architectures including Linux, macOS, and Windows.
1011 lines
34 KiB
Bash
Executable File
1011 lines
34 KiB
Bash
Executable File
#!/bin/bash
|
||
set -e
|
||
|
||
# ============================================================================
|
||
# Configuration
|
||
# ============================================================================
|
||
GITHUB_REPO="Gouryella/drip"
|
||
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
|
||
SERVICE_USER="${SERVICE_USER:-drip}"
|
||
CONFIG_DIR="/etc/drip"
|
||
WORK_DIR="/var/lib/drip"
|
||
VERSION="${VERSION:-}"
|
||
|
||
# Default values
|
||
DEFAULT_PORT=8443
|
||
DEFAULT_TCP_PORT_MIN=20000
|
||
DEFAULT_TCP_PORT_MAX=40000
|
||
|
||
# Colors
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
BOLD='\033[1m'
|
||
NC='\033[0m'
|
||
|
||
# Language (default: en)
|
||
LANG_CODE="${LANG_CODE:-en}"
|
||
|
||
# ============================================================================
|
||
# Internationalization
|
||
# ============================================================================
|
||
declare -A MSG_EN
|
||
declare -A MSG_ZH
|
||
|
||
# English messages
|
||
MSG_EN=(
|
||
["banner_title"]="Drip Server - One-Click Installer"
|
||
["select_lang"]="Select language / 选择语言"
|
||
["lang_en"]="English"
|
||
["lang_zh"]="中文"
|
||
["checking_root"]="Checking root privileges..."
|
||
["need_root"]="This script requires root privileges. Please run with sudo."
|
||
["checking_os"]="Checking operating system..."
|
||
["unsupported_os"]="Unsupported operating system. Only Linux is supported."
|
||
["checking_arch"]="Checking system architecture..."
|
||
["unsupported_arch"]="Unsupported architecture. Only amd64 and arm64 are supported."
|
||
["checking_deps"]="Checking dependencies..."
|
||
["installing_deps"]="Installing dependencies..."
|
||
["deps_ok"]="Dependencies check passed"
|
||
["downloading"]="Downloading Drip server"
|
||
["download_failed"]="Download failed"
|
||
["download_ok"]="Download completed"
|
||
["installing"]="Installing binary..."
|
||
["install_ok"]="Binary installed successfully"
|
||
["config_title"]="Server Configuration"
|
||
["enter_domain"]="Enter your domain (e.g., tunnel.example.com)"
|
||
["domain_required"]="Domain is required"
|
||
["enter_port"]="Enter server port"
|
||
["enter_token"]="Enter authentication token (leave empty to generate)"
|
||
["token_generated"]="Token generated"
|
||
["enter_cert_path"]="Enter TLS certificate path (public key)"
|
||
["enter_key_path"]="Enter TLS private key path"
|
||
["cert_not_found"]="Certificate file not found"
|
||
["key_not_found"]="Private key file not found"
|
||
["cert_option_title"]="TLS Certificate Configuration"
|
||
["cert_option_certbot"]="Use Let's Encrypt (recommended)"
|
||
["cert_option_self"]="Generate self-signed certificate (10 years)"
|
||
["cert_option_provide"]="Provide your own certificate"
|
||
["enter_email"]="Enter email for Let's Encrypt (optional, press Enter to skip)"
|
||
["obtaining_cert"]="Obtaining Let's Encrypt certificate..."
|
||
["cert_obtained"]="Certificate obtained successfully"
|
||
["cert_failed"]="Failed to obtain certificate. Please check domain DNS settings."
|
||
["generating_cert"]="Generating self-signed ECDSA certificate..."
|
||
["cert_generated"]="Self-signed certificate generated"
|
||
["cert_warning"]="Note: Self-signed certificates require --insecure flag on client"
|
||
["cert_exists"]="Certificate already exists, skipping..."
|
||
["certbot_domain_note"]="Make sure your domain DNS points to this server before proceeding"
|
||
["creating_user"]="Creating service user..."
|
||
["user_exists"]="User already exists"
|
||
["user_created"]="Service user created"
|
||
["creating_service"]="Creating systemd service..."
|
||
["service_created"]="Systemd service created"
|
||
["configuring_firewall"]="Configuring firewall..."
|
||
["firewall_ok"]="Firewall configured"
|
||
["saving_config"]="Saving configuration..."
|
||
["config_saved"]="Configuration saved"
|
||
["start_now"]="Start service now?"
|
||
["starting_service"]="Starting service..."
|
||
["service_started"]="Service started successfully"
|
||
["service_failed"]="Service failed to start"
|
||
["install_complete"]="Installation completed!"
|
||
["client_info"]="Client connection info"
|
||
["server_addr"]="Server"
|
||
["token_label"]="Token"
|
||
["service_commands"]="Service management commands"
|
||
["cmd_start"]="Start service"
|
||
["cmd_stop"]="Stop service"
|
||
["cmd_restart"]="Restart service"
|
||
["cmd_status"]="Check status"
|
||
["cmd_logs"]="View logs"
|
||
["cmd_enable"]="Enable auto-start"
|
||
["yes"]="y"
|
||
["no"]="n"
|
||
["press_enter"]="Press Enter to continue..."
|
||
["tcp_port_range"]="TCP tunnel port range"
|
||
["enter_tcp_min"]="Enter minimum TCP port"
|
||
["enter_tcp_max"]="Enter maximum TCP port"
|
||
["invalid_port"]="Invalid port number"
|
||
["enter_public_port"]="Enter public port (for URL display, e.g., behind reverse proxy)"
|
||
["wildcard_cert_note"]="Note: For subdomain tunnels, you need a wildcard certificate (*.domain.com)"
|
||
["already_installed"]="Drip server is already installed"
|
||
["current_version"]="Current version"
|
||
["update_now"]="Update to the latest version?"
|
||
["updating"]="Updating..."
|
||
["update_ok"]="Update completed"
|
||
["skip_update"]="Skipping update"
|
||
["service_not_found"]="Systemd service not found. Proceeding with full configuration..."
|
||
["copying_cert"]="Copying certificate with proper permissions..."
|
||
["cert_copied"]="Certificate copied successfully"
|
||
["creating_renewal_hook"]="Creating certificate renewal hook..."
|
||
["renewal_hook_created"]="Renewal hook created"
|
||
)
|
||
|
||
# Chinese messages
|
||
MSG_ZH=(
|
||
["banner_title"]="Drip 服务器 - 一键安装脚本"
|
||
["select_lang"]="Select language / 选择语言"
|
||
["lang_en"]="English"
|
||
["lang_zh"]="中文"
|
||
["checking_root"]="检查 root 权限..."
|
||
["need_root"]="此脚本需要 root 权限,请使用 sudo 运行。"
|
||
["checking_os"]="检查操作系统..."
|
||
["unsupported_os"]="不支持的操作系统,仅支持 Linux。"
|
||
["checking_arch"]="检查系统架构..."
|
||
["unsupported_arch"]="不支持的架构,仅支持 amd64 和 arm64。"
|
||
["checking_deps"]="检查依赖..."
|
||
["installing_deps"]="安装依赖..."
|
||
["deps_ok"]="依赖检查通过"
|
||
["downloading"]="下载 Drip 服务器"
|
||
["download_failed"]="下载失败"
|
||
["download_ok"]="下载完成"
|
||
["installing"]="安装二进制文件..."
|
||
["install_ok"]="二进制文件安装成功"
|
||
["config_title"]="服务器配置"
|
||
["enter_domain"]="输入你的域名(例如:tunnel.example.com)"
|
||
["domain_required"]="域名是必填项"
|
||
["enter_port"]="输入服务器端口"
|
||
["enter_token"]="输入认证令牌(留空自动生成)"
|
||
["token_generated"]="令牌已生成"
|
||
["enter_cert_path"]="输入 TLS 证书路径(公钥)"
|
||
["enter_key_path"]="输入 TLS 私钥路径"
|
||
["cert_not_found"]="证书文件未找到"
|
||
["key_not_found"]="私钥文件未找到"
|
||
["cert_option_title"]="TLS 证书配置"
|
||
["cert_option_certbot"]="使用 Let's Encrypt 自动获取(推荐)"
|
||
["cert_option_self"]="生成自签名证书(10 年有效期)"
|
||
["cert_option_provide"]="使用自己的证书"
|
||
["enter_email"]="输入 Let's Encrypt 邮箱(可选,直接回车跳过)"
|
||
["obtaining_cert"]="正在获取 Let's Encrypt 证书..."
|
||
["cert_obtained"]="证书获取成功"
|
||
["cert_failed"]="证书获取失败,请检查域名 DNS 是否正确指向本服务器"
|
||
["generating_cert"]="正在生成自签名 ECDSA 证书..."
|
||
["cert_generated"]="自签名证书已生成"
|
||
["cert_warning"]="提示:自签名证书需要客户端使用 --insecure 参数"
|
||
["cert_exists"]="证书已存在,跳过..."
|
||
["certbot_domain_note"]="请确保域名 DNS 已指向本服务器再继续"
|
||
["creating_user"]="创建服务用户..."
|
||
["user_exists"]="用户已存在"
|
||
["user_created"]="服务用户创建完成"
|
||
["creating_service"]="创建 systemd 服务..."
|
||
["service_created"]="systemd 服务创建完成"
|
||
["configuring_firewall"]="配置防火墙..."
|
||
["firewall_ok"]="防火墙配置完成"
|
||
["saving_config"]="保存配置..."
|
||
["config_saved"]="配置已保存"
|
||
["start_now"]="是否立即启动服务?"
|
||
["starting_service"]="正在启动服务..."
|
||
["service_started"]="服务启动成功"
|
||
["service_failed"]="服务启动失败"
|
||
["install_complete"]="安装完成!"
|
||
["client_info"]="客户端连接信息"
|
||
["server_addr"]="服务器"
|
||
["token_label"]="令牌"
|
||
["service_commands"]="服务管理命令"
|
||
["cmd_start"]="启动服务"
|
||
["cmd_stop"]="停止服务"
|
||
["cmd_restart"]="重启服务"
|
||
["cmd_status"]="查看状态"
|
||
["cmd_logs"]="查看日志"
|
||
["cmd_enable"]="开机启动"
|
||
["yes"]="y"
|
||
["no"]="n"
|
||
["press_enter"]="按 Enter 继续..."
|
||
["tcp_port_range"]="TCP 隧道端口范围"
|
||
["enter_tcp_min"]="输入最小 TCP 端口"
|
||
["enter_tcp_max"]="输入最大 TCP 端口"
|
||
["invalid_port"]="无效的端口号"
|
||
["enter_public_port"]="输入公开端口(用于 URL 显示,如在反向代理后)"
|
||
["wildcard_cert_note"]="注意:要支持子域名隧道,需要通配符证书(*.domain.com)"
|
||
["already_installed"]="Drip 服务器已安装"
|
||
["current_version"]="当前版本"
|
||
["update_now"]="是否更新到最新版本?"
|
||
["updating"]="正在更新..."
|
||
["update_ok"]="更新完成"
|
||
["skip_update"]="跳过更新"
|
||
["service_not_found"]="Systemd 服务未找到,将进行完整配置..."
|
||
["copying_cert"]="正在复制证书并设置权限..."
|
||
["cert_copied"]="证书复制成功"
|
||
["creating_renewal_hook"]="创建证书续期钩子..."
|
||
["renewal_hook_created"]="续期钩子创建完成"
|
||
)
|
||
|
||
# Get message by key
|
||
msg() {
|
||
local key="$1"
|
||
if [[ "$LANG_CODE" == "zh" ]]; then
|
||
echo "${MSG_ZH[$key]:-$key}"
|
||
else
|
||
echo "${MSG_EN[$key]:-$key}"
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# Output functions
|
||
# ============================================================================
|
||
print_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||
print_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||
print_warning() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||
print_error() { echo -e "${RED}[✗]${NC} $1"; }
|
||
print_step() { echo -e "${CYAN}[→]${NC} $1"; }
|
||
|
||
# Print banner
|
||
print_banner() {
|
||
echo -e "${GREEN}"
|
||
cat << "EOF"
|
||
____ _
|
||
/ __ \_____(_)___
|
||
/ / / / ___/ / __ \
|
||
/ /_/ / / / / /_/ /
|
||
/_____/_/ /_/ .___/
|
||
/_/
|
||
EOF
|
||
echo -e "${BOLD}$(msg banner_title)${NC}"
|
||
echo ""
|
||
}
|
||
|
||
# ============================================================================
|
||
# Language selection
|
||
# ============================================================================
|
||
select_language() {
|
||
echo ""
|
||
echo -e "${CYAN}╔════════════════════════════════════════╗${NC}"
|
||
echo -e "${CYAN}║ $(msg select_lang) ║${NC}"
|
||
echo -e "${CYAN}╠════════════════════════════════════════╣${NC}"
|
||
echo -e "${CYAN}║${NC} ${GREEN}1)${NC} English ${CYAN}║${NC}"
|
||
echo -e "${CYAN}║${NC} ${GREEN}2)${NC} 中文 ${CYAN}║${NC}"
|
||
echo -e "${CYAN}╚════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
|
||
read -p "Select [1]: " lang_choice < /dev/tty
|
||
case "$lang_choice" in
|
||
2)
|
||
LANG_CODE="zh"
|
||
;;
|
||
*)
|
||
LANG_CODE="en"
|
||
;;
|
||
esac
|
||
echo ""
|
||
}
|
||
|
||
# ============================================================================
|
||
# System checks
|
||
# ============================================================================
|
||
check_root() {
|
||
print_step "$(msg checking_root)"
|
||
if [[ $EUID -ne 0 ]]; then
|
||
print_error "$(msg need_root)"
|
||
exit 1
|
||
fi
|
||
print_success "root"
|
||
}
|
||
|
||
check_os() {
|
||
print_step "$(msg checking_os)"
|
||
if [[ "$(uname)" != "Linux" ]]; then
|
||
print_error "$(msg unsupported_os)"
|
||
exit 1
|
||
fi
|
||
|
||
# Detect distribution (use subshell to avoid variable pollution)
|
||
if [[ -f /etc/os-release ]]; then
|
||
OS_NAME=$(. /etc/os-release && echo "$ID")
|
||
OS_VERSION_ID=$(. /etc/os-release && echo "$VERSION_ID")
|
||
else
|
||
OS_NAME="unknown"
|
||
fi
|
||
|
||
print_success "Linux ($OS_NAME)"
|
||
}
|
||
|
||
check_arch() {
|
||
print_step "$(msg checking_arch)"
|
||
ARCH=$(uname -m)
|
||
case "$ARCH" in
|
||
x86_64)
|
||
ARCH="amd64"
|
||
;;
|
||
aarch64|arm64)
|
||
ARCH="arm64"
|
||
;;
|
||
*)
|
||
print_error "$(msg unsupported_arch): $ARCH"
|
||
exit 1
|
||
;;
|
||
esac
|
||
print_success "$ARCH"
|
||
}
|
||
|
||
check_dependencies() {
|
||
print_step "$(msg checking_deps)"
|
||
|
||
local deps_to_install=()
|
||
|
||
# Check curl or wget
|
||
if ! command -v curl &> /dev/null && ! command -v wget &> /dev/null; then
|
||
deps_to_install+=("curl")
|
||
fi
|
||
|
||
# Install missing dependencies
|
||
if [[ ${#deps_to_install[@]} -gt 0 ]]; then
|
||
print_info "$(msg installing_deps)"
|
||
|
||
if command -v apt-get &> /dev/null; then
|
||
apt-get update -qq
|
||
apt-get install -y -qq "${deps_to_install[@]}"
|
||
elif command -v yum &> /dev/null; then
|
||
yum install -y -q "${deps_to_install[@]}"
|
||
elif command -v dnf &> /dev/null; then
|
||
dnf install -y -q "${deps_to_install[@]}"
|
||
elif command -v pacman &> /dev/null; then
|
||
pacman -Sy --noconfirm "${deps_to_install[@]}"
|
||
fi
|
||
fi
|
||
|
||
print_success "$(msg deps_ok)"
|
||
}
|
||
|
||
get_latest_version() {
|
||
# Get latest version from GitHub API
|
||
local api_url="https://api.github.com/repos/${GITHUB_REPO}/releases/latest"
|
||
local version=""
|
||
|
||
if command -v curl &> /dev/null; then
|
||
version=$(curl -fsSL "$api_url" | grep '"tag_name":' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/' 2>/dev/null)
|
||
else
|
||
version=$(wget -qO- "$api_url" | grep '"tag_name":' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/' 2>/dev/null)
|
||
fi
|
||
|
||
if [[ -z "$version" ]]; then
|
||
print_error "Failed to get latest version from GitHub"
|
||
exit 1
|
||
fi
|
||
|
||
echo "$version"
|
||
}
|
||
|
||
|
||
check_existing_install() {
|
||
local server_path="$INSTALL_DIR/drip"
|
||
|
||
if [[ -f "$server_path" ]]; then
|
||
local current_version=$("$server_path" version 2>/dev/null | awk '/Version:/ {print $2}' || echo "unknown")
|
||
|
||
print_warning "$(msg already_installed): $server_path"
|
||
print_info "$(msg current_version): $current_version"
|
||
|
||
# Check remote version
|
||
print_step "Checking for updates..."
|
||
local latest_version=$(get_latest_version)
|
||
|
||
if [[ "$current_version" == "$latest_version" ]]; then
|
||
print_success "Already up to date ($current_version)"
|
||
exit 0
|
||
else
|
||
print_info "Latest version: $latest_version"
|
||
echo ""
|
||
read -p "$(msg update_now) [Y/n]: " update_choice < /dev/tty
|
||
fi
|
||
|
||
if [[ "$update_choice" =~ ^[Nn]$ ]]; then
|
||
print_info "$(msg skip_update)"
|
||
exit 0
|
||
fi
|
||
|
||
IS_UPDATE=true
|
||
|
||
# Stop service if running
|
||
if systemctl is-active --quiet drip-server 2>/dev/null; then
|
||
print_info "Stopping drip-server service..."
|
||
systemctl stop drip-server
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# Download and install
|
||
# ============================================================================
|
||
download_binary() {
|
||
# Get latest version if not set
|
||
if [[ -z "$VERSION" ]]; then
|
||
VERSION=$(get_latest_version)
|
||
fi
|
||
|
||
if [[ "$IS_UPDATE" == true ]]; then
|
||
print_step "$(msg updating)"
|
||
else
|
||
print_step "$(msg downloading)..."
|
||
fi
|
||
|
||
local binary_name="drip-${VERSION}-linux-${ARCH}"
|
||
local download_url="https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/${binary_name}"
|
||
|
||
local tmp_file="/tmp/drip"
|
||
|
||
if command -v curl &> /dev/null; then
|
||
# Use -# for progress bar instead of -s (silent)
|
||
if ! curl -f#L "$download_url" -o "$tmp_file"; then
|
||
print_error "$(msg download_failed): $download_url"
|
||
exit 1
|
||
fi
|
||
else
|
||
# Use --show-progress to display download progress
|
||
if ! wget --show-progress "$download_url" -O "$tmp_file" 2>&1 | grep -v "^$"; then
|
||
print_error "$(msg download_failed): $download_url"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
chmod +x "$tmp_file"
|
||
print_success "$(msg download_ok)"
|
||
}
|
||
|
||
install_binary() {
|
||
print_step "$(msg installing)"
|
||
|
||
mkdir -p "$INSTALL_DIR"
|
||
mv /tmp/drip "$INSTALL_DIR/drip"
|
||
chmod +x "$INSTALL_DIR/drip"
|
||
|
||
if [[ "$IS_UPDATE" == true ]]; then
|
||
print_success "$(msg update_ok): $INSTALL_DIR/drip"
|
||
else
|
||
print_success "$(msg install_ok): $INSTALL_DIR/drip"
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# Configuration
|
||
# ============================================================================
|
||
generate_token() {
|
||
# Generate a random 32-character token
|
||
if command -v openssl &> /dev/null; then
|
||
openssl rand -hex 16
|
||
else
|
||
head -c 16 /dev/urandom | xxd -p
|
||
fi
|
||
}
|
||
|
||
configure_server() {
|
||
echo ""
|
||
echo -e "${CYAN}╔════════════════════════════════════════╗${NC}"
|
||
echo -e "${CYAN}║ $(msg config_title) ${CYAN}║${NC}"
|
||
echo -e "${CYAN}╚════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
|
||
# Domain
|
||
while true; do
|
||
read -p "$(msg enter_domain): " DOMAIN < /dev/tty
|
||
if [[ -n "$DOMAIN" ]]; then
|
||
break
|
||
fi
|
||
print_error "$(msg domain_required)"
|
||
done
|
||
|
||
# Port
|
||
read -p "$(msg enter_port) [$DEFAULT_PORT]: " PORT < /dev/tty
|
||
PORT="${PORT:-$DEFAULT_PORT}"
|
||
|
||
# Public port (for URL display, e.g., behind reverse proxy)
|
||
read -p "$(msg enter_public_port) [$PORT]: " PUBLIC_PORT < /dev/tty
|
||
PUBLIC_PORT="${PUBLIC_PORT:-$PORT}"
|
||
|
||
# TCP port range
|
||
echo ""
|
||
print_info "$(msg tcp_port_range)"
|
||
read -p "$(msg enter_tcp_min) [$DEFAULT_TCP_PORT_MIN]: " TCP_PORT_MIN < /dev/tty
|
||
TCP_PORT_MIN="${TCP_PORT_MIN:-$DEFAULT_TCP_PORT_MIN}"
|
||
|
||
read -p "$(msg enter_tcp_max) [$DEFAULT_TCP_PORT_MAX]: " TCP_PORT_MAX < /dev/tty
|
||
TCP_PORT_MAX="${TCP_PORT_MAX:-$DEFAULT_TCP_PORT_MAX}"
|
||
|
||
# Token
|
||
echo ""
|
||
read -p "$(msg enter_token): " TOKEN < /dev/tty
|
||
if [[ -z "$TOKEN" ]]; then
|
||
TOKEN=$(generate_token)
|
||
print_success "$(msg token_generated): $TOKEN"
|
||
fi
|
||
|
||
# TLS certificate selection
|
||
echo ""
|
||
echo -e "${CYAN}╔════════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${CYAN}║ $(msg cert_option_title) ${CYAN}║${NC}"
|
||
echo -e "${CYAN}╠════════════════════════════════════════════════════╣${NC}"
|
||
echo -e "${CYAN}║${NC} ${GREEN}1)${NC} $(msg cert_option_certbot) ${CYAN}║${NC}"
|
||
echo -e "${CYAN}║${NC} ${GREEN}2)${NC} $(msg cert_option_self) ${CYAN}║${NC}"
|
||
echo -e "${CYAN}║${NC} ${GREEN}3)${NC} $(msg cert_option_provide) ${CYAN}║${NC}"
|
||
echo -e "${CYAN}╚════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
|
||
read -p "Select [1]: " cert_choice < /dev/tty
|
||
|
||
case "$cert_choice" in
|
||
2)
|
||
# Self-signed certificate
|
||
CERT_MODE="self_signed"
|
||
CERT_PATH="${CONFIG_DIR}/server.crt"
|
||
KEY_PATH="${CONFIG_DIR}/server.key"
|
||
echo ""
|
||
print_warning "$(msg cert_warning)"
|
||
;;
|
||
3)
|
||
# User provided certificate
|
||
CERT_MODE="provided"
|
||
echo ""
|
||
while true; do
|
||
read -p "$(msg enter_cert_path): " CERT_PATH < /dev/tty
|
||
if [[ -f "$CERT_PATH" ]]; then
|
||
break
|
||
fi
|
||
print_error "$(msg cert_not_found): $CERT_PATH"
|
||
done
|
||
|
||
while true; do
|
||
read -p "$(msg enter_key_path): " KEY_PATH < /dev/tty
|
||
if [[ -f "$KEY_PATH" ]]; then
|
||
break
|
||
fi
|
||
print_error "$(msg key_not_found): $KEY_PATH"
|
||
done
|
||
print_success "Certificate files verified"
|
||
;;
|
||
*)
|
||
# Let's Encrypt (default)
|
||
CERT_MODE="certbot"
|
||
CERT_PATH="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem"
|
||
KEY_PATH="/etc/letsencrypt/live/${DOMAIN}/privkey.pem"
|
||
echo ""
|
||
print_warning "$(msg certbot_domain_note)"
|
||
echo ""
|
||
read -p "$(msg enter_email): " CERTBOT_EMAIL < /dev/tty
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# ============================================================================
|
||
# Certificate
|
||
# ============================================================================
|
||
setup_certificate() {
|
||
case "$CERT_MODE" in
|
||
"certbot")
|
||
obtain_letsencrypt_cert
|
||
;;
|
||
"self_signed")
|
||
generate_self_signed_cert
|
||
;;
|
||
"provided")
|
||
# Certificate already verified in configure_server
|
||
print_success "Using provided certificate"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
obtain_letsencrypt_cert() {
|
||
# Let's Encrypt original cert paths
|
||
local le_cert_path="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem"
|
||
local le_key_path="/etc/letsencrypt/live/${DOMAIN}/privkey.pem"
|
||
|
||
# We'll copy certs to CONFIG_DIR for proper permissions
|
||
# Update global CERT_PATH and KEY_PATH
|
||
CERT_PATH="${CONFIG_DIR}/server.crt"
|
||
KEY_PATH="${CONFIG_DIR}/server.key"
|
||
|
||
# Check if certificate already exists (either in Let's Encrypt or our copy)
|
||
if [[ -f "$CERT_PATH" ]] && [[ -f "$KEY_PATH" ]]; then
|
||
print_warning "$(msg cert_exists)"
|
||
return
|
||
fi
|
||
|
||
# Check if Let's Encrypt cert exists but we just need to copy it
|
||
if [[ -f "$le_cert_path" ]]; then
|
||
print_info "Let's Encrypt certificate found, copying with proper permissions..."
|
||
copy_letsencrypt_cert "$le_cert_path" "$le_key_path"
|
||
return
|
||
fi
|
||
|
||
print_step "$(msg obtaining_cert)"
|
||
|
||
# Install certbot if not present
|
||
if ! command -v certbot &> /dev/null; then
|
||
print_info "Installing certbot..."
|
||
if command -v apt-get &> /dev/null; then
|
||
apt-get update -qq
|
||
apt-get install -y -qq certbot
|
||
elif command -v yum &> /dev/null; then
|
||
yum install -y -q certbot
|
||
elif command -v dnf &> /dev/null; then
|
||
dnf install -y -q certbot
|
||
elif command -v pacman &> /dev/null; then
|
||
pacman -Sy --noconfirm certbot
|
||
fi
|
||
fi
|
||
|
||
# Stop services that might use port 80
|
||
local nginx_was_running=false
|
||
local apache_was_running=false
|
||
|
||
if systemctl is-active --quiet nginx 2>/dev/null; then
|
||
systemctl stop nginx
|
||
nginx_was_running=true
|
||
fi
|
||
|
||
if systemctl is-active --quiet apache2 2>/dev/null; then
|
||
systemctl stop apache2
|
||
apache_was_running=true
|
||
fi
|
||
|
||
if systemctl is-active --quiet httpd 2>/dev/null; then
|
||
systemctl stop httpd
|
||
apache_was_running=true
|
||
fi
|
||
|
||
# Build certbot command
|
||
local certbot_args="certonly --standalone -d ${DOMAIN}"
|
||
if [[ -n "$CERTBOT_EMAIL" ]]; then
|
||
certbot_args="$certbot_args --email ${CERTBOT_EMAIL}"
|
||
else
|
||
certbot_args="$certbot_args --register-unsafely-without-email"
|
||
fi
|
||
certbot_args="$certbot_args --agree-tos --non-interactive"
|
||
|
||
# Obtain certificate
|
||
if certbot $certbot_args; then
|
||
print_success "$(msg cert_obtained)"
|
||
else
|
||
print_error "$(msg cert_failed)"
|
||
# Restart services before exit
|
||
[[ "$nginx_was_running" == true ]] && systemctl start nginx
|
||
[[ "$apache_was_running" == true ]] && (systemctl start apache2 2>/dev/null || systemctl start httpd 2>/dev/null)
|
||
exit 1
|
||
fi
|
||
|
||
# Restart services
|
||
[[ "$nginx_was_running" == true ]] && systemctl start nginx
|
||
[[ "$apache_was_running" == true ]] && (systemctl start apache2 2>/dev/null || systemctl start httpd 2>/dev/null)
|
||
|
||
# Copy certificates with proper permissions
|
||
copy_letsencrypt_cert "$le_cert_path" "$le_key_path"
|
||
|
||
# Create renewal hook for automatic certificate updates
|
||
create_certbot_renewal_hook
|
||
}
|
||
|
||
copy_letsencrypt_cert() {
|
||
local le_cert="$1"
|
||
local le_key="$2"
|
||
|
||
print_step "$(msg copying_cert)"
|
||
|
||
# Ensure config directory exists
|
||
mkdir -p "$CONFIG_DIR"
|
||
|
||
# Copy certificates
|
||
cp -L "$le_cert" "$CERT_PATH"
|
||
cp -L "$le_key" "$KEY_PATH"
|
||
|
||
# Set proper permissions (readable by drip user)
|
||
chmod 644 "$CERT_PATH"
|
||
chmod 640 "$KEY_PATH"
|
||
chown root:"$SERVICE_USER" "$KEY_PATH"
|
||
|
||
print_success "$(msg cert_copied)"
|
||
print_info "Certificate: $CERT_PATH"
|
||
print_info "Private Key: $KEY_PATH"
|
||
}
|
||
|
||
create_certbot_renewal_hook() {
|
||
print_step "$(msg creating_renewal_hook)"
|
||
|
||
local hook_dir="/etc/letsencrypt/renewal-hooks/deploy"
|
||
mkdir -p "$hook_dir"
|
||
|
||
cat > "${hook_dir}/drip-server.sh" << 'HOOK_EOF'
|
||
#!/bin/bash
|
||
# Drip Server certificate renewal hook
|
||
# This script copies renewed certificates and restarts the service
|
||
|
||
DOMAIN_DIR="/etc/letsencrypt/live"
|
||
CONFIG_DIR="/etc/drip"
|
||
SERVICE_USER="drip"
|
||
|
||
# Find the domain from the renewed certificate
|
||
for domain_path in "$DOMAIN_DIR"/*; do
|
||
if [[ -d "$domain_path" ]]; then
|
||
domain=$(basename "$domain_path")
|
||
le_cert="${domain_path}/fullchain.pem"
|
||
le_key="${domain_path}/privkey.pem"
|
||
|
||
if [[ -f "$le_cert" ]] && [[ -f "$le_key" ]]; then
|
||
# Copy certificates
|
||
cp -L "$le_cert" "${CONFIG_DIR}/server.crt"
|
||
cp -L "$le_key" "${CONFIG_DIR}/server.key"
|
||
|
||
# Set proper permissions
|
||
chmod 644 "${CONFIG_DIR}/server.crt"
|
||
chmod 640 "${CONFIG_DIR}/server.key"
|
||
chown root:"$SERVICE_USER" "${CONFIG_DIR}/server.key"
|
||
|
||
# Restart service
|
||
systemctl restart drip-server 2>/dev/null || true
|
||
break
|
||
fi
|
||
fi
|
||
done
|
||
HOOK_EOF
|
||
|
||
chmod +x "${hook_dir}/drip-server.sh"
|
||
print_success "$(msg renewal_hook_created)"
|
||
}
|
||
|
||
generate_self_signed_cert() {
|
||
# Check if certificate already exists
|
||
if [[ -f "$CERT_PATH" ]] && [[ -f "$KEY_PATH" ]]; then
|
||
print_warning "$(msg cert_exists)"
|
||
return
|
||
fi
|
||
|
||
print_step "$(msg generating_cert)"
|
||
|
||
# Ensure config directory exists
|
||
mkdir -p "$CONFIG_DIR"
|
||
|
||
# Generate 10-year self-signed ECDSA certificate
|
||
# Country: US (United States)
|
||
openssl ecparam -genkey -name prime256v1 -out "$KEY_PATH" 2>/dev/null
|
||
|
||
openssl req -new -x509 \
|
||
-key "$KEY_PATH" \
|
||
-out "$CERT_PATH" \
|
||
-days 3650 \
|
||
-subj "/C=US/ST=California/L=San Francisco/O=Drip Tunnel/OU=Server/CN=${DOMAIN}" \
|
||
-addext "subjectAltName=DNS:${DOMAIN},DNS:*.${DOMAIN}" \
|
||
2>/dev/null
|
||
|
||
# Set proper permissions
|
||
chmod 600 "$KEY_PATH"
|
||
chmod 644 "$CERT_PATH"
|
||
|
||
print_success "$(msg cert_generated)"
|
||
print_info "Certificate: $CERT_PATH"
|
||
print_info "Private Key: $KEY_PATH"
|
||
}
|
||
|
||
# ============================================================================
|
||
# System setup
|
||
# ============================================================================
|
||
create_service_user() {
|
||
print_step "$(msg creating_user)"
|
||
|
||
if id "$SERVICE_USER" &>/dev/null; then
|
||
print_warning "$(msg user_exists): $SERVICE_USER"
|
||
else
|
||
useradd -r -s /bin/false "$SERVICE_USER"
|
||
print_success "$(msg user_created): $SERVICE_USER"
|
||
fi
|
||
}
|
||
|
||
create_directories() {
|
||
mkdir -p "$CONFIG_DIR"
|
||
mkdir -p "$WORK_DIR"
|
||
chown "$SERVICE_USER:$SERVICE_USER" "$WORK_DIR"
|
||
}
|
||
|
||
create_systemd_service() {
|
||
print_step "$(msg creating_service)"
|
||
|
||
cat > /etc/systemd/system/drip-server.service << EOF
|
||
[Unit]
|
||
Description=Drip Tunnel Server
|
||
After=network.target
|
||
Documentation=https://github.com/Gouryella/drip
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=${SERVICE_USER}
|
||
Group=${SERVICE_USER}
|
||
Restart=on-failure
|
||
RestartSec=5s
|
||
|
||
# Resource limits
|
||
LimitNOFILE=65536
|
||
LimitNPROC=4096
|
||
|
||
# Working directory
|
||
WorkingDirectory=${WORK_DIR}
|
||
|
||
# Load environment variables from file
|
||
EnvironmentFile=${CONFIG_DIR}/server.env
|
||
|
||
# Start command (uses environment variables)
|
||
ExecStart=${INSTALL_DIR}/drip server
|
||
|
||
# Logging
|
||
StandardOutput=journal
|
||
StandardError=journal
|
||
SyslogIdentifier=drip-server
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
systemctl daemon-reload
|
||
print_success "$(msg service_created)"
|
||
}
|
||
|
||
configure_firewall() {
|
||
print_step "$(msg configuring_firewall)"
|
||
|
||
local ports_to_open=("$PORT" "$TCP_PORT_MIN:$TCP_PORT_MAX")
|
||
|
||
# UFW
|
||
if command -v ufw &> /dev/null; then
|
||
ufw allow "$PORT/tcp" >/dev/null 2>&1 || true
|
||
ufw allow "${TCP_PORT_MIN}:${TCP_PORT_MAX}/tcp" >/dev/null 2>&1 || true
|
||
fi
|
||
|
||
# firewalld
|
||
if command -v firewall-cmd &> /dev/null; then
|
||
firewall-cmd --permanent --add-port="$PORT/tcp" >/dev/null 2>&1 || true
|
||
firewall-cmd --permanent --add-port="${TCP_PORT_MIN}-${TCP_PORT_MAX}/tcp" >/dev/null 2>&1 || true
|
||
firewall-cmd --reload >/dev/null 2>&1 || true
|
||
fi
|
||
|
||
print_success "$(msg firewall_ok)"
|
||
}
|
||
|
||
save_config() {
|
||
print_step "$(msg saving_config)"
|
||
|
||
cat > "$CONFIG_DIR/server.env" << EOF
|
||
# Drip Server Configuration
|
||
# Generated: $(date)
|
||
# DO NOT SHARE THIS FILE - Contains sensitive information
|
||
|
||
# Server settings
|
||
DRIP_PORT=${PORT}
|
||
DRIP_PUBLIC_PORT=${PUBLIC_PORT}
|
||
DRIP_DOMAIN=${DOMAIN}
|
||
DRIP_TOKEN=${TOKEN}
|
||
|
||
# TLS certificate paths
|
||
DRIP_TLS_CERT=${CERT_PATH}
|
||
DRIP_TLS_KEY=${KEY_PATH}
|
||
|
||
# TCP tunnel port range
|
||
DRIP_TCP_PORT_MIN=${TCP_PORT_MIN}
|
||
DRIP_TCP_PORT_MAX=${TCP_PORT_MAX}
|
||
EOF
|
||
|
||
chmod 640 "$CONFIG_DIR/server.env"
|
||
chown root:"$SERVICE_USER" "$CONFIG_DIR/server.env"
|
||
print_success "$(msg config_saved): $CONFIG_DIR/server.env"
|
||
}
|
||
|
||
# ============================================================================
|
||
# Service management
|
||
# ============================================================================
|
||
start_service() {
|
||
read -p "$(msg start_now) [Y/n]: " start_choice < /dev/tty
|
||
|
||
if [[ "$start_choice" =~ ^[Nn]$ ]]; then
|
||
return
|
||
fi
|
||
|
||
print_step "$(msg starting_service)"
|
||
|
||
systemctl enable drip-server >/dev/null 2>&1
|
||
systemctl start drip-server
|
||
|
||
sleep 2
|
||
|
||
if systemctl is-active --quiet drip-server; then
|
||
print_success "$(msg service_started)"
|
||
else
|
||
print_error "$(msg service_failed)"
|
||
echo ""
|
||
journalctl -u drip-server -n 20 --no-pager
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# Final output
|
||
# ============================================================================
|
||
show_completion() {
|
||
echo ""
|
||
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${GREEN}║ $(msg install_complete) ${GREEN}║${NC}"
|
||
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
|
||
echo -e "${CYAN}$(msg client_info):${NC}"
|
||
echo -e " ${BOLD}$(msg server_addr):${NC} ${DOMAIN}:${PORT}"
|
||
echo -e " ${BOLD}$(msg token_label):${NC} ${TOKEN}"
|
||
echo ""
|
||
|
||
echo -e "${CYAN}$(msg service_commands):${NC}"
|
||
echo -e " ${GREEN}$(msg cmd_start):${NC} systemctl start drip-server"
|
||
echo -e " ${GREEN}$(msg cmd_stop):${NC} systemctl stop drip-server"
|
||
echo -e " ${GREEN}$(msg cmd_restart):${NC} systemctl restart drip-server"
|
||
echo -e " ${GREEN}$(msg cmd_status):${NC} systemctl status drip-server"
|
||
echo -e " ${GREEN}$(msg cmd_logs):${NC} journalctl -u drip-server -f"
|
||
echo -e " ${GREEN}$(msg cmd_enable):${NC} systemctl enable drip-server"
|
||
echo ""
|
||
}
|
||
|
||
# ============================================================================
|
||
# Main
|
||
# ============================================================================
|
||
main() {
|
||
clear
|
||
print_banner
|
||
select_language
|
||
|
||
echo -e "${BOLD}────────────────────────────────────────────${NC}"
|
||
|
||
check_root
|
||
check_os
|
||
check_arch
|
||
check_dependencies
|
||
check_existing_install
|
||
|
||
echo ""
|
||
download_binary
|
||
install_binary
|
||
|
||
# If updating, check if systemd service exists
|
||
if [[ "$IS_UPDATE" == true ]]; then
|
||
# Check if systemd service file exists
|
||
if [[ -f /etc/systemd/system/drip-server.service ]]; then
|
||
echo ""
|
||
print_step "Restarting drip-server service..."
|
||
systemctl start drip-server
|
||
|
||
if systemctl is-active --quiet drip-server; then
|
||
print_success "$(msg service_started)"
|
||
else
|
||
print_error "$(msg service_failed)"
|
||
journalctl -u drip-server -n 20 --no-pager
|
||
exit 1
|
||
fi
|
||
|
||
echo ""
|
||
local new_version=$("$INSTALL_DIR/drip" version 2>/dev/null | awk '/Version:/ {print $2}' || echo "unknown")
|
||
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${GREEN}║ $(msg update_ok) ${GREEN}║${NC}"
|
||
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
print_info "$(msg current_version): $new_version"
|
||
echo ""
|
||
exit 0
|
||
else
|
||
# Service file doesn't exist, need to configure
|
||
print_warning "$(msg service_not_found)"
|
||
IS_UPDATE=false
|
||
fi
|
||
fi
|
||
|
||
echo ""
|
||
configure_server
|
||
|
||
echo ""
|
||
setup_certificate
|
||
create_service_user
|
||
create_directories
|
||
create_systemd_service
|
||
configure_firewall
|
||
save_config
|
||
|
||
echo ""
|
||
start_service
|
||
|
||
show_completion
|
||
}
|
||
|
||
# Run
|
||
main "$@"
|