clean commit

This commit is contained in:
sushen339
2025-10-17 02:14:17 +08:00
commit ca016e8fca
6 changed files with 2342 additions and 0 deletions
+89
View File
@@ -0,0 +1,89 @@
# 🚀 脚本工具集
常用 Linux/路由器/服务器 Bash 工具脚本,适合自动化、性能优化、代理配置等场景。
---
## 🛠️ 工具与一键命令
### 1. tcp.sh —— TCP 网络优化
优化:
```bash
bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/tcp.sh") 1
```
代理加速:
```bash
bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/tcp.sh") 1
```
---
### 2. tproxy.sh —— OpenWrt/路由透明代理自动配置
一键运行:
```bash
bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/tproxy.sh")
```
代理加速:
```bash
bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/tproxy.sh")
```
---
### 3. mihomo-install.sh —— Mihomo 加速核心一键安装
一键安装:
```bash
bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/mihomo-install.sh")
```
代理加速:
```bash
bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/mihomo-install.sh")
```
---
### 4. mssh.sh —— 多主机 SSH 管理与端口转发
一键运行:
```bash
bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/mssh.sh")
```
代理加速:
```bash
bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/mssh.sh")
```
---
### 5. curl-cc.sh —— 模拟浏览器 UA 自动访问签到
一键运行:
```bash
bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/curl-cc.sh")
```
代理加速:
```bash
bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/curl-cc.sh")
```
---
> 建议所有脚本以 root 权限运行,详细参数和说明请阅读各脚本头部注释。
> 建议所有脚本以 root 权限运行,详细参数和说明请阅读各脚本头部注释。
+345
View File
@@ -0,0 +1,345 @@
#!/usr/bin/env bash
# curl-cc.sh
# 自动访问指定网站,模拟真实浏览器行为
# 用法: ./curl-cc.sh [-v|--verbose] [-h|--help]
set -o pipefail
# 配置选项
VERBOSE=0
SCRIPT_NAME=$(basename "$0")
# 显示帮助信息
show_help() {
cat << EOF
用法: $SCRIPT_NAME [选项]
描述:
自动访问指定网站,模拟真实浏览器行为进行签到或访问
选项:
-v, --verbose 显示详细输出信息
-h, --help 显示此帮助信息
示例:
$SCRIPT_NAME # 静默模式运行
$SCRIPT_NAME -v # 详细模式运行
EOF
exit 0
}
# 日志函数
log_info() {
if [ "$VERBOSE" -eq 1 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*"
fi
}
log_error() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" >&2
}
# 解析命令行参数
while [ $# -gt 0 ]; do
case "$1" in
-v|--verbose)
VERBOSE=1
shift
;;
-h|--help)
show_help
;;
*)
echo "未知选项: $1"
echo "使用 -h 或 --help 查看帮助"
exit 1
;;
esac
done
VISIT_PATH="/short_code/visit_short_code_proc_ajax.php?ref="
CLICK_PATH="/short_code/click_short_code_proc_ajax.php?c=c"
# Referer 来源列表(最常用的社交媒体和平台)
REFERERS=(
# 主流社交媒体
"https://www.facebook.com/"
"https://twitter.com/"
"https://www.instagram.com/"
"https://www.tiktok.com/"
"https://www.reddit.com/"
# 视频平台
"https://www.youtube.com/"
# 搜索引擎
"https://www.google.com/"
# 通讯工具
"https://t.me/"
)
# User-Agent 列表(2025年最新版本,最常用设备)
UAS=(
# === Android 设备 ===
# Samsung Galaxy (市场占有率最高)
"Mozilla/5.0 (Linux; Android 14; SM-S24 Ultra) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36"
"Mozilla/5.0 (Linux; Android 14; SM-S23) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36"
# Google Pixel
"Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.58 Mobile Safari/537.36"
# === iOS 设备 ===
# iPhone - Safari (最常用)
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Mobile/15E148 Safari/604.1"
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.7 Mobile/15E148 Safari/604.1"
# iPhone - Chrome
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/130.0.6723.90 Mobile/15E148 Safari/604.1"
# === Windows 桌面 ===
# Chrome (最常用)
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
# Edge
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.2849.68"
# === macOS 桌面 ===
# Safari
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15"
# Chrome on Mac
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
)
hosts=( "org.cc.cc" "sky.cc.cc" )
# 生成随机 ref 参数(30% 概率为空,70% 概率为 6-12 位随机字符串)
rand_ref() {
if [ $((RANDOM % 10)) -lt 3 ]; then
printf ""
else
local len=$((6 + RANDOM % 7))
# 使用更便携的随机字符串生成方式
if [ -r /dev/urandom ]; then
head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c "$len"
else
# 备用方案(如果 /dev/urandom 不可用)
< /dev/urandom tr -dc 'a-zA-Z0-9' | head -c "$len"
fi
fi
}
# 随机选择一个 User-Agent
choose_ua() {
echo "${UAS[$((RANDOM % ${#UAS[@]}))]}"
}
# 随机选择一个 Referer
choose_referer() {
echo "${REFERERS[$((RANDOM % ${#REFERERS[@]}))]}"
}
# 根据地理位置获取 Accept-Language
get_accept_language() {
local country_code=""
# 尝试从 ipinfo.io 获取国家代码(设置超时 3 秒)
if command -v curl &> /dev/null; then
country_code=$(curl -s --max-time 3 https://ipinfo.io/country 2>/dev/null | tr -d '[:space:]')
fi
# 根据国家代码返回对应的 Accept-Language
case "$country_code" in
CN|HK|TW|MO) # 中国、香港、台湾、澳门
echo "zh-CN,zh;q=0.9,en;q=0.8"
;;
JP) # 日本
echo "ja,en;q=0.9"
;;
KR) # 韩国
echo "ko,en;q=0.9"
;;
ES|MX|AR|CO|CL|PE|VE) # 西班牙语国家
echo "es,en;q=0.9"
;;
FR|BE|CH) # 法语国家
echo "fr,en;q=0.9"
;;
DE|AT) # 德语国家
echo "de,en;q=0.9"
;;
RU|BY|KZ|UA) # 俄语国家
echo "ru,en;q=0.9"
;;
PT|BR) # 葡萄牙语国家
echo "pt,en;q=0.9"
;;
IT) # 意大利
echo "it,en;q=0.9"
;;
TR) # 土耳其
echo "tr,en;q=0.9"
;;
SA|AE|EG|IQ|JO) # 阿拉伯语国家
echo "ar,en;q=0.9"
;;
IN) # 印度
echo "en-IN,hi;q=0.9,en;q=0.8"
;;
*) # 默认英文(包括 US, GB, CA, AU, SG 等)
echo "en-US,en;q=0.9"
;;
esac
}
# 执行访问请求
make_visit_request() {
local host="$1"
local ref_url="$2" # URL 参数中的 refreferral site URL
local ua="$3"
local referer="https://${host}/" # HTTP Header 中的 Referer 固定为自身
# 构建完整 URLref 参数为 referral site 的完整 URL
local url="https://${host}${VISIT_PATH}${ref_url}"
log_info "正在访问: ${host}"
log_info "URL: ${url}"
log_info "Referer Header: ${referer}"
log_info "User-Agent: ${ua}"
# 使用 || true 确保即使 curl 失败也不会导致脚本退出
local curl_exit_code=0
curl -s -S -X GET "$url" \
-H "Host: ${host}" \
-H "Connection: keep-alive" \
-H "Accept: */*" \
-H "X-Requested-With: XMLHttpRequest" \
-H "User-Agent: ${ua}" \
-H "Content-Type: charset=utf-8" \
-H "Referer: ${referer}" \
-H "Accept-Encoding: gzip, deflate" \
-H "Accept-Language: ${ACCEPT_LANG}" \
--compressed -o /dev/null --max-time ${MAX_TIME} || curl_exit_code=$?
if [ $curl_exit_code -eq 0 ]; then
log_info "${host} 访问成功"
return 0
else
log_error "${host} 访问失败 (curl 退出码: ${curl_exit_code})"
return 1
fi
}
# 执行电话点击请求
make_click_request() {
local host="$1"
local ua="$2"
local referer="https://${host}/" # HTTP Header 中的 Referer 固定为自身
# 构建完整 URL
local url="https://${host}${CLICK_PATH}"
log_info " ├─ 电话点击统计"
log_info " ├─ URL: ${url}"
# 使用 || true 确保即使 curl 失败也不会导致脚本退出
local curl_exit_code=0
curl -s -S -X GET "$url" \
-H "Host: ${host}" \
-H "Connection: keep-alive" \
-H "Accept: */*" \
-H "X-Requested-With: XMLHttpRequest" \
-H "User-Agent: ${ua}" \
-H "Content-Type: charset=utf-8" \
-H "Referer: ${referer}" \
-H "Accept-Encoding: gzip, deflate" \
-H "Accept-Language: ${ACCEPT_LANG}" \
--compressed -o /dev/null --max-time ${MAX_TIME} || curl_exit_code=$?
if [ $curl_exit_code -eq 0 ]; then
log_info " └─ ✓ 电话点击记录成功"
return 0
else
log_error " └─ ✗ 电话点击记录失败 (curl 退出码: ${curl_exit_code})"
return 1
fi
}
MAX_TIME=15
SLEEP_MIN=3
SLEEP_MAX=6
# 主执行流程
main() {
log_info "==================== 开始执行 ===================="
log_info "目标主机数量: ${#hosts[@]}"
local success_count=0
local fail_count=0
# 获取地理位置对应的语言
ACCEPT_LANG="$(get_accept_language)"
log_info "Accept-Language: ${ACCEPT_LANG}"
# 为本次执行生成统一的 UA(所有请求使用同一个 UA)
local ua
ua="$(choose_ua)"
for host in "${hosts[@]}"; do
local ref_url=""
# 33% 概率直接访问(unknown),67% 概率从社交媒体跳转
if [ $((RANDOM % 3)) -eq 0 ]; then
# 直接访问,ref 参数为空
ref_url=""
log_info "直接访问(referral site: unknown"
else
# 从随机社交媒体跳转
ref_url="$(choose_referer)"
log_info "referral site: ${ref_url}"
fi
# 执行访问请求,不管成功失败都继续
if make_visit_request "$host" "$ref_url" "$ua"; then
((success_count++))
# 20% 概率点击电话统计
if [ $((RANDOM % 5)) -eq 0 ]; then
log_info " ↳ 触发电话点击(20% 概率)"
make_click_request "$host" "$ua" || true
fi
else
((fail_count++))
fi
# 在请求之间随机等待(最后一个请求除外)
if [ "$host" != "${hosts[-1]}" ]; then
local sleep_sec=$((SLEEP_MIN + RANDOM % (SLEEP_MAX - SLEEP_MIN + 1)))
local ms=$((RANDOM % 1000))
local sleep_time
# 检查 awk 是否可用
if command -v awk &> /dev/null; then
sleep_time=$(awk "BEGIN{printf \"%.3f\", ${sleep_sec} + ${ms}/1000}")
else
sleep_time="${sleep_sec}"
fi
log_info "等待 ${sleep_time} 秒..."
sleep "$sleep_time"
fi
done
log_info "==================== 执行完成 ===================="
log_info "成功: ${success_count}, 失败: ${fail_count}"
# 如果有失败的请求,返回非零退出码
if [ "$fail_count" -gt 0 ]; then
exit 1
fi
}
# 执行主函数
main
+581
View File
@@ -0,0 +1,581 @@
#!/bin/bash
# mihomo 安装脚本
# 作者: su
# 版本: v1.0.0
# 日期: 2024年11月14日
set -e -o pipefail
# 定义颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # 无颜色
# 检测系统类型和架构
detect_system() {
if [ -f /etc/alpine-release ]; then
SYSTEM_TYPE="Alpine"
elif command -v systemctl > /dev/null; then
SYSTEM_TYPE="Debian"
elif [ -f /etc/openwrt_release ]; then
SYSTEM_TYPE="OpenWrt"
elif [ -d /etc/init.d ]; then
SYSTEM_TYPE="Init.d"
else
echo "不支持的初始化系统"
exit 1
fi
ARCH_RAW=$(uname -m)
case "${ARCH_RAW}" in
'x86_64') ARCH='amd64';;
'x86' | 'i686' | 'i386') ARCH='386';;
'aarch64' | 'arm64') ARCH='arm64';;
'armv7l') ARCH='armv7';;
's390x') ARCH='s390x';;
*) echo "Unsupported architecture: ${ARCH_RAW}"; exit 1;;
esac
if [ "$SYSTEM_TYPE" == "Debian" ]; then
SYSTEM_VERSION=$(cat /etc/os-release | grep VERSION_ID | cut -d'=' -f2 | tr -d '"')
elif [ "$SYSTEM_TYPE" == "Alpine" ]; then
SYSTEM_VERSION=$(cat /etc/alpine-release)
elif [ "$SYSTEM_TYPE" == "OpenWrt" ]; then
SYSTEM_VERSION=$(cat /etc/openwrt_release | grep DISTRIB_RELEASE | cut -d'=' -f2)
else
SYSTEM_VERSION="Unknown"
fi
echo -e "${GREEN}当前系统为 ${SYSTEM_TYPE}-${SYSTEM_VERSION}_${ARCH_RAW}${NC}"
}
# 设置时区
set_timezone() {
if [ "$SYSTEM_TYPE" == "Alpine" ]; then
echo -e "${YELLOW}检测到 Alpine 系统,使用 cp 和 echo 设置时区${NC}"
apk add --no-cache tzdata
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo "Asia/Shanghai" > /etc/timezone
apk del tzdata
else
CURRENT_TIMEZONE=$(timedatectl show --property=Timezone --value)
if [ "$CURRENT_TIMEZONE" != "Asia/Shanghai" ]; then
echo -e "${YELLOW}设置时区为Asia/Shanghai${NC}"
timedatectl set-timezone Asia/Shanghai
fi
fi
}
# 安装必要软件
install_software() {
if [ "$SYSTEM_TYPE" == "Alpine" ]; then
echo -e "${YELLOW}正在安装必要软件${NC}"
apk add --no-cache curl nano grep gzip
else
echo -e "${YELLOW}正在安装必要软件${NC}"
if command -v apt-get > /dev/null; then
apt-get update
apt-get install -y curl nano gzip
elif command -v yum > /dev/null; then
yum install -y curl nano gzip
else
echo -e "${RED}不支持的包管理器${NC}"
exit 1
fi
fi
}
# 检测并下载 mihomo 版本
download_mihomo() {
VERSION=$(curl -sL "https://gh.llkk.cc/https://api.github.com/repos/MetaCubeX/mihomo/releases/latest" | grep -oP '"tag_name": "\K(.*)(?=")')
if [ -z "$VERSION" ]; then
echo -e "${RED}无法获取最新版本号${NC}"
exit 1
fi
echo -e "${GREEN}获取到的版本: ${VERSION}${NC}"
DOWNLOAD_URL="https://gh.llkk.cc/https://github.com/MetaCubeX/mihomo/releases/download/${VERSION}/mihomo-linux-${ARCH}-${VERSION}.gz"
echo "${DOWNLOAD_URL} 下载 mihomo"
curl -Lo mihomo.gz "${DOWNLOAD_URL}"
echo -e "${YELLOW}Mihomo ${VERSION} 下载完成, 开始安装${NC}"
gzip -d mihomo.gz
chmod +x mihomo
mv mihomo /usr/local/bin/
}
# 删除已有服务
remove_existing_service() {
if [ -f /etc/systemd/system/mihomo.service ] || [ -f /etc/init.d/mihomo ] || [ -f /etc/rc.d/mihomo ]; then
read -p "检测到已有的 mihomo 服务,是否删除?(y/n): " choice
case "$choice" in
y|Y )
if [ -f /etc/systemd/system/mihomo.service ]; then
systemctl stop mihomo
systemctl disable mihomo
rm -f /etc/systemd/system/mihomo.service
systemctl daemon-reload
fi
if [ -f /etc/init.d/mihomo ]; then
/etc/init.d/mihomo stop
rm -f /etc/init.d/mihomo
fi
if [ -f /etc/rc.d/mihomo ]; then
/etc/rc.d/mihomo stop
rm -f /etc/rc.d/mihomo
fi
;;
n|N )
echo -e "${YELLOW}保留已有的 mihomo 服务,退出安装。${NC}"
exit 0
;;
* )
echo -e "${RED}无效的选择,退出安装。${NC}"
exit 1
;;
esac
fi
}
# 配置 OpenRC 服务,Alpine 专用
configure_openrc() {
echo "创建 OpenRC 服务文件"
cat <<EOF > /etc/init.d/mihomo
#!/sbin/openrc-run
command="/usr/local/bin/mihomo"
command_args="-d /etc/mihomo"
command_background=true
pidfile="/var/run/mihomo.pid"
name="Mihomo Service"
depend() {
need net
}
start_pre() {
ebegin "等待 1 秒"
sleep 1
eend \$?
}
EOF
chmod +x /etc/init.d/mihomo
rc-update add mihomo default
rc-service mihomo start
}
# 配置 systemd 服务,Debian系 专用
configure_systemd() {
echo "创建 systemd 服务文件"
cat <<EOF > /etc/systemd/system/mihomo.service
[Unit]
Description=mihomo Daemon, Another Clash Kernel.
After=network.target NetworkManager.service systemd-networkd.service iwd.service
[Service]
Type=simple
LimitNPROC=500
LimitNOFILE=1000000
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
Restart=always
ExecStartPre=/usr/bin/sleep 1s
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
ExecReload=/bin/kill -HUP \$MAINPID
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl start mihomo
systemctl enable mihomo
}
# 配置 init.d 服务,OpenWrt 和 Init.d 专用
configure_initd() {
echo "创建 init.d 服务文件"
cat <<EOF > /etc/init.d/mihomo
#!/bin/sh
### BEGIN INIT INFO
# Provides: mihomo
# Required-Start: \$network
# Required-Stop: \$network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Mihomo Service
### END INIT INFO
start() {
echo "Starting mihomo"
/usr/bin/sleep 1
/usr/local/bin/mihomo -d /etc/mihomo &
}
stop() {
echo "Stopping mihomo"
pkill -f /usr/local/bin/mihomo
}
restart() {
stop
start
}
case "\$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo "Usage: /etc/init.d/mihomo {start|stop|restart}"
exit 1
;;
esac
exit 0
EOF
chmod +x /etc/init.d/mihomo
update-rc.d mihomo defaults
/etc/init.d/mihomo start
}
# 内置配置
BUILTIN_CONFIG=$(cat <<EOF
port: 7890
socks-port: 7891
redir-port: 7892
tproxy-port: 7893
mixed-port: 1080
mode: rule
log-level: info
allow-lan: true # 允许局域网连接
bind-address: "*" # 绑定所有地址
ipv6: true # IPv6 总开关
tcp-concurrent: true # tcp 并发
unified-delay: true # 统一延迟
find-process-mode: strict # 进程匹配
global-client-fingerprint: random # 随机指纹
global-ua: clash-verge/v1.8.10 # 下载资源 UA
proxies:
# clashMeta配置参考可以看看这个wiki
# https://wiki.metacubex.one
### 锚点
p: &p # 订阅
type: http
interval: 1800
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 300
t : &t # 节点
type: url-test
include-all: true
url: https://www.gstatic.com/generate_204
interval: 300
skip-cert-verify: true # 跳过 TLS 证书验证
tolerance: 15 # 存在比当前节点延迟低15时切换
lazy: true # 未选择此策略组时,不会进行检测
sort:
delay: asc
speed: desc
r: &r # 规则
type: http
format: yaml
behavior: classical
interval: 86400
# 代理提供(订阅)组
proxy-providers:
1.机场1: # 机场名
url: ""
path: ./proxy_providers/机场1.yaml
#指定保存路径 非必填
<<: *p
2.机场2: # 机场名
url: ""
path: ./proxy_providers/机场2.yaml
proxy: DIRECT
<<: *p
# 代理组
proxy-groups:
- name: 🎯 总模式
type: select
proxies:
- ♻️ 自动选择
- 🚀 手动选择
- 🇭🇰 香港节点
- 🇸🇬 狮城节点
- 🇹🇼 台湾节点
- 🇯🇵 日本节点
- 🇺🇸 美国节点
- 🌎 其它地区
- 🔃 负载均衡-轮询
- 🔃 负载均衡-散列
- 直连
# 策略组
- name: ♻️ 自动选择
<<: *t
- name: 🚀 手动选择
type: select
<<: *t
- name: 🇭🇰 香港节点
filter: "(?i)🇭🇰|港|hk|hongkong|hong kong"
<<: *t
- name: 🇸🇬 狮城节点
filter: "(?i)🇸🇬|新|sg|singapore"
<<: *t
- name: 🇹🇼 台湾节点
filter: "(?i)🇹🇼|台|tw|taiwan"
<<: *t
- name: 🇯🇵 日本节点
filter: "(?i)🇯🇵|日|jp|japan"
<<: *t
- name: 🇺🇸 美国节点
filter: "(?i)🇺🇸|美|us|unitedstates|united states"
<<: *t
- name: 🌎 其它地区
type: select
filter: "(?i)^(?!.*(?:🇭🇰|🇯🇵|🇺🇸|🇸🇬|🇨🇳|港|hk|hongkong|台|tw|taiwan|日|jp|japan|新|sg|singapore|美|us|unitedstates)).*"
<<: *t
- name: 🔃 负载均衡-轮询
type: load-balance
strategy: round-robin
filter: "🇭🇰|🇯🇵|🇸🇬|香港|hk|HK|hongkong|台|台湾|TW|taiwan|日本|jp|JP|新加坡|sg|韩国|中转|狮城|菲律宾"
<<: *t
- name: 🔃 负载均衡-散列
type: load-balance
strategy: consistent-hashing
filter: "🇭🇰|🇯🇵|🇺🇸|🇸🇬|香港|hk|HK|hongkong|台|台湾|TW|taiwan|日本|jp|JP|新加坡|sg|美国|US"
<<: *t
- name: 直连
type: select
proxies:
- DIRECT
# 分流规则提供(订阅)组
rule-providers:
AWAvenue: # 秋风去广告
behavior: domain
path: ./rule_providers/AWAvenue-Ads-Rule-Clash.yaml
url: "https://raw.githubusercontent.com/TG-Twilight/AWAvenue-Ads-Rule/main/Filters/AWAvenue-Ads-Rule-Clash.yaml"
<<: *r
# OpenAi: # AI 服务
# path: ./rule_providers/OpenAi.yaml
# url: "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/Providers/Ruleset/OpenAi.yaml"
# <<: *r
直连: # 自定义直连
type: file
behavior: domain
format: text
path: ./rule_providers/直连.yaml
代理: # 自定义代理
type: file
behavior: domain
format: text
path: ./rule_providers/代理.yaml
# 其他参数
sniffer:
enable: true
sniff:
HTTP:
ports: [80, 8080-8880]
TLS:
ports: [443, 8443]
QUIC:
ports: [443, 8443]
skip-domain:
- Mijia Cloud
profile:
store-selected: true # 存储 select 选择记录
store-fake-ip: true # 持久化 fake-ip
# webui
external-controller: 0.0.0.0:9090
external-ui: webui
external-ui-name: xd
external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip"
# geo
geox-url:
geoip: "https://gcore.jsdelivr.net/gh/Loyalsoldier/geoip@release/geoip.dat"
geosite: "https://gcore.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat"
mmdb: "https://gcore.jsdelivr.net/gh/Loyalsoldier/geoip@release/Country.mmdb"
geodata-mode: true
geodata-loader: standard
geo-auto-update: true
geo-update-interval: 24
tun:
enable: true
device: tun
stack: mixed
dns-hijack:
- any:53
- tcp://any:53
auto-route: true
auto-redirect: true
auto-detect-interface: true
dns:
enable: true
cache-algorithm: arc
use-system-hosts: true
ipv6: true
listen: 0.0.0.0:1053
enhanced-mode: redir-host # fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- '+.lan'
- '+.invalid.*'
- '+.localhost'
- '+.local.*'
- '+.time.*'
- '+.ntp.*'
- '+.time.edu.cn'
- '+.ntp.org.cn'
- '+.pool.ntp.org'
- '+.qpic.cn'
- "+.stun.*"
- "+.stun.*.*"
- "+.stun.*.*.*"
- '+.sushen.tk'
- localhost.ptlogin2.qq.com
- dns.msftncsi.com
- www.msftncsi.com
- www.msftconnecttest.com
- time1.cloud.tencent.com
nameserver:
- https://1.0.0.1/dns-query
- https://8.8.8.8/dns-query
- https://208.67.222.222/dns-query
proxy-server-nameserver:
- https://119.29.29.29/dns-query
- https://223.5.5.5/dns-query
nameserver-policy:
"geosite:category-ads-all":
- rcode://success
direct-nameserver:
- system
- 119.29.29.29
- 223.5.5.5
direct-nameserver-follow-policy: true
# 分流规则
rules:
- AND,(AND,(DST-PORT,443),(NETWORK,UDP)),(NOT,((GEOSITE,CN))),REJECT-DROP # 禁用quic(不包括国内)
- RULE-SET,AWAvenue,REJECT-DROP
# - RULE-SET,OpenAi,🇸🇬 狮城节点
- RULE-SET,直连,DIRECT
- RULE-SET,代理,🎯 总模式
# - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- GEOSITE,openai,🇸🇬 狮城节点
- GEOSITE,ehentai,🎯 总模式
- GEOSITE,github,🎯 总模式
- GEOSITE,twitter,🎯 总模式
- GEOSITE,youtube,🎯 总模式
- GEOSITE,google,🎯 总模式
- GEOSITE,telegram,🎯 总模式
- GEOSITE,netflix,🎯 总模式
- GEOSITE,bahamut,🎯 总模式
- GEOSITE,spotify,🎯 总模式
- GEOSITE,bilibili,DIRECT
- GEOSITE,CN,DIRECT
- GEOSITE,private,DIRECT
- GEOSITE,category-ads-all,REJECT-DROP
- GEOIP,google,🎯 总模式
- GEOIP,netflix,🎯 总模式
- GEOIP,telegram,🎯 总模式
- GEOIP,twitter,🎯 总模式
- GEOIP,CN,DIRECT,no-resolve
- GEOIP,private,DIRECT,no-resolve
- MATCH,🎯 总模式
EOF
)
# 询问是否使用内置配置
ask_builtin_config() {
read -p "是否使用内置配置?(y/n): " choice
case "$choice" in
y|Y )
echo -e "${YELLOW}使用内置配置,请在配置中添加订阅${NC}"
mkdir /etc/mihomo/
echo "$BUILTIN_CONFIG" > /etc/mihomo/config.yaml
;;
n|N )
echo -e "${YELLOW}不使用内置配置,请上传配置到/etc/mihomo/config.yaml${NC}"
;;
* )
echo -e "${RED}无效的选择,退出安装。${NC}"
exit 1
;;
esac
}
# 启用系统转发
enable_ip_forwarding() {
echo 'net.ipv4.ip_forward = 1' | tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | tee -a /etc/sysctl.conf
sysctl -p
if [ "$SYSTEM_TYPE" == "Alpine" ]; then
rc-update add sysctl
fi
}
# 主函数
main() {
detect_system
set_timezone
install_software
remove_existing_service
download_mihomo
ask_builtin_config
# enable_ip_forwarding
case "$SYSTEM_TYPE" in
"Alpine")
configure_openrc
;;
"Debian")
configure_systemd
;;
"OpenWrt" | "Init.d")
configure_initd
;;
esac
echo -e "${GREEN}脚本执行完毕。${NC}"
}
main
+726
View File
@@ -0,0 +1,726 @@
#!/bin/bash
# mssh - SSH 管理工具
# 配置文件路径
CONFIG_FILE="$HOME/.mssh.conf"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
ORANGE='\033[0;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m'
# Emoji 定义
SERVER_EMOJI="🌐"
KEY_EMOJI="🔑"
PASSWORD_EMOJI="🔒"
CONNECT_EMOJI="🔌"
SUCCESS_EMOJI="✅"
ERROR_EMOJI="❌"
WARNING_EMOJI="⚠️"
INFO_EMOJI="𝒊"
FORWARD_EMOJI="📡"
# 信号处理
graceful_exit() {
echo -e "\n${INFO_EMOJI} ${CYAN} 正在退出 mssh...${NC}"
# 检查是否有活跃的端口转发
local active_forwards
active_forwards=$(get_section "active_forwards")
if [ -n "$active_forwards" ]; then
echo -e "${WARNING_EMOJI} ${YELLOW}检测到活跃的端口转发进程${NC}"
echo -e "${INFO_EMOJI} ${CYAN}这些进程将继续在后台运行${NC}"
echo -e "${CYAN}您可以随时重新启动 mssh 来管理它们${NC}"
echo
# 显示活跃的转发
local i=1
while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do
[ -z "$rule_name" ] && continue
printf " ${GREEN}%2d.${NC} ${FORWARD_EMOJI} ${YELLOW}%s${NC} (PID:%s)\n" "$i" "$rule_name" "$pid"
((i++))
done <<< "$active_forwards"
fi
echo -e "\n🔚 脚本已退出"
exit 0
}
# 设置信号处理
trap graceful_exit SIGINT SIGTERM SIGQUIT
# 密码加密函数
encrypt_password() {
local password="$1"
[ -z "$password" ] && return
# 简单的字符位移 + base64
local encrypted
encrypted=$(echo "$password" | tr 'A-Za-z0-9' 'N-ZA-Mn-za-m5-90-4' | base64 -w 0)
echo "ENC:$encrypted"
}
# 密码解密函数
decrypt_password() {
local encrypted="$1"
[ -z "$encrypted" ] && return
# 解密格式:ENC:base64data
if [[ "$encrypted" =~ ^ENC: ]]; then
local encrypted_data="${encrypted#ENC:}"
echo "$encrypted_data" | base64 -d | tr 'N-ZA-Mn-za-m5-90-4' 'A-Za-z0-9'
fi
}
# 安全读取函数
safe_read() {
local prompt="$1"
local var_name="$2"
local response
if ! read -r -p "$prompt" response; then
graceful_exit
fi
if [ -n "$var_name" ]; then
eval "$var_name=\"$response\""
fi
}
# 检查 sshpass
check_sshpass() {
if ! command -v sshpass >/dev/null 2>&1; then
echo -e "${ERROR_EMOJI} ${RED}未找到 sshpass。某些功能可能不可用。${NC}"
echo -e "${INFO_EMOJI} ${YELLOW}可以运行以下命令安装:${NC}"
echo -e " ${CYAN}Ubuntu/Debian: sudo apt install sshpass${NC}"
echo -e " ${CYAN}CentOS/RHEL: sudo yum install sshpass${NC}"
fi
}
# 初始化配置文件
init_config() {
if [ ! -f "$CONFIG_FILE" ]; then
cat > "$CONFIG_FILE" << 'EOF'
# mssh 配置文件
# 格式说明:
# [servers] 部分:alias|user@host|port|keypath|encrypted_password
# [port_forwards] 部分:server_alias|rule_name|local_port|remote_host|remote_port|user@host|port|keypath|encrypted_password
# [active_forwards] 部分:rule_name|local_port|pid|user@host|port|server_alias|remote_host|remote_port
[servers]
[port_forwards]
[active_forwards]
EOF
echo -e "${SUCCESS_EMOJI} ${GREEN}配置文件已创建: $CONFIG_FILE${NC}"
fi
}
# 获取配置文件中的特定部分
get_section() {
local section="$1"
local config_file="${2:-$CONFIG_FILE}"
awk -v section="[$section]" '
$0 == section { in_section = 1; next }
/^\[.*\]$/ { in_section = 0; next }
in_section && NF > 0 && !/^#/ { print }
' "$config_file"
}
# 格式化端口转发显示
format_forward_display() {
local num="$1"
local rule_name="$2"
local local_port="$3"
local server_alias="$4"
local remote_host="$5"
local remote_port="$6"
local pid="$7"
local pid_text=""
[ -n "$pid" ] && pid_text=" PID:$pid"
if [ "$remote_host" = "127.0.0.1" ]; then
printf " ${GREEN}%2d.${NC} ${FORWARD_EMOJI} ${YELLOW}%s${NC} (本地:%s -> %s:%s)%s\n" \
"$num" "$rule_name" "$local_port" "$server_alias" "$remote_port" "$pid_text"
else
printf " ${GREEN}%2d.${NC} ${FORWARD_EMOJI} ${YELLOW}%s${NC} (本地:%s -> %s -> %s:%s)%s\n" \
"$num" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port" "$pid_text"
fi
}
# 显示服务器列表
show_servers() {
echo -e "${CYAN}已保存的服务器:${NC}"
echo -e "${CYAN}---------------${NC}"
local servers
servers=$(get_section "servers")
if [ -z "$servers" ]; then
echo -e " ${YELLOW}暂无服务器${NC}"
return 0
fi
local i=1
while IFS='|' read -r alias_name user_host port key_path password; do
[ -z "$alias_name" ] && continue
# 显示密码和密钥状态
local has_password=""
local has_key=""
[ -n "$password" ] && has_password="${PASSWORD_EMOJI}"
[ -n "$key_path" ] && [ -f "$key_path" ] && has_key="${KEY_EMOJI}"
printf " ${GREEN}%2d.${NC} ${SERVER_EMOJI} ${YELLOW}%s${NC} (%s:%s) %s%s\n" \
"$i" "$alias_name" "$user_host" "$port" "$has_password" "$has_key"
((i++))
done <<< "$servers"
return $((i-1))
}
# 显示活跃的端口转发
show_active_forwards() {
local active_forwards
active_forwards=$(get_section "active_forwards")
if [ -n "$active_forwards" ]; then
echo
echo -e "${CYAN}已启用的端口转发:${NC}"
echo -e "${CYAN}------------------${NC}"
local i=1
while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do
[ -z "$rule_name" ] && continue
format_forward_display "$i" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port" "$pid"
((i++))
done <<< "$active_forwards"
return 0
else
return 1
fi
}
# 检查并清理无效的端口转发进程
check_and_cleanup_forwards() {
local active_forwards
active_forwards=$(get_section "active_forwards")
if [ -z "$active_forwards" ]; then
return 0
fi
local temp_file
temp_file=$(mktemp)
local cleanup_needed=false
local cleaned_count=0
# 复制配置文件到临时文件
cp "$CONFIG_FILE" "$temp_file"
while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do
[ -z "$rule_name" ] && continue
# 检查进程是否存在
if ! kill -0 "$pid" 2>/dev/null; then
# 进程不存在,从配置中移除
local rule_line="${rule_name}|${local_port}|${pid}|${user_host}|${port}|${server_alias}|${remote_host}|${remote_port}"
awk -v target_line="$rule_line" '
$0 == target_line && in_active { next }
/^\[active_forwards\]$/ { in_active=1 }
/^\[.*\]$/ && !/^\[active_forwards\]$/ { in_active=0 }
{ print }
' "$temp_file" > "${temp_file}.new" && mv "${temp_file}.new" "$temp_file"
cleanup_needed=true
((cleaned_count++))
fi
done <<< "$active_forwards"
# 如果有清理,更新配置文件并显示信息
if [ "$cleanup_needed" = true ]; then
mv "$temp_file" "$CONFIG_FILE"
if [ "$cleaned_count" -gt 0 ]; then
echo -e "${INFO_EMOJI} ${YELLOW}已清理 $cleaned_count 个失效的端口转发进程${NC}"
sleep 1
fi
else
rm -f "$temp_file"
fi
}
# 主菜单
main_menu() {
# 在显示菜单前检查并清理无效的端口转发进程
check_and_cleanup_forwards
clear
echo -e "${PURPLE}==========================================${NC}"
echo -e "${PURPLE}:: mssh - SSH 管理工具 ::${NC}"
echo -e "${PURPLE}==========================================${NC}"
echo
show_servers
show_active_forwards
local has_active_forwards=$?
echo
echo -e "${BLUE}选项分区:${NC}"
echo -e "${BLUE}---------${NC}"
echo -e " ${GREEN}[a] 增加服务器${NC}"
echo -e " ${RED}[d] 删除服务器${NC}"
echo -e " ${BLUE}[p] 端口转发${NC}"
echo -e " ${CYAN}[q] 退出${NC}"
# 显示端口转发持久化提示
if [ $has_active_forwards -eq 0 ]; then
echo
echo -e "${INFO_EMOJI} ${YELLOW}提示:端口转发将在退出程序后继续运行${NC}"
fi
echo
echo -ne "${GREEN}请选择操作: ${NC}"
}
# 添加服务器
add_server() {
while true; do
clear
echo -e "${PURPLE}============ 添加服务器 ============${NC}"
echo
echo -e "${INFO_EMOJI} ${CYAN}添加新服务器 (0或回车返回)${NC}"
echo
safe_read "$(echo -e "${CYAN}输入别名: ${NC}")" alias_name; [ "$alias_name" = "0" ] || [ -z "$alias_name" ] && return
safe_read "$(echo -e "${CYAN}输入用户名@主机: ${NC}")" user_host; [ "$user_host" = "0" ] || [ -z "$user_host" ] && return
safe_read "$(echo -e "${CYAN}输入端口 (默认22): ${NC}")" port; [ "$port" = "0" ] && return; port=${port:-22}
echo -ne "${CYAN}输入密码 (可选): ${NC}"
read -r -s password
echo
safe_read "$(echo -e "${CYAN}输入私钥路径 (可选): ${NC}")" key_path; [ "$key_path" = "0" ] && return
if [ -n "$key_path" ] && [ ! -f "$key_path" ]; then
echo -e "${ERROR_EMOJI} ${RED}密钥文件不存在${NC}"
safe_read "$(echo -e "${YELLOW}是否继续? (y/N): ${NC}")" continue_without_key
if [[ ! "${continue_without_key:-n}" =~ [yY] ]]; then
continue
fi
key_path=""
fi
# 加密密码
[ -n "$password" ] && password=$(encrypt_password "$password")
# 添加到配置文件
local temp_file
temp_file=$(mktemp)
awk -v new_server="${alias_name}|${user_host}|${port}|${key_path}|${password}" '
/^\[servers\]$/ { print; in_servers=1; next }
/^\[.*\]$/ { in_servers=0 }
in_servers && /^$/ { print new_server; added=1 }
{ print }
END { if (!added && in_servers) print new_server }
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
echo -e "${SUCCESS_EMOJI} ${GREEN}服务器已添加: $alias_name${NC}"
echo
safe_read "$(echo -e "${YELLOW}是否继续添加? (y/N): ${NC}")" continue_add
if [[ ! "${continue_add:-n}" =~ [yY] ]]; then
break
fi
done
}
# 删除服务器
delete_server() {
while true; do
clear
echo -e "${PURPLE}============ 删除服务器 ============${NC}"
echo
echo -e "${WARNING_EMOJI} ${YELLOW}删除服务器${NC}"
echo
show_servers
echo
safe_read "$(echo -e "${CYAN}选择要删除的服务器编号 (0或回车返回): ${NC}")" choice
[ "$choice" = "0" ] || [ -z "$choice" ] && return
local servers
servers=$(get_section "servers")
local server_line
server_line=$(echo "$servers" | sed -n "${choice}p")
if [ -n "$server_line" ]; then
local alias_name
alias_name=$(echo "$server_line" | cut -d'|' -f1)
echo -e "${WARNING_EMOJI} ${YELLOW}确认删除服务器: $alias_name${NC}"
local temp_file
temp_file=$(mktemp)
awk -v target_line="$server_line" '
$0 == target_line && in_servers { next }
/^\[servers\]$/ { in_servers=1 }
/^\[.*\]$/ && !/^\[servers\]$/ { in_servers=0 }
{ print }
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
echo -e "${SUCCESS_EMOJI} ${GREEN}服务器已删除: $alias_name${NC}"
else
echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}"
fi
echo
safe_read "$(echo -e "${YELLOW}是否继续删除? (y/N): ${NC}")" continue_del
if [[ ! "${continue_del:-n}" =~ [yY] ]]; then
break
fi
done
}
# 添加端口转发规则
add_port_forward_rule() {
echo -e "${INFO_EMOJI} ${CYAN}添加新端口转发规则 (0或回车返回):${NC}"
safe_read "$(echo -e "${CYAN}输入规则名称: ${NC}")" rule_name
if [ "$rule_name" = "0" ] || [ -z "$rule_name" ]; then
return 1
fi
safe_read "$(echo -e "${CYAN}输入本地端口: ${NC}")" local_port
if [ "$local_port" = "0" ] || [ -z "$local_port" ]; then
return 1
fi
safe_read "$(echo -e "${CYAN}输入远程主机 (默认127.0.0.1): ${NC}")" remote_host
if [ "$remote_host" = "0" ]; then
return 1
fi
remote_host=${remote_host:-127.0.0.1}
safe_read "$(echo -e "${CYAN}输入远程端口 (默认${local_port}): ${NC}")" remote_port
if [ "$remote_port" = "0" ]; then
return 1
fi
remote_port=${remote_port:-$local_port}
echo -e "\n${CYAN}请选择要使用的服务器:${NC}"
show_servers
echo
safe_read "$(echo -e "${CYAN}选择服务器编号: ${NC}")" server_choice
if [ "$server_choice" = "0" ] || [ -z "$server_choice" ]; then
return 1
fi
local servers
servers=$(get_section "servers")
local selected_server
selected_server=$(echo "$servers" | sed -n "${server_choice}p")
if [ -n "$selected_server" ]; then
IFS='|' read -r server_alias user_host port key_path password <<< "$selected_server"
local temp_file
temp_file=$(mktemp)
awk -v new_rule="${server_alias}|${rule_name}|${local_port}|${remote_host}|${remote_port}|${user_host}|${port}|${key_path}|${password}" '
/^\[port_forwards\]$/ { print; in_forwards=1; next }
/^\[.*\]$/ { in_forwards=0 }
in_forwards && /^$/ { print new_rule; added=1 }
{ print }
END { if (!added && in_forwards) print new_rule }
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
echo -e "${SUCCESS_EMOJI} ${GREEN}端口转发规则已添加: $rule_name${NC}"
return 0
else
echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}"
return 1
fi
}
# 删除端口转发规则
delete_port_forward() {
local rule_number="$1"
local forwards
forwards=$(get_section "port_forwards")
local rule_line
rule_line=$(echo "$forwards" | sed -n "${rule_number}p")
if [ -n "$rule_line" ]; then
local rule_name
rule_name=$(echo "$rule_line" | cut -d'|' -f2)
local temp_file
temp_file=$(mktemp)
awk -v target_line="$rule_line" '
$0 == target_line && in_forwards { next }
/^\[port_forwards\]$/ { in_forwards=1 }
/^\[.*\]$/ && !/^\[port_forwards\]$/ { in_forwards=0 }
{ print }
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
echo -e "${SUCCESS_EMOJI} ${GREEN}规则已删除: $rule_name${NC}"
else
echo -e "${ERROR_EMOJI} ${RED}无效的规则编号!${NC}"
fi
}
# 停止后台端口转发
stop_background_forward() {
local rule_number="$1"
local active_forwards
active_forwards=$(get_section "active_forwards")
local active_line
active_line=$(echo "$active_forwards" | sed -n "${rule_number}p")
if [ -n "$active_line" ]; then
IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port <<< "$active_line"
if kill "$pid" 2>/dev/null; then
echo -e "${SUCCESS_EMOJI} ${GREEN}端口转发已停止: $rule_name (PID: $pid)${NC}"
else
echo -e "${WARNING_EMOJI} ${YELLOW}进程可能已经停止: $rule_name (PID: $pid)${NC}"
fi
# 从活跃转发列表中移除
local temp_file
temp_file=$(mktemp)
awk -v target_line="$active_line" '
$0 == target_line && in_active { next }
/^\[active_forwards\]$/ { in_active=1 }
/^\[.*\]$/ && !/^\[active_forwards\]$/ { in_active=0 }
{ print }
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
else
echo -e "${ERROR_EMOJI} ${RED}无效的活跃转发编号!${NC}"
fi
}
# 启动后台端口转发
start_background_forward() {
local rule_number="$1"
local forwards
forwards=$(get_section "port_forwards")
local rule_line
rule_line=$(echo "$forwards" | sed -n "${rule_number}p")
if [ -n "$rule_line" ]; then
IFS='|' read -r server_alias rule_name local_port remote_host remote_port user_host port key_path password <<< "$rule_line"
# 解密密码
[ -n "$password" ] && password=$(decrypt_password "$password")
# 构建 SSH 命令
local ssh_args=("-p" "$port" "-L" "${local_port}:${remote_host}:${remote_port}" "-N" "$user_host")
[ -n "$key_path" ] && ssh_args+=("-i" "$key_path")
# 启动后台进程 (使用密码或密钥认证)
local ssh_pid
if [ -n "$password" ] && command -v sshpass >/dev/null 2>&1; then
echo -e "${INFO_EMOJI} ${YELLOW}使用保存的密码启动端口转发...${NC}"
nohup sshpass -p "$password" ssh "${ssh_args[@]}" >/dev/null 2>&1 &
ssh_pid=$!
disown $ssh_pid 2>/dev/null
# 等待SSH连接建立
sleep 1
# 检查进程是否仍在运行
if ! kill -0 "$ssh_pid" 2>/dev/null; then
echo -e "${ERROR_EMOJI} ${RED}端口转发启动失败,请检查网络连接和认证信息${NC}"
return 1
fi
else
echo -e "${INFO_EMOJI} ${YELLOW}启动端口转发 (需要手动输入密码)...${NC}"
nohup ssh "${ssh_args[@]}" </dev/tty >/dev/null 2>&1 &
ssh_pid=$!
disown $ssh_pid 2>/dev/null
# 给用户一些时间输入密码
echo -e "${INFO_EMOJI} ${CYAN}请在上方输入SSH密码...${NC}"
sleep 3
# 检查进程是否仍在运行
if ! kill -0 "$ssh_pid" 2>/dev/null; then
echo -e "${ERROR_EMOJI} ${RED}端口转发启动失败,请检查网络连接和认证信息${NC}"
return 1
fi
fi
local temp_file
temp_file=$(mktemp)
awk -v new_active="${rule_name}|${local_port}|${ssh_pid}|${user_host}|${port}|${server_alias}|${remote_host}|${remote_port}" '
/^\[active_forwards\]$/ { print; in_active=1; next }
/^\[.*\]$/ { in_active=0 }
in_active && /^$/ { print new_active; added=1 }
{ print }
END { if (!added && in_active) print new_active }
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
echo -e "${SUCCESS_EMOJI} ${GREEN}端口转发已启动: $rule_name (PID: $ssh_pid)${NC}"
else
echo -e "${ERROR_EMOJI} ${RED}无效的规则编号!${NC}"
fi
}
# 端口转发管理
list_port_forwards() {
while true; do
clear
echo -e "${PURPLE}=========== 端口转发管理 ===========${NC}"
echo
local forwards
forwards=$(get_section "port_forwards")
if [ -z "$forwards" ]; then
echo -e "${CYAN}已配置的端口转发规则:${NC}"
echo -e "${CYAN}------------------------${NC}"
echo -e " ${YELLOW}暂无端口转发规则${NC}"
else
echo -e "${CYAN}已配置的端口转发规则:${NC}"
echo -e "${CYAN}------------------------${NC}"
local i=1
while IFS='|' read -r server_alias rule_name local_port remote_host remote_port _ _ _ _ || [ -n "$rule_name" ]; do
[ -z "$rule_name" ] && continue
format_forward_display "$i" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port"
((i++))
done <<< "$forwards"
fi
show_active_forwards
echo
echo -e "${BLUE}操作选项:${NC}"
echo -e "${BLUE}---------${NC}"
echo -e " ${GREEN}[1] 添加端口转发${NC}"
echo -e " ${YELLOW}[2] 启动端口转发${NC}"
echo -e " ${ORANGE}[3] 停止端口转发${NC}"
echo -e " ${RED}[4] 删除转发规则${NC}"
echo
safe_read "$(echo -e "${CYAN}请选择操作 (0或回车返回): ${NC}")" choice
case $choice in
0|"") return ;;
1)
echo
if add_port_forward_rule; then
safe_read "按回车键继续..." pause_key
fi
;;
2)
if [ -z "$forwards" ]; then
echo -e "${ERROR_EMOJI} ${RED}暂无端口转发规则可启动!${NC}"
safe_read "按回车键继续..." pause_key
else
safe_read "$(echo -e "${CYAN}输入要启动的规则编号: ${NC}")" start_choice
if [ -n "$start_choice" ]; then
start_background_forward "$start_choice"
fi
safe_read "按回车键继续..." pause_key
fi
;;
3)
local active_forwards
active_forwards=$(get_section "active_forwards")
if [ -z "$active_forwards" ]; then
echo -e "${ERROR_EMOJI} ${RED}暂无活跃的端口转发可停止!${NC}"
safe_read "按回车键继续..." pause_key
else
echo
echo -e "${CYAN}当前活跃的端口转发:${NC}"
local i=1
while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do
[ -z "$rule_name" ] && continue
format_forward_display "$i" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port" "$pid"
((i++))
done <<< "$active_forwards"
echo
safe_read "$(echo -e "${CYAN}输入要停止的端口转发编号: ${NC}")" stop_choice
if [ -n "$stop_choice" ]; then
stop_background_forward "$stop_choice"
fi
safe_read "按回车键继续..." pause_key
fi
;;
4)
if [ -z "$forwards" ]; then
echo -e "${ERROR_EMOJI} ${RED}暂无端口转发规则可删除!${NC}"
safe_read "按回车键继续..." pause_key
else
safe_read "$(echo -e "${CYAN}输入要删除的规则编号: ${NC}")" del_choice
if [ -n "$del_choice" ]; then
delete_port_forward "$del_choice"
fi
safe_read "按回车键继续..." pause_key
fi
;;
*) echo -e "${ERROR_EMOJI} ${RED}无效选项!${NC}"; sleep 1 ;;
esac
done
}
# 连接服务器
connect_server() {
local choice=$1
local servers
servers=$(get_section "servers")
local selected_server
selected_server=$(echo "$servers" | sed -n "${choice}p")
if [ -n "$selected_server" ]; then
IFS='|' read -r alias_name user_host port key_path password <<< "$selected_server"
# 解密密码
[ -n "$password" ] && password=$(decrypt_password "$password")
echo -e "${CONNECT_EMOJI} ${GREEN}连接到: $alias_name ($user_host:$port)${NC}"
local ssh_args=("-p" "$port" "$user_host")
[ -n "$key_path" ] && ssh_args+=("-i" "$key_path")
if [ -n "$password" ] && command -v sshpass >/dev/null 2>&1; then
echo -e "${WARNING_EMOJI} ${YELLOW}使用保存的密码连接...${NC}"
sshpass -p "$password" ssh "${ssh_args[@]}"
else
ssh "${ssh_args[@]}"
fi
else
echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}"
sleep 1
fi
}
# 主程序
check_sshpass
init_config
while true; do
main_menu
safe_read "" choice
case $choice in
[0-9]*)
if [[ "$choice" =~ ^[0-9]+$ ]]; then
connect_server "$choice"
else
echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}"
sleep 1
fi
;;
a|A) add_server ;;
d|D) delete_server ;;
p|P) list_port_forwards ;;
q|Q) graceful_exit ;;
*) echo -e "${ERROR_EMOJI} ${RED}无效选项!${NC}"; sleep 1 ;;
esac
done
+255
View File
@@ -0,0 +1,255 @@
#!/bin/bash
# tcp调优脚本 v25.10.11
# 用法:
# ./tcp.sh 1 启用调优
# ./tcp.sh 0 还原配置
# ./tcp.sh 2 对比优化前后的参数
BACKUP_FILE="/var/backups/tcp_backup_adaptive.conf"
TUNE_FILE="/etc/sysctl.d/tcp_bbr_adaptive.conf"
# 检查是否以 root 权限运行
check_root() {
if [ "$EUID" -ne 0 ]; then
echo "✗ 错误: 此脚本需要 root 权限运行"
echo " 请使用: sudo $0"
exit 1
fi
}
# 确保备份目录存在
ensure_backup_dir() {
local backup_dir
backup_dir=$(dirname "$BACKUP_FILE")
if [ ! -d "$backup_dir" ]; then
mkdir -p "$backup_dir" 2>/dev/null || {
echo "✗ 无法创建备份目录: $backup_dir"
exit 1
}
fi
}
PARAMS=(
"net.core.default_qdisc"
"net.ipv4.tcp_congestion_control"
"net.core.rmem_max"
"net.core.wmem_max"
"net.ipv4.tcp_rmem"
"net.ipv4.tcp_wmem"
)
get_sys_info() {
MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
MEM_BYTES=$((MEM_KB * 1024))
echo "内存: $((MEM_BYTES/1024/1024)) MB, CPU: $(nproc)"
}
ask_network_info() {
# 读取并验证带宽输入
while true; do
read -p "最大带宽 (Mbps): " BW
if [[ "$BW" =~ ^[0-9]+(\.[0-9]+)?$ ]] && (( $(echo "$BW > 0" | bc -l) )); then
break
else
echo "✗ 无效输入,请输入正数 (例如: 100 或 1000)"
fi
done
# 读取并验证 RTT 输入
while true; do
read -p "平均延迟 RTT (ms): " RTT
if [[ "$RTT" =~ ^[0-9]+(\.[0-9]+)?$ ]] && (( $(echo "$RTT > 0" | bc -l) )); then
break
else
echo "✗ 无效输入,请输入正数 (例如: 10 或 50)"
fi
done
BW_BITS=$((BW * 1000000))
RTT_SEC=$(awk "BEGIN {print $RTT/1000}")
BDP_BITS=$(awk "BEGIN {print $BW_BITS * $RTT_SEC}")
BDP_BYTES=$(awk "BEGIN {print int($BDP_BITS/8)}")
MIN_BUF=$((4*1024*1024))
MAX_BUF_LIMIT=$((MEM_BYTES/8))
if [ $BDP_BYTES -lt $MIN_BUF ]; then
BUF=$MIN_BUF
elif [ $BDP_BYTES -gt $MAX_BUF_LIMIT ]; then
BUF=$MAX_BUF_LIMIT
else
BUF=$BDP_BYTES
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "计算结果:"
echo " 带宽延迟积 (BDP): $(numfmt --to=iec-i --suffix=B $BDP_BYTES)"
echo " 缓冲区上限: $(numfmt --to=iec-i --suffix=B $BUF)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}
check_bbr() {
if ! sysctl net.ipv4.tcp_available_congestion_control 2>/dev/null | grep -qw bbr; then
echo "⚠️ 未检测到 BBR 支持,尝试加载 tcp_bbr 模块..."
if modprobe tcp_bbr 2>/dev/null; then
echo "✓ tcp_bbr 模块加载成功"
# 添加到开机自动加载
if [ ! -f /etc/modules-load.d/tcp_bbr.conf ]; then
echo "tcp_bbr" > /etc/modules-load.d/tcp_bbr.conf
echo "✓ 已设置 BBR 模块开机自动加载"
fi
else
echo "✗ 内核不支持 BBR,无法加载模块"
echo " 请确认内核版本 >= 4.9 或更新内核"
exit 1
fi
else
echo "✓ BBR 支持已启用"
fi
}
apply_tune() {
check_root
ensure_backup_dir
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " 系统信息检测"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
get_sys_info
echo ""
check_bbr
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " 网络参数配置"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
ask_network_info
echo ""
if [ ! -f "$BACKUP_FILE" ]; then
echo "📋 备份当前参数到 $BACKUP_FILE"
for p in "${PARAMS[@]}"; do
echo "$p = $(sysctl -n $p)" >> "$BACKUP_FILE"
done
echo "✓ 备份完成"
else
echo "ℹ️ 检测到已存在备份文件,跳过备份"
fi
echo ""
echo "📝 生成配置文件: $TUNE_FILE"
cat > "$TUNE_FILE" <<EOF
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
net.core.rmem_max = $BUF
net.core.wmem_max = $BUF
net.ipv4.tcp_rmem = 4096 87380 $BUF
net.ipv4.tcp_wmem = 4096 65536 $BUF
EOF
echo "⚙️ 应用配置..."
if sysctl -p "$TUNE_FILE" >/dev/null 2>&1; then
echo "✓ 配置应用成功"
else
echo "✗ 配置应用失败"
exit 1
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " 当前参数"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
for p in "${PARAMS[@]}"; do
printf "%-35s %s\n" "$p" "$(sysctl -n $p)"
done
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}
restore_tune() {
check_root
if [ ! -f "$BACKUP_FILE" ]; then
echo "✗ 未找到备份文件: $BACKUP_FILE"
echo " 无法还原配置"
exit 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " 还原配置"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
[ -f "$TUNE_FILE" ] && rm -f "$TUNE_FILE" && echo "✓ 已删除调优配置文件"
echo "⚙️ 还原系统参数..."
while IFS= read -r line; do
key=$(echo "$line" | awk -F= '{print $1}' | xargs)
value=$(echo "$line" | awk -F= '{print $2}' | xargs)
if [ -n "$key" ] && [ -n "$value" ]; then
if sysctl -w "$key=$value" >/dev/null 2>&1; then
echo "$key"
else
echo "$key (失败)"
fi
fi
done < "$BACKUP_FILE"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " 当前参数"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
for p in "${PARAMS[@]}"; do
printf "%-35s %s\n" "$p" "$(sysctl -n $p)"
done
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
read -p "是否删除备份文件? (y/N): " del_backup
if [[ "$del_backup" =~ ^[Yy]$ ]]; then
rm -f "$BACKUP_FILE"
echo "✓ 已删除备份文件"
else
echo "️ 保留备份文件: $BACKUP_FILE"
fi
}
compare_params() {
if [ ! -f "$BACKUP_FILE" ]; then
echo "✗ 未找到备份文件: $BACKUP_FILE"
echo " 无法对比参数"
exit 1
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
printf "%-35s %-25s %-25s\n" "参数" "优化前" "优化后"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
for p in "${PARAMS[@]}"; do
old=$(grep "^$p" "$BACKUP_FILE" | awk -F= '{print $2}' | xargs)
new=$(sysctl -n $p 2>/dev/null || echo "N/A")
printf "%-35s %-25s %-25s\n" "$p" "$old" "$new"
done
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}
if [ -z "$1" ]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " TCP 调优脚本 v25.10.11"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "请选择操作:"
echo " 1 - 启用 BBR 调优"
echo " 0 - 还原默认配置"
echo " 2 - 对比优化前后参数"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
read -p "请输入选项 (1/0/2): " choice
case "$choice" in
1) apply_tune ;;
0) restore_tune ;;
2) compare_params ;;
*) echo "✗ 无效选项" ; exit 1 ;;
esac
else
case "$1" in
1) apply_tune ;;
0) restore_tune ;;
2) compare_params ;;
*) echo "用法: $0 {1|0|2}
1 = 启用调优
0 = 还原配置
2 = 对比优化前后的参数" ; exit 1 ;;
esac
fi
+346
View File
@@ -0,0 +1,346 @@
#!/bin/sh
# TPROXY 透明代理脚本 for OpenWrt with NFTables
# 功能: IPv4/v6, DNS劫持(可选), IP分流, 本机代理
# 版本: 1.1.2 (2025-06-16)
# 维护: su
# --- [ 用户配置区: 请根据您的环境修改此区域 ] ---
# --- 功能开关 ---
# 是否开启DNS劫持功能. "true":开启, "false":关闭
ENABLE_DNS_HIJACKING="false"
# --- 核心设置 ---
# TPROXY 代理服务监听的 TCP/UDP 端口
PROXY_PORT=7893
# Adguard 或其他 DNS 软件的监听端口
ADGUARDHOME_DNS_PORT=553
# 代理软件的 DNS 监听端口 (仅在开启劫持时有效)
PROXY_DNS_PORT=1053
# --- 系统集成设置 ---
# LAN 接口名称
LAN_IF="br-lan"
# 运行代理软件的用户名 (用于豁免代理自身流量,防止死循环)
PROXY_USER="root"
# 存放 .nft 自定义规则文件的目录
NFT_CUSTOM_PATH="/etc/nikki/nftables"
# 您正在使用的 nftables 表名
NFT_TABLE="nikki"
# --- IP 集合名称 (需与 .nft 文件中定义的名称完全一致) ---
NFT_SET_RESERVED_V4="reserved_ip"
NFT_SET_RESERVED_V6="reserved_ip6"
NFT_SET_BYPASS_V4="china_ip"
NFT_SET_BYPASS_V6="china_ip6"
# --- 高级设置 ---
# 防火墙标记 (fwmark) 和策略路由表 ID
PROXY_MARK=0xff
ROUTE_TABLE=100
# 锁文件路径 (防止重复运行)
LOCK_FILE="/var/run/tproxy.lock"
# --- [ 核心逻辑区: 通常无需修改 ] ---
check_root() {
if [ "$(id -u)" -ne 0 ]; then
printf "错误: 此脚本需要以 root 权限运行。\n" >&2
exit 1
fi
}
# 检查是否已有实例运行
check_lock() {
if [ -f "$LOCK_FILE" ]; then
# 检查服务是否真正在运行(通过防火墙表存在性判断)
if nft list table inet "$NFT_TABLE" >/dev/null 2>&1; then
printf "错误: TPROXY 服务已在运行中\n" >&2
printf "如需重新启动,请使用: %s restart\n" "$0" >&2
printf "如需强制启动,请先停止服务: %s stop\n" "$0" >&2
exit 1
else
# 清理失效的锁文件
printf " -> 清理失效的锁文件...\n"
rm -f "$LOCK_FILE"
fi
fi
}
# 创建锁文件
create_lock() {
# 确保锁文件目录存在
local lock_dir=$(dirname "$LOCK_FILE")
[ ! -d "$lock_dir" ] && mkdir -p "$lock_dir" 2>/dev/null
# 写入启动时间戳而不是PID,因为脚本执行完后进程会结束
date '+%Y-%m-%d %H:%M:%S' > "$LOCK_FILE"
if [ $? -ne 0 ]; then
printf "警告: 无法创建锁文件 %s\n" "$LOCK_FILE" >&2
else
printf " -> 锁文件已创建: %s\n" "$LOCK_FILE"
fi
}
# 清理锁文件
remove_lock() {
if [ -f "$LOCK_FILE" ]; then
rm -f "$LOCK_FILE"
printf " -> 锁文件已清理: %s\n" "$LOCK_FILE"
fi
}
load_user_nft_files() {
if [ ! -d "$NFT_CUSTOM_PATH" ]; then
printf "错误: 找不到目录 %s\n" "$NFT_CUSTOM_PATH" >&2
exit 1
fi
printf " -> 正在从 %s 加载自定义规则...\n" "$NFT_CUSTOM_PATH"
# 检查是否存在 .nft 文件
file_count=$(find "$NFT_CUSTOM_PATH" -name "*.nft" -type f | wc -l)
if [ "$file_count" -eq 0 ]; then
printf "警告: 目录 %s 中没有找到 .nft 文件\n" "$NFT_CUSTOM_PATH" >&2
return 0
fi
for file in "$NFT_CUSTOM_PATH"/*.nft; do
if [ -f "$file" ]; then
printf " - 加载 %s\n" "$(basename "$file")"
if ! nft -f "$file"; then
printf "错误: 加载 %s 时发生错误!\n" "$file" >&2
exit 1
fi
fi
done
}
do_start() {
printf "▶️ 启动透明代理服务...\n"
check_lock
# 先清理可能存在的残留规则,但不删除锁文件
printf " -> 清理可能存在的残留规则...\n"
ip -4 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true
ip -6 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true
ip -4 route flush table $ROUTE_TABLE 2>/dev/null
ip -6 route flush table $ROUTE_TABLE 2>/dev/null
nft delete table inet $NFT_TABLE 2>/dev/null || true
# 创建锁文件
create_lock
# 设置异常退出时自动清理锁文件
trap 'remove_lock; exit' INT TERM
# 1. 设置策略路由
printf " -> 正在设置策略路由...\n"
ip -4 rule add fwmark $PROXY_MARK lookup $ROUTE_TABLE
ip -6 rule add fwmark $PROXY_MARK lookup $ROUTE_TABLE
ip -4 route add local default dev lo table $ROUTE_TABLE
ip -6 route add local default dev lo table $ROUTE_TABLE
# 2. 加载并配置 nftables
printf " -> 正在加载并配置 nftables...\n"
nft add table inet $NFT_TABLE
if [ $? -ne 0 ]; then
printf "错误: 创建 nftables 表 '%s' 失败。请检查是否已有同名表或nftables服务是否正常。\n" "$NFT_TABLE" >&2
exit 1
fi
# 加载 IP Set 文件
load_user_nft_files
nft add chain inet $NFT_TABLE mangle_prerouting { type filter hook prerouting priority -150\; }; nft flush chain inet $NFT_TABLE mangle_prerouting
nft add chain inet $NFT_TABLE mangle_output { type route hook output priority -150\; }; nft flush chain inet $NFT_TABLE mangle_output
for chain in tproxy_lan_v4 tproxy_lan_v6 tproxy_loc_v4 tproxy_loc_v6; do
nft add chain inet $NFT_TABLE "$chain"; nft flush chain inet $NFT_TABLE "$chain"
done
# 3. 应用规则
printf " -> 正在应用核心防火墙规则...\n"
# 规则 A: DNS 劫持 (可选)
if [ "$ENABLE_DNS_HIJACKING" = "true" ]; then
printf " - DNS 劫持已开启。\n"
nft add chain inet $NFT_TABLE dns_redirect { type nat hook prerouting priority -100\; }; nft flush chain inet $NFT_TABLE dns_redirect
nft add rule inet $NFT_TABLE dns_redirect iifname $LAN_IF meta l4proto {tcp, udp} th dport 53 redirect to :$PROXY_DNS_PORT
else
printf " - DNS 劫持已关闭。\n"
fi
# 规则 B: 处理本机发出流量 (在 OUTPUT 链中仅作标记)
nft add rule inet $NFT_TABLE mangle_output meta nfproto ipv4 jump tproxy_loc_v4
nft add rule inet $NFT_TABLE mangle_output meta nfproto ipv6 jump tproxy_loc_v6
nft add rule inet $NFT_TABLE tproxy_loc_v4 skuid $PROXY_USER return # 关键: 豁免代理程序自身,防止死循环
nft add rule inet $NFT_TABLE tproxy_loc_v4 ip daddr @$NFT_SET_RESERVED_V4 return
nft add rule inet $NFT_TABLE tproxy_loc_v4 ip daddr @$NFT_SET_BYPASS_V4 return
# 不处理 DNS 流量
nft add rule inet $NFT_TABLE tproxy_loc_v4 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return
nft add rule inet $NFT_TABLE tproxy_loc_v4 meta l4proto {tcp, udp} meta mark set $PROXY_MARK
nft add rule inet $NFT_TABLE tproxy_loc_v6 skuid $PROXY_USER return
nft add rule inet $NFT_TABLE tproxy_loc_v6 ip6 daddr @$NFT_SET_RESERVED_V6 return
nft add rule inet $NFT_TABLE tproxy_loc_v6 ip6 daddr @$NFT_SET_BYPASS_V6 return
# 不处理 DNS 流量
nft add rule inet $NFT_TABLE tproxy_loc_v6 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return
nft add rule inet $NFT_TABLE tproxy_loc_v6 meta l4proto {tcp, udp} meta mark set $PROXY_MARK
# 规则 C: 处理局域网及本机回环流量 (在 PREROUTING 链中执行 TPROXY)
nft add rule inet $NFT_TABLE mangle_prerouting iifname $LAN_IF meta nfproto ipv4 meta l4proto {tcp, udp} jump tproxy_lan_v4
nft add rule inet $NFT_TABLE mangle_prerouting iifname $LAN_IF meta nfproto ipv6 meta l4proto {tcp, udp} jump tproxy_lan_v6
nft add rule inet $NFT_TABLE mangle_prerouting iifname "lo" meta nfproto ipv4 meta l4proto {tcp, udp} meta mark $PROXY_MARK jump tproxy_lan_v4
nft add rule inet $NFT_TABLE mangle_prerouting iifname "lo" meta nfproto ipv6 meta l4proto {tcp, udp} meta mark $PROXY_MARK jump tproxy_lan_v6
nft add rule inet $NFT_TABLE tproxy_lan_v4 ip daddr @$NFT_SET_RESERVED_V4 return
nft add rule inet $NFT_TABLE tproxy_lan_v4 ip daddr @$NFT_SET_BYPASS_V4 return
# 不处理 DNS 流量
nft add rule inet $NFT_TABLE tproxy_lan_v4 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return
nft add rule inet $NFT_TABLE tproxy_lan_v4 meta l4proto {tcp, udp} meta mark set $PROXY_MARK tproxy to :$PROXY_PORT
nft add rule inet $NFT_TABLE tproxy_lan_v6 ip6 daddr @$NFT_SET_RESERVED_V6 return
nft add rule inet $NFT_TABLE tproxy_lan_v6 ip6 daddr @$NFT_SET_BYPASS_V6 return
# 不处理 DNS 流量
nft add rule inet $NFT_TABLE tproxy_lan_v6 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return
nft add rule inet $NFT_TABLE tproxy_lan_v6 meta l4proto {tcp, udp} meta mark set $PROXY_MARK tproxy to :$PROXY_PORT
printf "✅ 透明代理服务已成功启动。\n"
# 清除陷阱,正常启动时保持锁文件
trap - INT TERM
}
do_stop() {
printf "⏹️ 停止透明代理服务...\n"
# 清理策略路由
ip -4 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true
ip -6 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true
# 清理自定义路由表内容
ip -4 route flush table $ROUTE_TABLE 2>/dev/null
ip -6 route flush table $ROUTE_TABLE 2>/dev/null
# 删除 nft 路由表
printf " -> 正在清理防火墙表 '%s'...\n" "$NFT_TABLE"
nft delete table inet $NFT_TABLE 2>/dev/null || true
# 清理锁文件
remove_lock
printf "🛑 透明代理服务已完全停止。\n"
}
do_status() {
ICON_OK="✅"
ICON_FAIL="❌"
ICON_INFO="️"
ICON_OFF="⚪️"
printf "--- TPROXY 服务状态检查 ---\n"
# 检查锁文件状态
if [ -f "$LOCK_FILE" ]; then
local start_time=$(cat "$LOCK_FILE" 2>/dev/null)
if [ -n "$start_time" ]; then
printf "锁文件状态: %s 存在 (启动时间: %s)\n" "$ICON_OK" "$start_time"
else
printf "锁文件状态: %s 存在但无效\n" "$ICON_FAIL"
fi
else
printf "锁文件状态: %s 不存在\n" "$ICON_OFF"
fi
if ! nft list table inet "$NFT_TABLE" >/dev/null 2>&1; then
printf "总状态: %s 未激活 (找不到防火墙表 '%s')\n" "$ICON_OFF" "$NFT_TABLE"
exit 0
fi
if ip rule show | grep -q "fwmark $PROXY_MARK lookup $ROUTE_TABLE"; then
printf "总状态: %s 激活\n" "$ICON_OK"
else
printf "总状态: %s 部分激活 (防火墙规则存在,但策略路由缺失)\n" "$ICON_FAIL"
fi
printf "\n[+] IP 路由与规则:\n"
if ip rule show | grep -q "fwmark $PROXY_MARK"; then
printf " %s 策略路由:\n" "$ICON_INFO"
ip rule show | grep "fwmark $PROXY_MARK" | sed 's/^/ -> /'
else
printf " %s 策略路由: %s 缺失!\n" "$ICON_FAIL" "$ICON_FAIL"
fi
printf " %s 自定义路由表 (ID: %s):\n" "$ICON_INFO" "$ROUTE_TABLE"
ip route show table "$ROUTE_TABLE" | sed 's/^/ -> /'
printf "\n[+] NFTables 规则状态 (主表: %s)\n" "$NFT_TABLE"
if [ "$ENABLE_DNS_HIJACKING" = "true" ]; then
if nft list chain inet "$NFT_TABLE" "dns_redirect" >/dev/null 2>&1; then
printf " %s DNS 劫持: 已激活 (转发至端口 %s)\n" "$ICON_OK" "$PROXY_DNS_PORT"
else
printf " %s DNS 劫持: 激活失败\n" "$ICON_FAIL"
fi
else
printf " %s DNS 劫持: 已禁用\n" "$ICON_OFF"
fi
if nft list chain inet "$NFT_TABLE" "tproxy_lan_v4" >/dev/null 2>&1; then
printf " %s TPROXY 代理: 已激活 (监听端口 %s)\n" "$ICON_OK" "$PROXY_PORT"
else
printf " %s TPROXY 代理: 激活失败\n" "$ICON_FAIL"
fi
printf " %s IP 列表加载状态:\n" "$ICON_INFO"
if nft list set inet "$NFT_TABLE" "$NFT_SET_RESERVED_V4" >/dev/null 2>&1; then
printf " - %s: %s\n" "$NFT_SET_RESERVED_V4" "$ICON_OK 本地 IPv4 已直连"
else
printf " - %s: %s\n" "$NFT_SET_RESERVED_V4" "$ICON_FAIL 未加载"
fi
if nft list set inet "$NFT_TABLE" "$NFT_SET_RESERVED_V6" >/dev/null 2>&1; then
printf " - %s: %s\n" "$NFT_SET_RESERVED_V6" "$ICON_OK 本地 IPv6 已直连"
else
printf " - %s: %s\n" "$NFT_SET_RESERVED_V6" "$ICON_FAIL 未加载"
fi
if nft list set inet "$NFT_TABLE" "$NFT_SET_BYPASS_V4" >/dev/null 2>&1; then
printf " - %s: %s\n" "$NFT_SET_BYPASS_V4" "$ICON_OK 国内 IPv4 已直连"
else
printf " - %s: %s\n" "$NFT_SET_BYPASS_V4" "$ICON_FAIL 未加载"
fi
if nft list set inet "$NFT_TABLE" "$NFT_SET_BYPASS_V6" >/dev/null 2>&1; then
printf " - %s: %s\n" "$NFT_SET_BYPASS_V6" "$ICON_OK 国内 IPv6 已直连"
else
printf " - %s: %s\n" "$NFT_SET_BYPASS_V6" "$ICON_FAIL 未加载"
fi
printf -- "--------------------------------\n"
}
# --- 脚本主入口 ---
check_root
case "$1" in
start)
do_start
;;
stop)
do_stop
;;
restart)
printf "🔄 重启透明代理服务...\n"
do_stop
sleep 1
do_start
;;
status)
do_status
;;
*)
printf "用法: %s {start|stop|restart|status}\n" "$0"
printf " start - 启动透明代理服务\n"
printf " stop - 停止透明代理服务\n"
printf " restart - 重启透明代理服务\n"
printf " status - 查看服务状态\n"
exit 1
;;
esac
exit 0