#!/bin/bash # # frpc 安装与配置脚本 (Rocky Linux) # 功能:下载 frpc,交互式配置,并注册为 systemd 自启动服务 # 路径:/opt/frpc/ # 支持:GitHub 官方源 + 多个国内加速镜像 # set -e # ==================== 颜色定义 ==================== RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # ==================== 常量 ==================== FRP_DIR="/opt/frpc" FRP_BIN="${FRP_DIR}/frpc" FRP_CONF="${FRP_DIR}/frpc.toml" SERVICE_FILE="/etc/systemd/system/frpc.service" # ==================== GitHub 加速源 ==================== # 格式: "名称|下载URL模板" (模板中 {version} 和 {arch} 会被替换) declare -A MIRROR_MAP MIRRORS=( "ghproxy 加速|https://ghproxy.net/https://github.com/fatedier/frp/releases/download/{version}/frp_{ver_num}_linux_{arch}.tar.gz" "gh-proxy.com|https://gh-proxy.com/https://github.com/fatedier/frp/releases/download/{version}/frp_{ver_num}_linux_{arch}.tar.gz" "github.91chi.fun|https://github.91chi.fun/https://github.com/fatedier/frp/releases/download/{version}/frp_{ver_num}_linux_{arch}.tar.gz" "GitHub 加速 (moonjump)|https://github.moeyy.xyz/https://github.com/fatedier/frp/releases/download/{version}/frp_{ver_num}_linux_{arch}.tar.gz" "wget 代理|https://wget.la/https://github.com/fatedier/frp/releases/download/{version}/frp_{ver_num}_linux_{arch}.tar.gz" "GitHub 官方源|https://github.com/fatedier/frp/releases/download/{version}/frp_{ver_num}_linux_{arch}.tar.gz" ) # 最新版本号缓存 LATEST_VERSION="" # ==================== 检查 root 权限 ==================== if [[ $EUID -ne 0 ]]; then echo -e "${RED}[错误] 请使用 root 权限运行此脚本: sudo bash $0${NC}" exit 1 fi echo -e "${BLUE}================================================${NC}" echo -e "${BLUE} frpc 安装与配置脚本 (Rocky Linux)${NC}" echo -e "${BLUE} 支持 GitHub 多源加速下载${NC}" echo -e "${BLUE}================================================${NC}" echo "" # ==================== 检测系统 ==================== if [[ ! -f /etc/rocky-release ]]; then echo -e "${YELLOW}[警告] 此脚本专为 Rocky Linux 设计,当前系统可能不完全兼容。${NC}" fi # 获取系统架构 ARCH=$(uname -m) case "$ARCH" in x86_64) FRP_ARCH="amd64" ;; aarch64) FRP_ARCH="arm64" ;; *) echo -e "${RED}[错误] 不支持的架构: $ARCH${NC}" exit 1 ;; esac echo -e "${GREEN}[信息] 系统架构: ${ARCH} -> frp 架构: ${FRP_ARCH}${NC}" # ==================== 获取最新版本号 ==================== get_latest_version() { if [[ -n "$LATEST_VERSION" ]]; then echo "$LATEST_VERSION" return fi echo -e "${YELLOW}[信息] 正在查询 frp 最新版本...${NC}" # 尝试多个 API 源获取版本号 local api_urls=( "https://api.github.com/repos/fatedier/frp/releases/latest" "https://ghproxy.net/https://api.github.com/repos/fatedier/frp/releases/latest" "https://gh-proxy.com/https://api.github.com/repos/fatedier/frp/releases/latest" ) for api_url in "${api_urls[@]}"; do local response response=$(curl -s --connect-timeout 5 --max-time 10 "$api_url" 2>/dev/null) if [[ -n "$response" ]]; then local ver ver=$(echo "$response" | grep '"tag_name":' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/') if [[ -n "$ver" ]]; then LATEST_VERSION="$ver" echo -e "${GREEN} ✓ 最新版本: ${LATEST_VERSION}${NC}" echo "$LATEST_VERSION" return fi fi done # 如果 API 全部失败,使用一个已知的稳定版本作为fallback echo -e "${YELLOW}[警告] 无法通过 API 获取最新版本,使用 fallback 版本${NC}" LATEST_VERSION="v0.61.1" # 请根据实际情况更新此版本号 echo -e "${YELLOW} Fallback 版本: ${LATEST_VERSION}${NC}" echo "$LATEST_VERSION" } # ==================== 安装依赖 ==================== echo -e "${YELLOW}[步骤 1/6] 检查并安装依赖...${NC}" MISSING_PKGS="" for cmd in tar wget curl; do if ! command -v "$cmd" &> /dev/null; then MISSING_PKGS="$MISSING_PKGS $cmd" fi done if [[ -n "$MISSING_PKGS" ]]; then echo -e " 安装缺失的包:${MISSING_PKGS}" dnf install -y tar wget curl else echo -e "${GREEN} ✓ 所需工具已安装${NC}" fi # ==================== 创建目录 ==================== echo -e "${YELLOW}[步骤 2/6] 创建安装目录 ${FRP_DIR}...${NC}" mkdir -p "$FRP_DIR" echo -e "${GREEN} ✓ 目录已创建${NC}" # ==================== 下载 frpc ==================== echo -e "${YELLOW}[步骤 3/6] 下载 frpc(支持多源加速)...${NC}" # 获取版本号 LATEST_VER=$(get_latest_version) VER_NUM="${LATEST_VER#v}" TMP_DIR=$(mktemp -d) cd "$TMP_DIR" DOWNLOAD_SUCCESS=false # 遍历所有镜像源尝试下载 for i in "${!MIRRORS[@]}"; do mirror="${MIRRORS[$i]}" mirror_name="${mirror%%|*}" mirror_url="${mirror##*|}" # 替换模板变量 download_url="${mirror_url//\{version\}/$LATEST_VER}" download_url="${download_url//\{ver_num\}/$VER_NUM}" download_url="${download_url//\{arch\}/$FRP_ARCH}" echo "" echo -e " ${CYAN}[尝试 $((i+1))/${#MIRRORS[@]}] ${mirror_name}${NC}" echo -e " URL: ${download_url}" # 尝试下载,超时60秒,重试2次 if wget -q --show-progress --timeout=60 --tries=2 \ -O frp.tar.gz "$download_url" 2>&1; then # 验证下载的文件 if [[ -f frp.tar.gz ]] && [[ $(stat -c%s frp.tar.gz 2>/dev/null || stat -f%z frp.tar.gz 2>/dev/null || echo 0) -gt 10000 ]]; then echo -e " ${GREEN} ✓ ${mirror_name} 下载成功!${NC}" DOWNLOAD_SUCCESS=true break else echo -e " ${RED} ✗ 文件不完整,尝试下一个源...${NC}" rm -f frp.tar.gz fi else echo -e " ${RED} ✗ ${mirror_name} 下载失败,尝试下一个源...${NC}" rm -f frp.tar.gz fi done # 如果所有在线源都失败,尝试手动输入 if [[ "$DOWNLOAD_SUCCESS" != true ]]; then echo "" echo -e "${RED}================================================${NC}" echo -e "${RED} 所有自动下载源均失败!${NC}" echo -e "${RED}================================================${NC}" echo "" echo -e "${YELLOW}请尝试以下方法之一:${NC}" echo "" echo -e " 1. 手动下载 frp_${VER_NUM}_linux_${FRP_ARCH}.tar.gz" echo -e " 官方地址: https://github.com/fatedier/frp/releases" echo -e " 将文件放到: ${TMP_DIR}/frp.tar.gz" echo "" echo -e " 2. 直接提供 frpc 二进制文件" echo -e " 将 frpc 放到: ${FRP_BIN}" echo "" echo -e " 3. 输入自定义下载地址" echo "" while true; do read -r -p "请输入 frp 压缩包的下载地址(或输入 skip 跳过下载): " CUSTOM_URL if [[ "$CUSTOM_URL" == "skip" ]]; then break fi if [[ -n "$CUSTOM_URL" ]]; then echo -e " 正在尝试自定义地址下载..." if wget -q --show-progress --timeout=120 --tries=2 \ -O frp.tar.gz "$CUSTOM_URL" 2>&1; then if [[ -f frp.tar.gz ]] && [[ $(stat -c%s frp.tar.gz 2>/dev/null || echo 0) -gt 10000 ]]; then echo -e " ${GREEN} ✓ 自定义地址下载成功!${NC}" DOWNLOAD_SUCCESS=true break fi fi echo -e " ${RED} ✗ 自定义地址下载失败${NC}" fi done # 如果仍然失败,检查是否已有 frpc 二进制 if [[ "$DOWNLOAD_SUCCESS" != true ]]; then if [[ -f "$FRP_BIN" ]] && [[ -x "$FRP_BIN" ]]; then echo -e "${YELLOW}[信息] 使用已存在的 frpc 二进制文件: ${FRP_BIN}${NC}" DOWNLOAD_SUCCESS="skip" else echo -e "${RED}[错误] 下载失败且没有可用的 frpc 二进制文件${NC}" echo -e "${YELLOW}[提示] 请手动将 frpc 放到 ${FRP_BIN} 后重新运行脚本${NC}" rm -rf "$TMP_DIR" exit 1 fi fi fi # 解压并安装 if [[ "$DOWNLOAD_SUCCESS" == true ]]; then echo "" echo -e "${YELLOW} 正在解压...${NC}" tar -xzf frp.tar.gz # 查找解压后的目录 FRP_EXTRACT_DIR=$(find . -maxdepth 1 -type d -name "frp_*" | head -1) if [[ -z "$FRP_EXTRACT_DIR" ]]; then echo -e "${RED}[错误] 未找到解压目录${NC}" ls -la rm -rf "$TMP_DIR" exit 1 fi echo -e " 解压目录: ${FRP_EXTRACT_DIR}" # 复制 frpc 到目标目录 cp "${FRP_EXTRACT_DIR}/frpc" "$FRP_BIN" chmod +x "$FRP_BIN" echo -e "${GREEN} ✓ frpc 已安装到 ${FRP_BIN}${NC}" fi # 清理临时文件 rm -rf "$TMP_DIR" # 验证安装 if [[ -f "$FRP_BIN" ]] && [[ -x "$FRP_BIN" ]]; then echo -e " 版本信息: $($FRP_BIN --version 2>/dev/null || echo '未知')" else echo -e "${RED}[错误] frpc 安装验证失败${NC}" exit 1 fi # ==================== 交互式配置 ==================== echo "" echo -e "${BLUE}================================================${NC}" echo -e "${BLUE} 请输入 frpc 配置信息${NC}" echo -e "${BLUE}================================================${NC}" echo "" echo -e "${CYAN}提示:直接回车使用方括号内的默认值${NC}" echo "" # 输入远程主机 IP while true; do read -r -p "远程主机 IP 地址: " REMOTE_HOST if [[ -n "$REMOTE_HOST" ]]; then # 简单验证 IP 或域名格式 if [[ "$REMOTE_HOST" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || \ [[ "$REMOTE_HOST" =~ ^[a-zA-Z0-9][-a-zA-Z0-9\.]*[a-zA-Z0-9]$ ]]; then break fi echo -e "${RED} [错误] IP 地址或域名格式不正确${NC}" else echo -e "${RED} [错误] IP 地址不能为空${NC}" fi done # 输入远程主机端口 while true; do read -r -p "远程主机端口 [7000]: " REMOTE_PORT REMOTE_PORT=${REMOTE_PORT:-7000} if [[ "$REMOTE_PORT" =~ ^[0-9]+$ ]] && ((REMOTE_PORT > 0 && REMOTE_PORT < 65536)); then break fi echo -e "${RED} [错误] 请输入有效的端口号 (1-65535)${NC}" done # 输入远程主机 Token while true; do read -r -p "远程主机 Token (认证密钥): " REMOTE_TOKEN if [[ -n "$REMOTE_TOKEN" ]]; then break fi echo -e "${RED} [错误] Token 不能为空${NC}" done # 输入映射类型 echo "" echo -e "${CYAN}请选择代理类型:${NC}" echo " 1) TCP (最常用,适用于 Web、SSH、数据库等)" echo " 2) UDP (适用于 DNS、游戏、VoIP 等)" echo " 3) TCP + UDP (同时映射两种协议)" while true; do read -r -p "请选择 [1-3,默认 1]: " PROXY_TYPE_CHOICE PROXY_TYPE_CHOICE=${PROXY_TYPE_CHOICE:-1} case "$PROXY_TYPE_CHOICE" in 1) PROXY_TYPE="tcp"; UDP_ALSO=false; break ;; 2) PROXY_TYPE="udp"; UDP_ALSO=false; break ;; 3) PROXY_TYPE="tcp"; UDP_ALSO=true; break ;; *) echo -e "${RED} [错误] 请输入 1、2 或 3${NC}" ;; esac done # 输入本地IP read -r -p "本地服务 IP [默认: 127.0.0.1]: " LOCAL_IP LOCAL_IP=${LOCAL_IP:-127.0.0.1} # 输入本地端口 while true; do read -r -p "要映射的本地端口: " LOCAL_PORT if [[ "$LOCAL_PORT" =~ ^[0-9]+$ ]] && ((LOCAL_PORT > 0 && LOCAL_PORT < 65536)); then break fi echo -e "${RED} [错误] 请输入有效的端口号 (1-65535)${NC}" done # 输入远程端口 (可选,默认与本地端口相同) read -r -p "远程端口 [默认: ${LOCAL_PORT}]: " REMOTE_BIND_PORT REMOTE_BIND_PORT=${REMOTE_BIND_PORT:-$LOCAL_PORT} # 输入代理名称 (可选) DEFAULT_NAME="proxy_${LOCAL_PORT}" read -r -p "代理名称 [默认: ${DEFAULT_NAME}]: " PROXY_NAME PROXY_NAME=${PROXY_NAME:-$DEFAULT_NAME} # 额外配置选项 echo "" echo -e "${CYAN}高级选项(通常无需修改,直接回车跳过):${NC}" read -r -p "启用压缩传输? (true/false) [默认: false]: " USE_COMPRESSION USE_COMPRESSION=${USE_COMPRESSION:-false} read -r -p "启用加密传输? (true/false) [默认: false]: " USE_ENCRYPTION USE_ENCRYPTION=${USE_ENCRYPTION:-false} # ==================== 生成配置文件 ==================== echo "" echo -e "${YELLOW}[步骤 4/6] 生成配置文件...${NC}" # 备份旧配置 if [[ -f "$FRP_CONF" ]]; then BACKUP_FILE="${FRP_CONF}.backup.$(date +%Y%m%d_%H%M%S)" cp "$FRP_CONF" "$BACKUP_FILE" echo -e "${YELLOW} ✓ 旧配置已备份: ${BACKUP_FILE}${NC}" fi cat > "$FRP_CONF" << EOF # ======================================== # frpc 配置文件 # 生成时间: $(date '+%Y-%m-%d %H:%M:%S') # 版本: ${LATEST_VER} # ======================================== # 服务端连接配置 serverAddr = "${REMOTE_HOST}" serverPort = ${REMOTE_PORT} auth.token = "${REMOTE_TOKEN}" # 日志配置 log.to = "${FRP_DIR}/frpc.log" log.level = "info" log.maxDays = 3 # 代理配置 [[proxies]] name = "${PROXY_NAME}" type = "${PROXY_TYPE}" localIP = "${LOCAL_IP}" localPort = ${LOCAL_PORT} remotePort = ${REMOTE_BIND_PORT} transport.useCompression = ${USE_COMPRESSION} transport.useEncryption = ${USE_ENCRYPTION} EOF # 如果选择了 TCP+UDP,额外添加 UDP 代理 if [[ "$UDP_ALSO" == true ]]; then cat >> "$FRP_CONF" << EOF [[proxies]] name = "${PROXY_NAME}_udp" type = "udp" localIP = "${LOCAL_IP}" localPort = ${LOCAL_PORT} remotePort = ${REMOTE_BIND_PORT} transport.useCompression = ${USE_COMPRESSION} transport.useEncryption = ${USE_ENCRYPTION} EOF fi echo -e "${GREEN} ✓ 配置文件已生成: ${FRP_CONF}${NC}" echo "" echo -e "${BLUE}--- 配置预览 ---${NC}" cat "$FRP_CONF" | grep -v "^$" | grep -v "^#" echo -e "${BLUE}-----------------${NC}" # ==================== 创建 systemd 服务 ==================== echo "" echo -e "${YELLOW}[步骤 5/6] 创建 systemd 自启动服务...${NC}" cat > "$SERVICE_FILE" << 'SERVICE_EOF' [Unit] Description=frpc - Fast Reverse Proxy Client Documentation=https://gofrp.org After=network.target network-online.target Wants=network-online.target [Service] Type=simple User=root WorkingDirectory=/opt/frpc ExecStart=/opt/frpc/frpc -c /opt/frpc/frpc.toml ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure RestartSec=10s LimitNOFILE=1048576 # 安全加固 NoNewPrivileges=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/opt/frpc PrivateTmp=true [Install] WantedBy=multi-user.target SERVICE_EOF echo -e "${GREEN} ✓ 服务文件已创建: ${SERVICE_FILE}${NC}" # ==================== 启用并启动服务 ==================== echo "" echo -e "${YELLOW}[步骤 6/6] 启动服务并设置自启动...${NC}" # 重新加载 systemd systemctl daemon-reload # 启用自启动 systemctl enable frpc.service echo -e "${GREEN} ✓ 已设置开机自启动${NC}" # 如果服务已在运行,先停止 if systemctl is-active --quiet frpc.service; then echo -e " 停止旧服务..." systemctl stop frpc.service fi # 启动服务 echo -e " 启动 frpc 服务..." systemctl start frpc.service # 等待片刻后检查状态 sleep 2 # 检查服务是否正常启动 if systemctl is-active --quiet frpc.service; then SERVICE_STATUS="${GREEN}运行中 ✓${NC}" else SERVICE_STATUS="${RED}未运行 ✗${NC}" fi echo "" echo -e "${BLUE}================================================${NC}" echo -e "${BLUE} 安装完成 - 服务状态${NC}" echo -e "${BLUE}================================================${NC}" echo "" echo -e " frpc 服务: ${SERVICE_STATUS}" echo "" systemctl status frpc.service --no-pager -l 2>&1 | head -20 || true # ==================== 最终汇总 ==================== echo "" echo -e "${BLUE}================================================${NC}" echo -e "${GREEN} ✓ frpc 安装配置完成!${NC}" echo -e "${BLUE}================================================${NC}" echo "" echo -e "${CYAN}📁 安装路径: ${NC}${FRP_DIR}" echo -e "${CYAN}🔧 配置文件: ${NC}${FRP_CONF}" echo -e "${CYAN}📋 日志文件: ${NC}${FRP_DIR}/frpc.log" echo -e "${CYAN}⚙️ 服务文件: ${NC}${SERVICE_FILE}" echo -e "${CYAN}🔄 代理映射: ${NC}${LOCAL_IP}:${LOCAL_PORT} -> ${REMOTE_HOST}:${REMOTE_BIND_PORT}" echo "" echo -e "${YELLOW}━━━━━━━━━━━ 常用管理命令 ━━━━━━━━━━━${NC}" echo "" echo -e " ${GREEN}查看状态${NC} systemctl status frpc" echo -e " ${GREEN}重启服务${NC} systemctl restart frpc" echo -e " ${GREEN}停止服务${NC} systemctl stop frpc" echo -e " ${GREEN}启动服务${NC} systemctl start frpc" echo -e " ${GREEN}禁用自启动${NC} systemctl disable frpc" echo "" echo -e " ${GREEN}实时日志${NC} journalctl -u frpc -f" echo -e " ${GREEN}今日日志${NC} journalctl -u frpc --since today" echo -e " ${GREEN}文件日志${NC} tail -f ${FRP_DIR}/frpc.log" echo "" echo -e " ${GREEN}编辑配置${NC} vi ${FRP_CONF}" echo -e " ${GREEN}重载配置${NC} systemctl reload frpc" echo "" # 检查日志中是否有连接错误 if [[ -f "${FRP_DIR}/frpc.log" ]]; then if grep -qi "error\|failed\|refused" "${FRP_DIR}/frpc.log" 2>/dev/null; then echo -e "${RED}⚠ 检测到日志中有错误信息,请检查:${NC}" grep -i "error\|failed\|refused" "${FRP_DIR}/frpc.log" 2>/dev/null | tail -5 echo "" echo -e "${YELLOW}常见问题排查:${NC}" echo -e " 1. 确认远程 frps 服务已启动" echo -e " 2. 确认远程防火墙放行了 ${REMOTE_PORT} 和 ${REMOTE_BIND_PORT} 端口" echo -e " 3. 确认 Token 与 frps 配置一致" echo "" fi fi echo -e "${BLUE}================================================${NC}"