这篇简单记一下自己用 Phantun 给 Hy2 做 FakeTCP 伪装的过程,顺便放一个「一键脚本」,后面换机器的时候直接跑脚本即可,不必每次都得翻文档查命令。
场景大概是:
- 落地机:Hy2 服务端 + Phantun 服务端
- 中转机:Phantun 客户端
- 本地:Hy2 客户端连中转机的 UDP 端口
一、部署 Phantun 二进制
这里以 x86_64 + glibc(Debian/Ubuntu) 为例,其他架构/Alpine 自行换对应的 zip 文件名。
1. 下载到 /usr/local/bin
cd /usr/local/bin2. 从 GitHub Releases 下载
当前版本示例:v0.8.1,x86\_64 glibc 对应文件名:
phantun_x86_64-unknown-linux-gnu.zip下载:
wget https://github.com/dndx/phantun/releases/download/v0.8.1/phantun_x86_64-unknown-linux-gnu.zip3. 解压 + 加执行权限
apt install unzip -y # 如果系统没有 unzip
unzip phantun_x86_64-unknown-linux-gnu.zip
chmod +x phantun_server phantun_client二、手动伪装 Hy2 的 UDP 流量
下面先用最基础的方式手动跑一遍(不考虑持久化),方便理解整个链路。
假设:
落地机已经跑好了 Hy2 服务端:
- 地址:
127.0.0.1 - 端口:
51431
- 地址:
- 中转机稍后会通过 FakeTCP 连到这个端口。
2.1 服务端(server)配置
1)启动 phantun\_server
在落地机上执行:
RUST_LOG=info phantun_server --local 51431 --remote 127.0.0.1:51431 &含义:
--local 51431:对外暴露的 FakeTCP 端口--remote 127.0.0.1:51431:实际丢给 Hy2 的 UDP 端口
2)开启 IP 转发
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
sysctl -p /etc/sysctl.conf3)确认外网网卡
ip route
# 看这一行:
# default via x.x.x.x dev ens17假设外网网卡是 ens17。
4)DNAT 伪装规则
把来自外网的 TCP 51431 转到 phantun 的 TUN 对端 192.168.201.2:
iptables -t nat -A PREROUTING -p tcp -i ens17 --dport 51431 \
-j DNAT --to-destination 192.168.201.25)放行 Hy2 的 UDP 端口
iptables -A INPUT -p udp --dport 51431 -j ACCEPT
netfilter-persistent save # Debian/Ubuntu 上持久化规则2.2 客户端(client,中转机)配置
假设中转机是 Alpine,外网网卡为 eth0。
1)开启 IP 转发
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p /etc/sysctl.conf2)给 phantun 的 TUN 做 SNAT
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.200.2 -j MASQUERADE192.168.200.2 是 phantun 客户端默认的 TUN IP。3)启动 phantun\_client
RUST_LOG=info \
/usr/local/bin/phantun_client \
--local 0.0.0.0:51431 \
--remote 落地机IP:51431到这一步:
- 本地 Hy2 客户端连:中转机 IP:51431(UDP)
- 中转机 phantun\_client → 用 FakeTCP 连落地机的 51431
- 落地机 phantun\_server → 再把流量转给本机 Hy2
缺点也很明显:没有持久化,重启就没了,命令中断就挂。
三、一键菜单脚本(支持多客户端实例)
为了省事,写了一个「phanyun 一键配置脚本」(名字随便,脚本里创建的命令是 phantun):
- 支持 Debian / Ubuntu / Alpine
- 自动安装 phantun
- 自动检测外网网卡
- 自动开启 IP 转发、检测
/dev/net/tun 服务端菜单:
- 一键配置/修改服务端
- 添加伪装端口
- 删除伪装端口(选择 1,2,3…)
客户端菜单:
- 配置/修改主通道
- 添加新的转发实例(本地 UDP ↔ 远程 FakeTCP)
- 删除转发实例(同样是选择 1,2,3…)
其它:
- 查看当前配置和 iptables 规则
- 重启所有 phantun 服务
- 一键卸载 phantun + 规则 + 服务
- 自动创建
/usr/local/bin/phantun快捷命令,以后直接输入phantun即可打开菜单
下面是完整脚本,可以直接保存使用。
3.1 脚本代码
#!/bin/sh
set -e
PHANTUN_VERSION="v0.8.1"
CONFIG_FILE="/etc/phantun/phantun.conf"
RED="$(printf '\033[31m')"
GREEN="$(printf '\033[32m')"
YELLOW="$(printf '\033[33m')"
BLUE="$(printf '\033[34m')"
CYAN="$(printf '\033[36m')"
RESET="$(printf '\033[0m')"
log_info() { printf "%s[INFO]%s %s\n" "$BLUE" "$RESET" "$*"; }
log_ok() { printf "%s[OK]%s %s\n" "$GREEN" "$RESET" "$*"; }
log_warn() { printf "%s[WARN]%s %s\n" "$YELLOW" "$RESET" "$*"; }
log_error() { printf "%s[ERR]%s %s\n" "$RED" "$RESET" "$*" >&2; }
ensure_root() {
if [ "$(id -u)" != "0" ]; then
log_error "请用 root 运行此脚本"
exit 1
fi
}
detect_os() {
if command -v apt >/dev/null 2>&1 && command -v systemctl >/dev/null 2>&1; then
OS_TYPE="debian"
elif command -v apk >/dev/null 2>&1 && command -v rc-update >/dev/null 2>&1; then
OS_TYPE="alpine"
else
OS_TYPE="unknown"
fi
}
auto_detect_iface() {
IFACE_DETECTED="$(ip route 2>/dev/null | awk '/default/ {print $5; exit}')"
if [ -n "$IFACE_DETECTED" ]; then
echo "$IFACE_DETECTED"
else
log_error "无法自动检测出口网卡"
exit 1
fi
}
ensure_ip_forward() {
CURRENT="$(sysctl -n net.ipv4.ip_forward 2>/dev/null || echo 0)"
if [ "$CURRENT" != "1" ]; then
log_info "开启 IPv4 转发"
if ! grep -q 'net.ipv4.ip_forward=1' /etc/sysctl.conf 2>/dev/null; then
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
fi
sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1 || true
sysctl -p /etc/sysctl.conf >/dev/null 2>&1 || true
fi
log_ok "IPv4 转发已开启"
}
ensure_tun_device() {
log_info "检查 /dev/net/tun"
if [ ! -e /dev/net/tun ]; then
modprobe tun 2>/dev/null || true
if [ ! -e /dev/net/tun ]; then
mkdir -p /dev/net 2>/dev/null || true
mknod /dev/net/tun c 10 200 2>/dev/null || true
chmod 600 /dev/net/tun 2>/dev/null || true
fi
fi
if [ ! -e /dev/net/tun ]; then
log_error "/dev/net/tun 不存在,当前环境不支持 TUN"
exit 1
fi
log_ok "/dev/net/tun 正常"
}
detect_arch_suffix() {
ARCH="$(uname -m)"
FLAVOR="$1"
case "$ARCH" in
x86_64)
if [ "$FLAVOR" = "gnu" ]; then
echo "x86_64-unknown-linux-gnu"
else
echo "x86_64-unknown-linux-musl"
fi
;;
aarch64|arm64)
if [ "$FLAVOR" = "gnu" ]; then
echo "aarch64-unknown-linux-gnu"
else
echo "aarch64-unknown-linux-musl"
fi
;;
*)
log_error "未支持的架构: $ARCH"
exit 1
;;
esac
}
install_phantun_debian() {
BIN_DIR="/usr/local/bin"
ARCH_SUFFIX="$(detect_arch_suffix gnu)"
ZIP_PATH="${BIN_DIR}/phantun_${ARCH_SUFFIX}.zip"
URL="https://github.com/dndx/phantun/releases/download/${PHANTUN_VERSION}/phantun_${ARCH_SUFFIX}.zip"
log_info "安装依赖 (apt)"
export DEBIAN_FRONTEND=noninteractive
apt update -y >/dev/null
apt install -y wget unzip ca-certificates iptables iptables-persistent iproute2 >/dev/null
update-ca-certificates >/dev/null 2>&1 || true
if command -v phantun_server >/dev/null 2>&1; then
log_ok "检测到 phantun 已安装"
return
fi
log_info "下载 phantun: ${URL}"
mkdir -p "$BIN_DIR"
cd "$BIN_DIR"
wget -O "$ZIP_PATH" "$URL"
unzip -o "$ZIP_PATH"
chmod +x phantun_server phantun_client
rm -f "$ZIP_PATH"
log_ok "phantun 已安装到 ${BIN_DIR}"
}
install_phantun_alpine() {
BIN_DIR="/usr/local/bin"
ARCH_SUFFIX="$(detect_arch_suffix musl)"
ZIP_PATH="${BIN_DIR}/phantun_${ARCH_SUFFIX}.zip"
URL="https://github.com/dndx/phantun/releases/download/${PHANTUN_VERSION}/phantun_${ARCH_SUFFIX}.zip"
log_info "安装依赖 (apk)"
apk update >/dev/null
apk add --no-cache wget unzip ca-certificates iptables ip6tables iptables-openrc openrc iproute2 >/dev/null
update-ca-certificates >/dev/null 2>&1 || true
if command -v phantun_client >/dev/null 2>&1; then
log_ok "检测到 phantun 已安装"
return
fi
log_info "下载 phantun: ${URL}"
mkdir -p "$BIN_DIR"
cd "$BIN_DIR"
wget -O "$ZIP_PATH" "$URL"
unzip -o "$ZIP_PATH"
chmod +x phantun_server phantun_client
rm -f "$ZIP_PATH"
log_ok "phantun 已安装到 ${BIN_DIR}"
}
save_server_config() {
mkdir -p /etc/phantun
cat >"$CONFIG_FILE" <<EOF
ROLE=server
LISTEN_PORT=$LISTEN_PORT
BACKEND_ADDR=$BACKEND_ADDR
BACKEND_PORT=$BACKEND_PORT
WAN_IFACE=$WAN_IFACE
EOF
}
save_client_config() {
mkdir -p /etc/phantun
cat >"$CONFIG_FILE" <<EOF
ROLE=client
REMOTE_ADDR=$REMOTE_ADDR
REMOTE_PORT=$REMOTE_PORT
LOCAL_PORT=$LOCAL_PORT
WAN_IFACE=$WAN_IFACE
EOF
}
load_config() {
if [ ! -f "$CONFIG_FILE" ]; then
return 1
fi
ROLE=""
LISTEN_PORT=""
BACKEND_ADDR=""
BACKEND_PORT=""
REMOTE_ADDR=""
REMOTE_PORT=""
LOCAL_PORT=""
WAN_IFACE=""
while IFS='=' read -r key val; do
case "$key" in
ROLE|LISTEN_PORT|BACKEND_ADDR|BACKEND_PORT|REMOTE_ADDR|REMOTE_PORT|LOCAL_PORT|WAN_IFACE)
eval "$key=\"\$val\""
;;
esac
done < "$CONFIG_FILE"
if [ -n "$ROLE" ]; then
return 0
else
return 1
fi
}
ensure_shortcut() {
SCRIPT_PATH="$0"
if command -v readlink >/dev/null 2>&1; then
SCRIPT_PATH="$(readlink -f "$0" 2>/dev/null || echo "$0")"
fi
TARGET="/usr/local/bin/phantun"
if [ ! -e "$TARGET" ]; then
ln -s "$SCRIPT_PATH" "$TARGET" 2>/dev/null || true
chmod +x "$SCRIPT_PATH" 2>/dev/null || true
if command -v phantun >/dev/null 2>&1; then
log_ok "已创建快捷命令: phantun"
fi
fi
}
setup_server_main() {
printf "%s\n" "${CYAN}当前模式: 服务端${RESET}"
while :; do
printf "请输入 FakeTCP 监听端口: "
read LISTEN_PORT
[ -n "$LISTEN_PORT" ] && break
log_warn "端口不能为空"
done
while :; do
printf "请输入 Hy2 后端 IP (回车默认 127.0.0.1): "
read BACKEND_ADDR
[ -z "$BACKEND_ADDR" ] && BACKEND_ADDR="127.0.0.1"
[ -n "$BACKEND_ADDR" ] && break
done
while :; do
printf "请输入 Hy2 后端端口: "
read BACKEND_PORT
[ -n "$BACKEND_PORT" ] && break
log_warn "端口不能为空"
done
WAN_IFACE="$(auto_detect_iface)"
log_info "FakeTCP 监听端口: ${GREEN}${LISTEN_PORT}${RESET}"
log_info "Hy2 后端: ${GREEN}${BACKEND_ADDR}:${BACKEND_PORT}${RESET}"
log_info "出口网卡: ${GREEN}${WAN_IFACE}${RESET}"
if [ "$OS_TYPE" = "debian" ]; then
install_phantun_debian
elif [ "$OS_TYPE" = "alpine" ]; then
install_phantun_alpine
else
log_error "不支持的系统类型"
exit 1
fi
ensure_tun_device
ensure_ip_forward
if [ "$OS_TYPE" = "debian" ]; then
SERVICE_PATH="/etc/systemd/system/phantun-server.service"
log_info "写入 systemd 服务: ${SERVICE_PATH}"
cat >"$SERVICE_PATH" <<EOF
[Unit]
Description=Phantun Server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment=RUST_LOG=info
ExecStart=/usr/local/bin/phantun_server --local ${LISTEN_PORT} --remote ${BACKEND_ADDR}:${BACKEND_PORT}
Restart=always
RestartSec=3
User=root
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now phantun-server
log_ok "phantun-server 已启动"
else
SERVICE_PATH="/etc/init.d/phantun-server"
log_info "写入 OpenRC 服务: ${SERVICE_PATH}"
cat >"$SERVICE_PATH" <<EOF
#!/sbin/openrc-run
description="Phantun server"
command="/usr/local/bin/phantun_server"
command_args="--local ${LISTEN_PORT} --remote ${BACKEND_ADDR}:${BACKEND_PORT}"
command_background="yes"
pidfile="/run/\${RC_SVCNAME}.pid"
depend() { need net; }
EOF
chmod +x "$SERVICE_PATH"
rc-update add phantun-server default >/dev/null 2>&1 || true
rc-service phantun-server restart >/dev/null 2>&1 || rc-service phantun-server start >/dev/null 2>&1 || true
log_ok "phantun-server 已启动"
fi
log_info "添加服务端 iptables 规则"
iptables -t nat -A PREROUTING -p tcp -i "$WAN_IFACE" --dport "$LISTEN_PORT" -j DNAT --to-destination 192.168.201.2 2>/dev/null || true
iptables -A FORWARD -i "$WAN_IFACE" -o tun0 -j ACCEPT 2>/dev/null || true
iptables -A FORWARD -i tun0 -o "$WAN_IFACE" -j ACCEPT 2>/dev/null || true
iptables -A INPUT -p udp --dport "$BACKEND_PORT" -j ACCEPT 2>/dev/null || true
if [ "$OS_TYPE" = "debian" ]; then
netfilter-persistent save >/dev/null 2>&1 || true
else
if command -v rc-update >/dev/null 2>&1 && [ -x /etc/init.d/iptables ]; then
rc-update add iptables default >/dev/null 2>&1 || true
/etc/init.d/iptables save >/dev/null 2>&1 || true
fi
fi
save_server_config
log_ok "服务端配置已保存到 ${CONFIG_FILE}"
}
setup_client_main() {
printf "%s\n" "${CYAN}当前模式: 客户端主通道${RESET}"
while :; do
printf "请输入远程服务器 IP: "
read REMOTE_ADDR
[ -n "$REMOTE_ADDR" ] && break
log_warn "IP 不能为空"
done
while :; do
printf "请输入远程 FakeTCP 端口: "
read REMOTE_PORT
[ -n "$REMOTE_PORT" ] && break
log_warn "端口不能为空"
done
while :; do
printf "请输入本地 UDP 端口 (Hy2 客户端连接此端口): "
read LOCAL_PORT
[ -n "$LOCAL_PORT" ] && break
log_warn "端口不能为空"
done
WAN_IFACE="$(auto_detect_iface)"
log_info "远程 FakeTCP: ${GREEN}${REMOTE_ADDR}:${REMOTE_PORT}${RESET}"
log_info "本地 UDP: ${GREEN}0.0.0.0:${LOCAL_PORT}${RESET}"
log_info "出口网卡: ${GREEN}${WAN_IFACE}${RESET}"
if [ "$OS_TYPE" = "debian" ]; then
install_phantun_debian
elif [ "$OS_TYPE" = "alpine" ]; then
install_phantun_alpine
else
log_error "不支持的系统类型"
exit 1
fi
ensure_tun_device
ensure_ip_forward
if [ "$OS_TYPE" = "debian" ]; then
SERVICE_PATH="/etc/systemd/system/phantun-client.service"
log_info "写入 systemd 客户端主服务: ${SERVICE_PATH}"
cat >"$SERVICE_PATH" <<EOF
[Unit]
Description=Phantun Client Main
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment=RUST_LOG=info
ExecStart=/usr/local/bin/phantun_client --local 0.0.0.0:${LOCAL_PORT} --remote ${REMOTE_ADDR}:${REMOTE_PORT}
Restart=always
RestartSec=3
User=root
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now phantun-client
log_ok "phantun-client 主通道已启动"
else
SERVICE_PATH="/etc/init.d/phantun-client"
log_info "写入 OpenRC 客户端主服务: ${SERVICE_PATH}"
cat >"$SERVICE_PATH" <<EOF
#!/sbin/openrc-run
description="Phantun client main"
command="/usr/local/bin/phantun_client"
command_args="--local 0.0.0.0:${LOCAL_PORT} --remote ${REMOTE_ADDR}:${REMOTE_PORT}"
command_background="yes"
pidfile="/run/\${RC_SVCNAME}.pid"
depend() { need net; }
EOF
chmod +x "$SERVICE_PATH"
rc-update add phantun-client default >/dev/null 2>&1 || true
rc-service phantun-client restart >/dev/null 2>&1 || rc-service phantun-client start >/dev/null 2>&1 || true
log_ok "phantun-client 主通道已启动"
fi
log_info "检查/添加 MASQUERADE 规则"
if ! iptables -t nat -S POSTROUTING 2>/dev/null | grep -q "192.168.200.2"; then
iptables -t nat -A POSTROUTING -o "$WAN_IFACE" -s 192.168.200.2 -j MASQUERADE 2>/dev/null || true
fi
if [ "$OS_TYPE" = "debian" ]; then
netfilter-persistent save >/dev/null 2>&1 || true
else
if command -v rc-update >/dev/null 2>&1 && [ -x /etc/init.d/iptables ]; then
rc-update add iptables default >/dev/null 2>&1 || true
/etc/init.d/iptables save >/dev/null 2>&1 || true
fi
fi
save_client_config
log_ok "客户端主通道配置已保存到 ${CONFIG_FILE}"
}
add_client_instance() {
printf "%s\n" "${CYAN}客户端: 添加新的转发实例${RESET}"
while :; do
printf "请输入远程服务器 IP: "
read REMOTE_ADDR
[ -n "$REMOTE_ADDR" ] && break
log_warn "IP 不能为空"
done
while :; do
printf "请输入远程 FakeTCP 端口: "
read REMOTE_PORT
[ -n "$REMOTE_PORT" ] && break
log_warn "端口不能为空"
done
while :; do
printf "请输入本地 UDP 端口 (该实例监听此端口): "
read LOCAL_PORT
[ -n "$LOCAL_PORT" ] && break
log_warn "端口不能为空"
done
ensure_tun_device
ensure_ip_forward
if [ "$OS_TYPE" = "debian" ]; then
SERVICE_NAME="phantun-client-${LOCAL_PORT}.service"
SERVICE_PATH="/etc/systemd/system/${SERVICE_NAME}"
log_info "写入 systemd 客户端实例: ${SERVICE_PATH}"
cat >"$SERVICE_PATH" <<EOF
[Unit]
Description=Phantun Client ${LOCAL_PORT}
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment=RUST_LOG=info
ExecStart=/usr/local/bin/phantun_client --local 0.0.0.0:${LOCAL_PORT} --remote ${REMOTE_ADDR}:${REMOTE_PORT}
Restart=always
RestartSec=3
User=root
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now "phantun-client-${LOCAL_PORT}.service"
log_ok "客户端实例 phantun-client-${LOCAL_PORT} 已启动"
else
SERVICE_NAME="phantun-client-${LOCAL_PORT}"
SERVICE_PATH="/etc/init.d/${SERVICE_NAME}"
log_info "写入 OpenRC 客户端实例: ${SERVICE_PATH}"
cat >"$SERVICE_PATH" <<EOF
#!/sbin/openrc-run
description="Phantun client ${LOCAL_PORT}"
command="/usr/local/bin/phantun_client"
command_args="--local 0.0.0.0:${LOCAL_PORT} --remote ${REMOTE_ADDR}:${REMOTE_PORT}"
command_background="yes"
pidfile="/run/\${RC_SVCNAME}.pid"
depend() { need net; }
EOF
chmod +x "$SERVICE_PATH"
rc-update add "$SERVICE_NAME" default >/dev/null 2>&1 || true
rc-service "$SERVICE_NAME" restart >/dev/null 2>&1 || rc-service "$SERVICE_NAME" start >/dev/null 2>&1 || true
log_ok "客户端实例 ${SERVICE_NAME} 已启动"
fi
WAN_IFACE="$(auto_detect_iface)"
if ! iptables -t nat -S POSTROUTING 2>/dev/null | grep -q "192.168.200.2"; then
log_info "添加 MASQUERADE 规则"
iptables -t nat -A POSTROUTING -o "$WAN_IFACE" -s 192.168.200.2 -j MASQUERADE 2>/dev/null || true
fi
if [ "$OS_TYPE" = "debian" ]; then
netfilter-persistent save >/dev/null 2>&1 || true
else
if command -v rc-update >/dev/null 2>&1 && [ -x /etc/init.d/iptables ]; then
rc-update add iptables default >/dev/null 2>&1 || true
/etc/init.d/iptables save >/dev/null 2>&1 || true
fi
fi
log_info "该客户端实例本地监听: 0.0.0.0:${LOCAL_PORT}"
}
delete_client_instances() {
printf "%s\n" "${CYAN}客户端: 删除转发实例${RESET}"
TMP="/tmp/phantun_cli_inst_$$"
: >"$TMP"
INDEX=0
if [ "$OS_TYPE" = "debian" ]; then
for unit in /etc/systemd/system/phantun-client-*.service; do
[ -f "$unit" ] || continue
INDEX=$((INDEX+1))
echo "$unit" >>"$TMP"
LINE="$(grep -E '^ExecStart=' "$unit" 2>/dev/null | head -n1 || true)"
LPORT="$(echo "$LINE" | sed -n 's/.*--local 0.0.0.0:\([0-9]*\).*/\1/p')"
RADDR="$(echo "$LINE" | sed -n 's/.*--remote \([^ ]*\).*/\1/p')"
if [ -n "$LPORT" ] && [ -n "$RADDR" ]; then
printf " %s) 本地 UDP: %s -> 远程: %s (%s)\n" "$INDEX" "$LPORT" "$RADDR" "$(basename "$unit")"
else
printf " %s) %s\n" "$INDEX" "$(basename "$unit")"
fi
done
else
for script in /etc/init.d/phantun-client-*; do
[ -f "$script" ] || continue
BASE="$(basename "$script")"
[ "$BASE" = "phantun-client" ] && continue
INDEX=$((INDEX+1))
echo "$script" >>"$TMP"
LINE="$(grep -E '^command_args=' "$script" 2>/dev/null | head -n1 || true)"
LPORT="$(echo "$LINE" | sed -n 's/.*--local 0.0.0.0:\([0-9]*\).*/\1/p')"
RADDR="$(echo "$LINE" | sed -n 's/.*--remote \([^ ]*\).*/\1/p')"
if [ -n "$LPORT" ] && [ -n "$RADDR" ]; then
printf " %s) 本地 UDP: %s -> 远程: %s (%s)\n" "$INDEX" "$LPORT" "$RADDR" "$BASE"
else
printf " %s) %s\n" "$INDEX" "$BASE"
fi
done
fi
if [ "$INDEX" -eq 0 ]; then
log_warn "未检测到任何额外客户端实例"
rm -f "$TMP"
return
fi
printf "请输入要删除的序号(支持多个,用逗号分隔,如 1,2,3),回车取消: "
read SEL
if [ -z "$SEL" ]; then
log_warn "未选择任何实例"
rm -f "$TMP"
return
fi
OLDIFS="$IFS"
IFS=','
for idx in $SEL; do
idx_trim="$(echo "$idx" | tr -d ' ')"
[ -n "$idx_trim" ] || continue
PATH_LINE="$(sed -n "${idx_trim}p" "$TMP" 2>/dev/null || true)"
if [ -z "$PATH_LINE" ]; then
log_warn "序号 ${idx_trim} 无效"
continue
fi
if [ "$OS_TYPE" = "debian" ]; then
SVC="$(basename "$PATH_LINE")"
systemctl disable --now "$SVC" >/dev/null 2>&1 || true
rm -f "$PATH_LINE"
systemctl daemon-reload >/dev/null 2>&1 || true
log_ok "已删除客户端实例: ${SVC}"
else
SVC="$(basename "$PATH_LINE")"
rc-service "$SVC" stop >/dev/null 2>&1 || true
rc-update del "$SVC" >/dev/null 2>&1 || true
rm -f "$PATH_LINE"
log_ok "已删除客户端实例: ${SVC}"
fi
done
IFS="$OLDIFS"
rm -f "$TMP"
}
add_extra_port() {
printf "%s\n" "${CYAN}服务端: 添加伪装端口${RESET}"
if ! load_config || [ "$ROLE" != "server" ]; then
log_warn "当前未配置为服务端,无法添加伪装端口"
return
fi
if [ -z "$WAN_IFACE" ]; then
WAN_IFACE="$(auto_detect_iface)"
fi
log_info "出口网卡: ${GREEN}${WAN_IFACE}${RESET}"
while :; do
printf "请输入要新增伪装的 TCP 端口: "
read EXTRA_PORT
[ -n "$EXTRA_PORT" ] && break
log_warn "端口不能为空"
done
iptables -t nat -A PREROUTING -p tcp -i "$WAN_IFACE" --dport "$EXTRA_PORT" -j DNAT --to-destination 192.168.201.2 2>/dev/null || true
if [ "$OS_TYPE" = "debian" ]; then
netfilter-persistent save >/dev/null 2>&1 || true
else
if command -v rc-update >/dev/null 2>&1 && [ -x /etc/init.d/iptables ]; then
rc-update add iptables default >/dev/null 2>&1 || true
/etc/init.d/iptables save >/dev/null 2>&1 || true
fi
fi
log_ok "已为端口 ${EXTRA_PORT} 添加伪装 (DNAT -> 192.168.201.2)"
}
delete_ports() {
printf "%s\n" "${CYAN}服务端: 删除伪装端口${RESET}"
if ! command -v iptables >/dev/null 2>&1; then
log_warn "未检测到 iptables"
return
fi
RULES="$(iptables -t nat -S PREROUTING 2>/dev/null | grep 192.168.201.2 2>/dev/null || true)"
if [ -z "$RULES" ]; then
log_warn "未检测到指向 192.168.201.2 的伪装端口"
return
fi
TMP="/tmp/phantun_dnat_$$"
echo "$RULES" >"$TMP"
log_info "当前可删除的伪装端口:"
nl -ba "$TMP" | while read num line; do
PORT="$(echo "$line" | sed -n 's/.*--dport \([0-9]*\).*/\1/p')"
if [ -n "$PORT" ]; then
printf " %s) 端口 %s\n" "$num" "$PORT"
else
printf " %s) 规则 %s\n" "$num" "$line"
fi
done
printf "请输入要删除的序号(支持多个,用逗号分隔,如 1,2,3),回车取消: "
read SEL
if [ -z "$SEL" ]; then
log_warn "未选择任何端口"
rm -f "$TMP"
return
fi
OLDIFS="$IFS"
IFS=','
for idx in $SEL; do
idx_trim="$(echo "$idx" | tr -d ' ')"
[ -n "$idx_trim" ] || continue
LINE="$(sed -n "${idx_trim}p" "$TMP" 2>/dev/null || true)"
if [ -z "$LINE" ]; then
log_warn "序号 ${idx_trim} 无效"
continue
fi
CMD="$(echo "$LINE" | sed 's/^-A /-D /')"
iptables -t nat $CMD 2>/dev/null && log_ok "已删除: ${LINE}" || log_warn "删除失败: ${LINE}"
done
IFS="$OLDIFS"
rm -f "$TMP"
if [ "$OS_TYPE" = "debian" ]; then
netfilter-persistent save >/dev/null 2>&1 || true
else
if command -v rc-update >/dev/null 2>&1 && [ -x /etc/init.d/iptables ]; then
rc-update add iptables default >/dev/null 2>&1 || true
/etc/init.d/iptables save >/dev/null 2>&1 || true
fi
fi
}
show_config() {
printf "%s\n" "${CYAN}当前配置与状态:${RESET}"
if load_config; then
printf "主配置角色: %s\n" "$ROLE"
if [ "$ROLE" = "server" ]; then
printf "FakeTCP 监听端口: %s\n" "$LISTEN_PORT"
printf "Hy2 后端: %s:%s\n" "$BACKEND_ADDR" "$BACKEND_PORT"
printf "出口网卡: %s\n" "$WAN_IFACE"
elif [ "$ROLE" = "client" ]; then
printf "主通道远程 FakeTCP: %s:%s\n" "$REMOTE_ADDR" "$REMOTE_PORT"
printf "主通道本地 UDP 端口: %s\n" "$LOCAL_PORT"
printf "出口网卡: %s\n" "$WAN_IFACE"
fi
else
log_warn "未找到主配置文件: ${CONFIG_FILE}"
fi
if [ "$OS_TYPE" = "debian" ]; then
COUNT=0
for unit in /etc/systemd/system/phantun-client-*.service; do
[ -f "$unit" ] || continue
if [ "$COUNT" -eq 0 ]; then
printf "%s\n" "${GREEN}额外客户端实例:${RESET}"
fi
COUNT=$((COUNT+1))
LINE="$(grep -E '^ExecStart=' "$unit" 2>/dev/null | head -n1 || true)"
LPORT="$(echo "$LINE" | sed -n 's/.*--local 0.0.0.0:\([0-9]*\).*/\1/p')"
RADDR="$(echo "$LINE" | sed -n 's/.*--remote \([^ ]*\).*/\1/p')"
if [ -n "$LPORT" ] && [ -n "$RADDR" ]; then
printf " 本地 UDP: %s -> 远程: %s (%s)\n" "$LPORT" "$RADDR" "$(basename "$unit")"
else
printf " %s\n" "$(basename "$unit")"
fi
done
else
COUNT=0
for script in /etc/init.d/phantun-client-*; do
[ -f "$script" ] || continue
BASE="$(basename "$script")"
[ "$BASE" = "phantun-client" ] && continue
if [ "$COUNT" -eq 0 ]; then
printf "%s\n" "${GREEN}额外客户端实例:${RESET}"
fi
COUNT=$((COUNT+1))
LINE="$(grep -E '^command_args=' "$script" 2>/dev/null | head -n1 || true)"
LPORT="$(echo "$LINE" | sed -n 's/.*--local 0.0.0.0:\([0-9]*\).*/\1/p')"
RADDR="$(echo "$LINE" | sed -n 's/.*--remote \([^ ]*\).*/\1/p')"
if [ -n "$LPORT" ] && [ -n "$RADDR" ]; then
printf " 本地 UDP: %s -> 远程: %s (%s)\n" "$LPORT" "$RADDR" "$BASE"
else
printf " %s\n" "$BASE"
fi
done
fi
if command -v iptables >/dev/null 2>&1; then
DNAT_PORTS="$(iptables -t nat -S PREROUTING 2>/dev/null | grep 192.168.201.2 2>/dev/null | sed -n 's/.*--dport \([0-9]*\).*/\1/p' || true)"
if [ -n "$DNAT_PORTS" ]; then
printf "%s\n" "${GREEN}当前 DNAT 到 192.168.201.2 的伪装端口:${RESET}"
echo "$DNAT_PORTS" | sort -u | while read p; do [ -n "$p" ] && echo " TCP 端口: $p"; done
else
log_warn "未检测到指向 192.168.201.2 的 DNAT 规则"
fi
MASQ_RULES="$(iptables -t nat -S POSTROUTING 2>/dev/null | grep 192.168.200.2 2>/dev/null || true)"
if [ -n "$MASQ_RULES" ]; then
printf "%s\n" "${GREEN}当前针对 192.168.200.2 的 MASQUERADE 规则:${RESET}"
echo "$MASQ_RULES"
else
log_warn "未检测到针对 192.168.200.2 的 MASQUERADE 规则"
fi
else
log_warn "系统中未检测到 iptables,无法检测伪装规则"
fi
}
restart_all_services() {
printf "%s\n" "${CYAN}重启所有 phantun 服务${RESET}"
if [ "$OS_TYPE" = "debian" ]; then
if systemctl list-unit-files 2>/dev/null | grep -q phantun-server.service; then
systemctl restart phantun-server >/dev/null 2>&1 && log_ok "已重启 phantun-server" || log_warn "重启 phantun-server 失败"
fi
if systemctl list-unit-files 2>/dev/null | grep -q phantun-client.service; then
systemctl restart phantun-client >/dev/null 2>&1 && log_ok "已重启 phantun-client 主通道" || log_warn "重启 phantun-client 主通道失败"
fi
for unit in /etc/systemd/system/phantun-client-*.service; do
[ -f "$unit" ] || continue
SVC="$(basename "$unit")"
systemctl restart "$SVC" >/dev/null 2>&1 && log_ok "已重启 ${SVC}" || log_warn "重启 ${SVC} 失败"
done
elif [ "$OS_TYPE" = "alpine" ]; then
if [ -f /etc/init.d/phantun-server ]; then
rc-service phantun-server restart >/dev/null 2>&1 && log_ok "已重启 phantun-server" || log_warn "重启 phantun-server 失败"
fi
if [ -f /etc/init.d/phantun-client ]; then
rc-service phantun-client restart >/dev/null 2>&1 && log_ok "已重启 phantun-client 主通道" || log_warn "重启 phantun-client 主通道失败"
fi
for script in /etc/init.d/phantun-client-*; do
[ -f "$script" ] || continue
BASE="$(basename "$script")"
[ "$BASE" = "phantun-client" ] && continue
rc-service "$BASE" restart >/dev/null 2>&1 && log_ok "已重启 ${BASE}" || log_warn "重启 ${BASE} 失败"
done
else
log_error "未知系统类型,无法重启服务"
fi
}
uninstall_phantun() {
printf "%s\n" "${CYAN}卸载 phantun 与伪装规则${RESET}"
load_config || true
if [ "$OS_TYPE" = "debian" ]; then
if systemctl list-unit-files 2>/dev/null | grep -q phantun-server.service; then
systemctl disable --now phantun-server >/dev/null 2>&1 || true
rm -f /etc/systemd/system/phantun-server.service
fi
if systemctl list-unit-files 2>/dev/null | grep -q phantun-client.service; then
systemctl disable --now phantun-client >/dev/null 2>&1 || true
rm -f /etc/systemd/system/phantun-client.service
fi
for unit in /etc/systemd/system/phantun-client-*.service; do
[ -f "$unit" ] || continue
SVC="$(basename "$unit")"
systemctl disable --now "$SVC" >/dev/null 2>&1 || true
rm -f "$unit"
done
systemctl daemon-reload >/dev/null 2>&1 || true
elif [ "$OS_TYPE" = "alpine" ]; then
if [ -f /etc/init.d/phantun-server ]; then
rc-service phantun-server stop >/dev/null 2>&1 || true
rc-update del phantun-server >/dev/null 2>&1 || true
rm -f /etc/init.d/phantun-server
fi
if [ -f /etc/init.d/phantun-client ]; then
rc-service phantun-client stop >/dev/null 2>&1 || true
rc-update del phantun-client >/dev/null 2>&1 || true
rm -f /etc/init.d/phantun-client
fi
for script in /etc/init.d/phantun-client-*; do
[ -f "$script" ] || continue
BASE="$(basename "$script")"
[ "$BASE" = "phantun-client" ] && continue
rc-service "$BASE" stop >/dev/null 2>&1 || true
rc-update del "$BASE" >/dev/null 2>&1 || true
rm -f "$script"
done
fi
if command -v iptables >/dev/null 2>&1; then
iptables -t nat -S PREROUTING 2>/dev/null | grep 192.168.201.2 2>/dev/null | while read -r line; do
iptables -t nat $(echo "$line" | sed 's/^-A /-D /') >/dev/null 2>&1 || true
done
iptables -t nat -S POSTROUTING 2>/dev/null | grep 192.168.200.2 2>/dev/null | while read -r line; do
iptables -t nat $(echo "$line" | sed 's/^-A /-D /') >/dev/null 2>&1 || true
done
if [ "$OS_TYPE" = "debian" ]; then
netfilter-persistent save >/dev/null 2>&1 || true
else
if command -v rc-update >/dev/null 2>&1 && [ -x /etc/init.d/iptables ]; then
rc-update add iptables default >/dev/null 2>&1 || true
/etc/init.d/iptables save >/dev/null 2>&1 || true
fi
fi
fi
rm -f /usr/local/bin/phantun_server /usr/local/bin/phantun_client /usr/local/bin/phantun 2>/dev/null || true
rm -f "$CONFIG_FILE" 2>/dev/null || true
rmdir /etc/phantun 2>/dev/null || true
log_ok "已卸载 phantun 及相关服务和伪装规则"
}
server_menu() {
while true; do
printf "\n%s\n" "${CYAN}====== 服务端管理菜单 ======${RESET}"
printf " 1) 一键配置/修改 服务端\n"
printf " 2) 添加伪装端口\n"
printf " 3) 删除伪装端口\n"
printf " 4) 返回主菜单\n"
printf "请输入数字: "
read CH
case "$CH" in
1) setup_server_main ;;
2) add_extra_port ;;
3) delete_ports ;;
4) break ;;
*) log_warn "无效选择" ;;
esac
done
}
client_menu() {
while true; do
printf "\n%s\n" "${CYAN}====== 客户端管理菜单 ======${RESET}"
printf " 1) 配置/修改 客户端主通道\n"
printf " 2) 添加新的转发实例\n"
printf " 3) 删除转发实例\n"
printf " 4) 返回主菜单\n"
printf "请输入数字: "
read CH
case "$CH" in
1) setup_client_main ;;
2) add_client_instance ;;
3) delete_client_instances ;;
4) break ;;
*) log_warn "无效选择" ;;
esac
done
}
main_menu() {
while true; do
printf "\n%s\n" "${CYAN}========== phantun 一键配置脚本 ==========${RESET}"
printf " 1) 进入 服务端 管理菜单\n"
printf " 2) 进入 客户端 管理菜单\n"
printf " 3) 查看当前配置和状态\n"
printf " 4) 重启所有 phantun 服务\n"
printf " 5) 卸载 phantun 及伪装\n"
printf " 6) 退出\n"
printf "请输入数字: "
read CH
case "$CH" in
1) server_menu ;;
2) client_menu ;;
3) show_config ;;
4) restart_all_services ;;
5) uninstall_phantun ;;
6) exit 0 ;;
*) log_warn "无效选择" ;;
esac
done
}
ensure_root
detect_os
ensure_shortcut
if [ "$OS_TYPE" = "unknown" ]; then
log_error "未知系统,仅支持 Debian/Ubuntu 和 Alpine"
exit 1
fi
main_menu3.2 使用方式
- 保存脚本:
cd /root
nano phantun-menu.sh # 或 vi phantun-menu.sh
# 把上面的脚本完整粘进去保存- 加执行权限:
chmod +x phantun-menu.sh- 首次运行(会顺便创建
phantun快捷命令):
./phantun-menu.sh之后就可以直接敲:
phantun- 选「服务端菜单」,在落地机上配置 Hy2 伪装端口
- 选「客户端菜单」,在中转机上配置主通道和多个转发实例
本文著作权归作者 [ Miku ] 享有,未经作者书面授权,禁止转载,封面图片来源于 [ 互联网 ] ,本文仅供个人学习、研究和欣赏使用。如有异议,请联系博主及时处理。