diff --git a/README.md b/README.md index 440c3d5..ad678a2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,508 @@ # softroute + 如果我是云服务器,一个配置了外网IP,其他主机只有内网ip,上述脚本要怎么修改 + +好的,这是一个非常常见的云服务器场景。在这种情况下,你需要对脚本做一些关键的调整,因为云环境的网络接口和 IP 分配方式与物理机有所不同。 + +核心区别: + + 网卡名称: 云服务器的网卡名称通常是 ensX 或 ethX,但更常见的是 ensX,需要通过 ip a 确认。 + + “内网网卡”的含义: 在云环境中,通常一个实例可能只有一块网卡 (ensX)。这块网卡上可能同时绑定了公网 IP 和内网 IP。或者,如果你创建了多块网卡,其中一块是私有网络接口,另一块是公有网络接口。 + + 内网互通: 云平台通常会提供一个私有网络 (VPC/VNet) 功能,所有相同私有网络中的云主机可以通过它们的内网 IP 相互访问,即使它们在不同的子网或区域,只要私有网络配置允许。 + + IP 地址规划: 云平台会为你的内网 IP 地址范围。 + +假设的云环境拓扑: + + 网关云主机 (Gateway Cloud Server): + + 主网卡 (e.g., ens5): 绑定了 公网 IP 和 内网 IP (例如 10.0.0.10)。 + + 我们仍将其 ens5 视为**“外网网卡”** (因为公网 IP 在其上)。 + + 同时,该网卡也是**“内网网卡”** (因为内网 IP 在其上,其他内网主机通过此 IP 访问)。 + + 这将是一个 “单臂路由” 或 “单网卡路由” 的配置,所有流量都通过这一个接口进出。 + + 内网云主机 (Internal Cloud Server): + + 主网卡 (e.g., ens5): 绑定了 内网 IP (例如 10.0.0.11, 10.0.0.12 等)。 + + 默认网关通常由云平台在 DHCP 中提供,指向其私有网络的网关。 + +挑战: + + 单网卡作为内外网接口: iptables 规则需要更仔细地定义,区分进入网卡的内网流量和从网卡出去的公网流量。 + + 云平台 DHCP 的冲突: 云平台通常自带 DHCP 服务,如果你在网关主机上搭建 DHCP 服务器,可能会和云平台冲突。更推荐的做法是:内网主机继续使用云平台提供的 DHCP 获取 IP,但将其 DNS 指向网关主机。默认网关仍然是云平台提供的。然后,网关主机使用 iptables 拦截和重定向所有出站流量到代理。 + +修正后的核心思路 (云服务器环境) + + 网关主机: + + Netplan: 保持其主网卡 (ensX) 使用 DHCP 获取 IP(包括公网和内网 IP),或者配置静态内网 IP (但要确保不和云平台 DHCP 冲突)。通常,保持 DHCP 模式,然后由脚本获取网卡名和内网 IP。 + + IP 转发: 保持开启。 + + SSH SOCKS5 代理: 保持不变。 + + Redsocks2 透明代理: 保持不变。 + + Dnsmasq 智能 DNS 转发: 保持不变,监听其内网 IP。 + + ISC DHCP Server: 不再需要在网关主机上运行 DHCP 服务器! 因为云平台已经提供了。 + + IPTables: 这是最关键的改动。不再有明确的 WAN_INTERFACE 和 LAN_INTERFACE 区别。所有流量都通过一个接口。PREROUTING 规则需要捕获来自所有内网 IP 段的流量。POSTROUTING 仍然将流量伪装成网关主机的公网 IP (或其主内网 IP 如果流量不出公网)。 + + 内网主机: + + Netplan: 保持 DHCP 客户端模式(默认),让它们从云平台获取 IP 和默认网关。 + + DNS: 这是唯一需要手动或通过云平台配置的地方。 将内网主机的 DNS 服务器手动或通过云平台提供的机制,设置为 网关云主机的内网 IP。 + +客制化参数 (Jinja2 变量) 调整 + +新/修改的变量: + + gateway_main_interface: 网关云主机的主网卡名称 (e.g., ens5) + + client_main_interface: 内网云主机的主网卡名称 (e.g., ens5) + + internal_network_range: 你的云平台提供的内部网络 IP 范围 (e.g., 10.0.0.0/16) + +移除的变量: + + wan_interface + + lan_interface + + dhcp_start_ip, dhcp_end_ip, dhcp_lease_time (因为不再运行 DHCP 服务器) + +gateway_config.sh.j2 模板 (云服务器版) + +代码段 +``` +#!/bin/bash +set -eo pipefail + +# ============================================================================== +# 网关主机配置变量 - 请根据你的实际环境修改! +# ============================================================================== +# 网关主机的主网卡名称 (例如 eth0, ens5 等) +GATEWAY_MAIN_INTERFACE="{{ gateway_main_interface }}" + +# 网关主机的内网IP (脚本会自动获取,用于监听和DNS) +# 我们假设该网卡上有一个内网IP,例如 10.0.0.10 +GATEWAY_LAN_IP="" +GATEWAY_PUBLIC_IP="" # 网关主机的公网IP,脚本会尝试获取 +GATEWAY_LAN_CIDR="{{ internal_network_range }}" # 你的云平台内部网络的IP范围,例如 10.0.0.0/16 + +# SSH SOCKS5 代理配置 +REMOTE_SSH_USER="{{ remote_ssh_user }}" # 远程服务器的SSH用户名 +REMOTE_SSH_IP="{{ remote_ssh_ip }}" # 远程服务器的IP地址 +REMOTE_SSH_PORT="{{ remote_ssh_port }}" # 远程服务器的SSH端口 +LOCAL_SOCKS5_PORT="{{ local_socks5_port }}" # SSH SOCKS5 代理在本机监听的端口 (127.0.0.1:1080) + +# 透明代理工具 redsocks2 监听端口 +REDSOCKS_PORT="{{ redsocks_port }}" # redsocks2 在本机监听的端口 + +# DNSMASQ 配置 +DNSMASQ_LISTEN_IP="" # 脚本会自动获取 GATEWAY_LAN_IP +DOMESTIC_DNS="{{ domestic_dns }}" # 国内DNS服务器 +FOREIGN_DNS="{{ foreign_dns }}" # 国外DNS服务器 (通过SSH SOCKS5代理访问) + +# GFWLIST2NEW 工具仓库和安装路径 +GFWLIST2NEW_REPO="{{ gfwlist2new_repo }}" +GFWLIST2NEW_DIR="{{ gfwlist2new_dir }}" + +# ============================================================================== +# 通用函数 +# ============================================================================== +log_info() { + echo -e "\e[32m[INFO] $(date +'%Y-%m-%d %H:%M:%S') $1\e[0m" +} + +log_warn() { + echo -e "\e[33m[WARN] $(date +'%Y-%m-%d %H:%M:%S') $1\e[0m" >&2 +} + +log_error() { + echo -e "\e[31m[ERROR] $(date +'%Y-%m-%d %H:%M:%S') $1\e[0m" >&2 + exit 1 +} + +check_root() { + if [[ $EUID -ne 0 ]]; then + log_error "此脚本必须以 root 用户或使用 sudo 运行。" + fi +} + +install_package() { + PACKAGE="$1" + if ! dpkg -s "$PACKAGE" &>/dev/null; then + log_info "安装 $PACKAGE..." + sudo apt install -y "$PACKAGE" || log_error "安装 $PACKAGE 失败。" + else + log_info "$PACKAGE 已安装。" + fi +} + +# ============================================================================== +# 0. 前置检查与环境初始化 +# ============================================================================== +check_root +log_info "开始网关主机配置脚本 (云服务器环境)..." + +log_info "更新系统软件包列表..." +sudo apt update || log_error "apt update 失败。" + +log_info "禁用 UFW 以避免与 iptables 冲突..." +sudo ufw disable || log_warn "UFW 未运行或禁用失败,请手动确认。" + +log_info "安装必要工具:net-tools, iptables-persistent, ipset, dnsmasq, redsocks2, git, python3-pip..." +install_package net-tools +install_package iptables-persistent +install_package ipset +install_package dnsmasq +install_package redsocks2 +install_package git +install_package python3-pip +# install_package isc-dhcp-server # 云服务器环境不需要运行 DHCP 服务器 + +# 动态获取网关主机的内网IP和公网IP +log_info "获取网关主机的 IP 地址..." +GATEWAY_LAN_IP=$(ip -4 addr show dev ${GATEWAY_MAIN_INTERFACE} | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1) +if [ -z "${GATEWAY_LAN_IP}" ]; then + log_error "无法获取网卡 ${GATEWAY_MAIN_INTERFACE} 的内网 IP 地址。请检查网卡名称或网络配置。" +fi +log_info "网关主机的内网 IP: ${GATEWAY_LAN_IP}" + +# 尝试获取公网IP (可能需要专门的服务或配置,这里假设通常第一个IP是公网或路由器的出口IP) +# 更准确的获取公网IP方式是 curl ifconfig.me 但这取决于网络能否访问外网 +# 这里我们直接使用网卡IP进行MASQUERADE,或如果云平台有专门的公网IP,需要替换 +GATEWAY_PUBLIC_IP=$(curl -s ifconfig.me) # 尝试通过外部服务获取公网IP,如果无法访问,则MASQUERADE会使用网卡主IP +if [ -z "${GATEWAY_PUBLIC_IP}" ]; then + log_warn "无法通过外部服务获取公网 IP。MASQUERADE 将使用网卡主 IP。" + # 另一种方式是获取该网卡上的任何一个非10. 172.16-31. 192.168. 的IP + # GATEWAY_PUBLIC_IP=$(ip -4 addr show dev ${GATEWAY_MAIN_INTERFACE} | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -vE '^(10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.)' | head -1) +fi +log_info "网关主机的公网 IP (或出口 IP): ${GATEWAY_PUBLIC_IP:-Using main interface IP}" +DNSMASQ_LISTEN_IP="${GATEWAY_LAN_IP}" + + +# ============================================================================== +# 1. 配置网卡和 IP 转发 +# ============================================================================== +log_info "确保网关主机主网卡 (${GATEWAY_MAIN_INTERFACE}) 为 DHCP 模式..." +# Netplan 配置 (通常云服务器默认就是 DHCP) +NETPLAN_FILE="/etc/netplan/01-netcfg.yaml" +# 备份现有配置 +sudo cp ${NETPLAN_FILE} ${NETPLAN_FILE}.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份Netplan文件失败。" + +cat < /dev/null +network: + version: 2 + renderer: networkd + ethernets: + {{ gateway_main_interface }}: + dhcp4: true # 云服务器通常使用DHCP获取IP + optional: true +EOF + +sudo netplan try +sudo netplan apply || log_error "应用 Netplan 配置失败,请检查网卡名称。" +log_info "网卡 ${GATEWAY_MAIN_INTERFACE} 已配置为 DHCP 模式。" + + +log_info "开启 IP 转发功能..." +sudo sed -i '/^#net.ipv4.ip_forward=1/s/^#//' /etc/sysctl.conf || true # 取消注释 +echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf > /dev/null # 确保添加 +sudo sysctl -p > /dev/null || log_error "应用 sysctl 配置失败。" +log_info "IP 转发已开启。" + +# ============================================================================== +# 2. SSH SOCKS5 代理 (保持不变) +# ============================================================================== +log_info "启动 SSH SOCKS5 代理 (${REMOTE_SSH_USER}@${REMOTE_SSH_IP}:${REMOTE_SSH_PORT})..." +if [ ! -f "$HOME/.ssh/id_rsa" ] && [ ! -f "$HOME/.ssh/id_dsa" ]; then + log_warn "SSH 密钥文件不存在。SSH 连接可能需要输入密码或先生成密钥对。" + log_warn "请确保可以无密码SSH登录到远程服务器,或手动输入密码完成连接。" +fi +sudo pkill -f "ssh -D ${LOCAL_SOCKS5_PORT}" || true +sudo ssh -D ${LOCAL_SOCKS5_PORT} -N -f -p ${REMOTE_SSH_PORT} ${REMOTE_SSH_USER}@${REMOTE_SSH_IP} -o ExitOnForwardFailure=yes +if [ $? -ne 0 ]; then + log_error "SSH SOCKS5 代理启动失败。请检查远程SSH服务器信息和连接。" +fi +log_info "SSH SOCKS5 代理已在 127.0.0.1:${LOCAL_SOCKS5_PORT} 启动。" +sleep 2 +if ! ss -tnlp | grep ":${LOCAL_SOCKS5_PORT}" &>/dev/null; then + log_error "SSH SOCKS5 代理端口 ${LOCAL_SOCKS5_PORT} 未在监听。请手动检查 SSH 进程。" +fi + +# ============================================================================== +# 3. Redsocks2 透明代理 (保持不变) +# ============================================================================== +log_info "配置 redsocks2 透明代理..." +sudo cp /etc/redsocks.conf /etc/redsocks.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 redsocks.conf 失败。" +cat < /dev/null +base { + log_debug = off; + log_info = on; + log = "syslog:daemon"; + daemon = on; + redirector = iptables; +} +redsocks { + local_ip = 127.0.0.1; + local_port = ${REDSOCKS_PORT}; + ip = 127.0.0.1; + port = ${LOCAL_SOCKS5_PORT}; + type = socks5; +} +EOF +sudo systemctl restart redsocks2 || log_error "重启 redsocks2 服务失败。" +sudo systemctl enable redsocks2 || log_error "启用 redsocks2 服务失败。" +log_info "redsocks2 透明代理已配置并启动,监听 127.0.0.1:${REDSOCKS_PORT}。" + +# ============================================================================== +# 4. Dnsmasq 智能 DNS 转发 (保持不变,监听内网IP) +# ============================================================================== +log_info "配置 dnsmasq 智能 DNS 转发..." +sudo cp /etc/dnsmasq.conf /etc/dnsmasq.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 dnsmasq.conf 失败。" +sudo rm -f /etc/dnsmasq.d/* # 清理旧的额外配置 +cat < /dev/null +listen-address=${DNSMASQ_LISTEN_IP},127.0.0.1 +no-resolv +no-poll +server={{ domestic_dns }} +conf-dir=/etc/dnsmasq.d +EOF + +# 确保 dnsmasq 不会干扰云平台的DHCP (不再是 isc-dhcp-server) +# 这些选项只是建议,如果云平台DHCP工作良好,可能不需要 +# echo "dhcp-option=option:router,${GATEWAY_LAN_IP}" | sudo tee -a /etc/dnsmasq.conf > /dev/null +# echo "dhcp-option=option:dns-server,${GATEWAY_LAN_IP}" | sudo tee -a /etc/dnsmasq.conf > /dev/null + +sudo systemctl restart dnsmasq || log_error "重启 dnsmasq 服务失败。" +sudo systemctl enable dnsmasq || log_error "启用 dnsmasq 服务失败。" +log_info "dnsmasq 智能 DNS 转发已初步配置,监听 ${DNSMASQ_LISTEN_IP}。" + +# ============================================================================== +# 5. 安装和配置 gfwlist2new (保持不变) +# ============================================================================== +log_info "安装和配置 gfwlist2new 工具..." +if [ ! -d "${GFWLIST2NEW_DIR}" ]; then + log_info "克隆 gfwlist2new 仓库..." + sudo git clone "{{ gfwlist2new_repo }}" "${GFWLIST2NEW_DIR}" || log_error "克隆 gfwlist2new 仓库失败。" +fi +cd "${GFWLIST2NEW_DIR}" +log_info "安装 gfwlist2new 依赖..." +sudo pip3 install -r requirements.txt || log_error "安装 gfwlist2new 依赖失败。" + +log_info "配置 gfwlist2new 的配置文件 config.conf..." +sudo cp config.conf config.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 gfwlist2new config.conf 失败。" +cat < /dev/null +[DEFAULT] +IPSET_NAME = gfwlist +IPSET_FILE = /etc/ipset/gfwlist.conf +DNSMASQ_CONF_PATH = /etc/dnsmasq.d/gfwlist_router.conf +DNSMASQ_LOG_PATH = /var/log/dnsmasq.log +DNS_PROXY_SERVER = 127.0.0.1#${REDSOCKS_PORT} # 这里的端口要指向 redsocks2 +Proxy Config +SOCKS5_SERVER = 127.0.0.1:${LOCAL_SOCKS5_PORT} # 这里的端口是SSH SOCKS5代理 +GITHUB_RAW_URL = raw.githubusercontent.com +EOF + +log_info "执行 gfwlist2new 生成 ipset 和 dnsmasq 规则..." +sudo python3 gfwlist2new.py -s "${SOCKS5_SERVER}" -f "{{ foreign_dns }}" -d "{{ domestic_dns }}" || log_error "gfwlist2new 运行失败。" +sudo systemctl restart dnsmasq || log_error "重启 dnsmasq (gfwlist) 失败。" + +log_info "设置 gfwlist2new 定时更新任务 (每天凌晨 3:00)..." +(sudo crontab -l 2>/dev/null; echo "0 3 * * * cd ${GFWLIST2NEW_DIR} && sudo python3 gfwlist2new.py -s ${SOCKS5_SERVER} -f {{ foreign_dns }} -d {{ domestic_dns }} && sudo systemctl restart dnsmasq") | sudo crontab - +log_info "gfwlist2new 配置完成并设置定时更新。" +cd - > /dev/null + +# ============================================================================== +# 6. 配置 IPTABLES 规则实现透明代理和 NAT (针对单网卡环境) +# ============================================================================== +log_info "配置 IPTABLES 规则 (NAT 和透明代理,针对云服务器单网卡环境)..." + +sudo iptables -F +sudo iptables -X +sudo iptables -t nat -F +sudo iptables -t nat -X +sudo iptables -t mangle -F +sudo iptables -t mangle -X + +# 1. 开启 NAT (使内网访问外网) +# MASQUERADE 应该基于出口接口,这里就是 GATEWAY_MAIN_INTERFACE +# --to-source 可以明确指定公网IP,如果GATEWAY_PUBLIC_IP能获取到 +if [ -n "${GATEWAY_PUBLIC_IP}" ]; then + sudo iptables -t nat -A POSTROUTING -o ${GATEWAY_MAIN_INTERFACE} -s ${GATEWAY_LAN_CIDR} -j SNAT --to-source ${GATEWAY_PUBLIC_IP} + log_info "已配置 NAT 规则,出口IP为 ${GATEWAY_PUBLIC_IP}。" +else + sudo iptables -t nat -A POSTROUTING -o ${GATEWAY_MAIN_INTERFACE} -s ${GATEWAY_LAN_CIDR} -j MASQUERADE + log_info "已配置 NAT 规则,使用 ${GATEWAY_MAIN_INTERFACE} 主IP作为出口。" +fi + +# 2. IP 转发规则 (允许所有来自内网段的流量通过 GATEWAY_MAIN_INTERFACE 转发) +# 这里更简化,所有来自内网段的流量都接受转发 +sudo iptables -A FORWARD -s ${GATEWAY_LAN_CIDR} -o ${GATEWAY_MAIN_INTERFACE} -j ACCEPT +sudo iptables -A FORWARD -i ${GATEWAY_MAIN_INTERFACE} -d ${GATEWAY_LAN_CIDR} -m state --state RELATED,ESTABLISHED -j ACCEPT +log_info "已配置 IP 转发规则。" + +# 3. 透明代理规则 (使用 redsocks2 和 ipset) +# 重定向所有来自内网段的 TCP 流量,如果目标IP在 gfwlist 中,则到 redsocks2 +sudo iptables -t nat -A PREROUTING -i ${GATEWAY_MAIN_INTERFACE} -p tcp -s ${GATEWAY_LAN_CIDR} -m set --match-set gfwlist dst -j REDIRECT --to-ports ${REDSOCKS_PORT} +log_info "已配置透明代理重定向规则 (所有端口,目标IP在 gfwlist 中的内网TCP流量到 redsocks2)。" + +# 4. 排除 redsocks2 自身流量循环 (保持不变) +sudo iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport ${REDSOCKS_PORT} -j RETURN +sudo iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport ${LOCAL_SOCKS5_PORT} -j RETURN +log_info "已配置排除 redsocks2 自身流量循环的规则。" + +# 保存 iptables 规则 +sudo netfilter-persistent save || log_error "保存 iptables 规则失败。" +sudo systemctl enable netfilter-persistent || log_error "启用 netfilter-persistent 服务失败。" +log_info "IPTABLES 规则已配置并保存。" + +log_info "----------------------------------------------------------------------------------" +log_info "网关云主机配置完成!" +log_info "请检查 redsocks2 和 ssh -D 进程是否正常运行。" +log_info "请注意:SSH SOCKS5 代理可能需要手动输入密码或配置免密登录才能持久运行。" +log_info "----------------------------------------------------------------------------------" +``` +render_configs.py 脚本 (云服务器版) + +Python +``` +from jinja2 import Environment, FileSystemLoader + +# 1. 定义你的客制化参数字典 +config_vars = { + # 全局/网关主机变量 + "gateway_main_interface": "ens5", # !!! 网关云主机的主网卡名称,请根据实际情况修改! + "internal_network_range": "10.0.0.0/16", # !!! 你的云平台私有网络IP范围,例如 10.0.0.0/16, 172.16.0.0/12, 192.168.0.0/16 + + # SSH SOCKS5 代理配置 + "remote_ssh_user": "your_remote_user", # !!! 替换为你的远程SSH服务器用户名 + "remote_ssh_ip": "your_remote_server_ip", # !!! 替换为你的远程SSH服务器IP + "remote_ssh_port": "22", + + # 其他默认值,通常无需修改 + "local_socks5_port": "1080", + "redsocks_port": "12345", + "domestic_dns": "223.5.5.5,114.114.114.114", + "foreign_dns": "8.8.8.8,1.1.1.1", + "gfwlist2new_repo": "https://github.com/louisabrahams/gfwlist2new.git", + "gfwlist2new_dir": "/opt/gfwlist2new", +} + +# 2. 设置 Jinja2 环境 +env = Environment(loader=FileSystemLoader('.')) + +# 3. 渲染 gateway_config.sh +gateway_template = env.get_template('gateway_config.sh.j2') +rendered_gateway_script = gateway_template.render(config_vars) + +with open('gateway_config.sh', 'w') as f: + f.write(rendered_gateway_script) +print("Generated gateway_config.sh for Cloud Server Environment.") + +# 4. 提供内网主机配置说明 +print("\n--- 内网云主机配置说明 ---") +print("内网云主机无需运行任何脚本!它们会从云平台 DHCP 获取 IP 和默认网关。") +print("\n您唯一需要做的是:") +print("1. 确保内网云主机的网卡 (例如 {{ config_vars['client_main_interface'] | default('ens5') }}) 是 DHCP 客户端模式 (云平台默认)。") +print("2. **最关键的步骤:将内网云主机的 DNS 服务器设置为网关云主机的内网 IP。**") +print(" - 这通常通过云平台的管理控制台进行配置,或者在内网云主机上手动修改 Netplan。") +print(" - 例如,如果网关云主机的内网 IP 是 10.0.0.10,则内网云主机的 DNS 应该设置为 10.0.0.10。") +print("\n 示例 Netplan 配置 (内网云主机,如果需要手动修改):") +print(f" network:") +print(f" version: 2") +print(f" renderer: networkd") +print(f" ethernets:") +print(f" {{ config_vars['client_main_interface'] | default('ens5') }}: # 替换为内网主机的实际网卡名称") +print(f" dhcp4: true") +print(f" nameservers:") +print(f" addresses: [{{ config_vars['gateway_lan_ip'] }}] # DNS 指向网关云主机的内网 IP") +print(f" optional: true") +print("\n 修改后,应用 Netplan 配置:sudo netplan try && sudo netplan apply") +print(" 或者重启内网云主机。") +print("--------------------------") + +print("\n!!! 请务必检查生成的 gateway_config.sh 文件,特别是 SSH 代理、网卡名称和云平台私有网络范围等配置。") +print("在执行前,给它添加可执行权限:chmod +x gateway_config.sh") +``` + +使用步骤 (云服务器环境): + + 保存模板和 Python 脚本: + + 将上面修改后的 gateway_config.sh.j2 模板保存好。 + + 将修改后的 render_configs.py 脚本保存好。 + + 修改 render_configs.py 中的变量: + + gateway_main_interface: 非常重要! 在网关云主机上运行 ip a 确认主网卡名称 (例如 ens5 或 eth0)。 + + internal_network_range: 非常重要! 查阅你的云平台文档,了解你的私有网络的 IP 范围,例如 10.0.0.0/16。 + + remote_ssh_user, remote_ssh_ip, remote_ssh_port: 你的境外代理服务器信息。 + + client_main_interface: 客户端网卡名称,主要用于说明文档。 + + 运行 Python 渲染脚本: +``` +python3 render_configs.py +``` + +这将生成 gateway_config.sh 文件。 + +在网关云主机上执行 gateway_config.sh: +Bash +``` +sudo chmod +x gateway_config.sh +sudo ./gateway_config.sh +``` + 执行过程中,脚本会尝试动态获取网关主机的内网 IP,并可能会询问 SSH 密码。 + + 配置内网云主机: + + 关键: 登录到每个内网云主机,将其 DNS 服务器设置为网关云主机的内网 IP (例如 10.0.0.10)。 + + 如何配置 DNS: + + 云平台控制台: 许多云平台允许你在实例的网络配置中指定自定义 DNS 服务器。这是最推荐的方式。 + + 手动修改 Netplan: 如果云平台不支持,或者你想验证,可以登录到内网主机,编辑 01-netcfg.yaml,确保其 nameservers 部分指向网关主机的内网 IP。 + + 然后应用 Netplan 或重启内网主机。 + + 测试: 在内网云主机上测试网络连接。 + +重要提示: + + SSH 免密登录: 在云环境中,强烈建议为 SSH SOCKS5 代理配置 SSH 密钥对进行免密登录,否则每次重启 SSH 代理或网关主机都可能需要手动输入密码。 + + 云平台安全组/防火墙: 确保你的云平台安全组或防火墙允许以下流量: + + 网关云主机: + + 允许来自内网主机的流量到网关主机的内网 IP。 + + 允许网关主机访问外部网络 (SSH 到远程代理服务器,以及正常的出站流量)。 + + 允许内网主机访问网关主机的 53 端口 (DNS)。 + + 内网云主机: + + 允许内网主机访问网关主机的内网 IP (默认网关和 DNS)。 + + 允许内网主机访问其他内网主机。 + + + diff --git a/scripts/gateway.sh b/scripts/gateway.sh new file mode 100644 index 0000000..d28c88c --- /dev/null +++ b/scripts/gateway.sh @@ -0,0 +1,354 @@ +#!/bin/bash +set -eo pipefail + +# ============================================================================== +# 网关主机配置变量 - 请根据你的实际环境修改! +# ============================================================================== +# 网关主机内网IP (用于其他内网主机连接) +GATEWAY_LAN_IP="{{ gateway_lan_ip }}" +GATEWAY_LAN_CIDR="{{ gateway_lan_cidr }}" # 内网网段 +# DHCP 租约范围 +DHCP_START_IP="{{ dhcp_start_ip }}" +DHCP_END_IP="{{ dhcp_end_ip }}" +DHCP_LEASE_TIME="{{ dhcp_lease_time }}" # 租约时间,秒 (例如 2 小时) + +# 网关主机网卡名称 +WAN_INTERFACE="{{ wan_interface }}" # 连接外网的网卡,通常是DHCP获取IP +LAN_INTERFACE="{{ lan_interface }}" # 连接内网的网卡,我们将配置静态IP + +# SSH SOCKS5 代理配置 +REMOTE_SSH_USER="{{ remote_ssh_user }}" # 远程服务器的SSH用户名 +REMOTE_SSH_IP="{{ remote_ssh_ip }}" # 远程服务器的IP地址 +REMOTE_SSH_PORT="{{ remote_ssh_port }}" # 远程服务器的SSH端口 +LOCAL_SOCKS5_PORT="{{ local_socks5_port }}" # SSH SOCKS5 代理在本机监听的端口 (127.0.0.1:1080) + +# 透明代理工具 redsocks2 监听端口 +REDSOCKS_PORT="{{ redsocks_port }}" # redsocks2 在本机监听的端口 + +# DNSMASQ 配置 +DNSMASQ_LISTEN_IP="${GATEWAY_LAN_IP}" # dnsmasq 监听的IP +DOMESTIC_DNS="{{ domestic_dns }}" # 国内DNS服务器 +FOREIGN_DNS="{{ foreign_dns }}" # 国外DNS服务器 (通过SSH SOCKS5代理访问) + +# GFWLIST2NEW 工具仓库和安装路径 +GFWLIST2NEW_REPO="{{ gfwlist2new_repo }}" +GFWLIST2NEW_DIR="{{ gfwlist2new_dir }}" + +# ============================================================================== +# 通用函数 +# ============================================================================== +log_info() { + echo -e "\e[32m[INFO] $(date +'%Y-%m-%d %H:%M:%S') $1\e[0m" +} + +log_warn() { + echo -e "\e[33m[WARN] $(date +'%Y-%m-%d %H:%M:%S') $1\e[0m" >&2 +} + +log_error() { + echo -e "\e[31m[ERROR] $(date +'%Y-%m-%d %H:%M:%S') $1\e[0m" >&2 + exit 1 +} + +check_root() { + if [[ $EUID -ne 0 ]]; then + log_error "此脚本必须以 root 用户或使用 sudo 运行。" + fi +} + +install_package() { + PACKAGE="$1" + if ! dpkg -s "$PACKAGE" &>/dev/null; then + log_info "安装 $PACKAGE..." + sudo apt install -y "$PACKAGE" || log_error "安装 $PACKAGE 失败。" + else + log_info "$PACKAGE 已安装。" + fi +} + +# ============================================================================== +# 0. 前置检查与环境初始化 +# ============================================================================== +check_root +log_info "开始网关主机配置脚本..." + +log_info "更新系统软件包列表..." +sudo apt update || log_error "apt update 失败。" + +log_info "禁用 UFW 以避免与 iptables 冲突..." +sudo ufw disable || log_warn "UFW 未运行或禁用失败,请手动确认。" + +log_info "安装必要工具:net-tools, iptables-persistent, ipset, dnsmasq, redsocks2, git, python3-pip, isc-dhcp-server..." +install_package net-tools +install_package iptables-persistent +install_package ipset +install_package dnsmasq +install_package redsocks2 +install_package git +install_package python3-pip +install_package isc-dhcp-server # 新增 DHCP 服务器安装 + +# ============================================================================== +# 1. 配置内网网卡 IP 和 IP 转发 +# ============================================================================== +log_info "配置网关主机内网网卡 (${LAN_INTERFACE}) 静态 IP..." +# Netplan 配置 +NETPLAN_FILE="/etc/netplan/01-netcfg.yaml" +# 备份现有配置 +sudo cp ${NETPLAN_FILE} ${NETPLAN_FILE}.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份Netplan文件失败。" + +cat < /dev/null +network: + version: 2 + renderer: networkd + ethernets: + {{ wan_interface }}: + dhcp4: true # 外网网卡通常通过DHCP获取IP + optional: true # 允许此接口暂时不可用 + {{ lan_interface }}: + addresses: + - ${GATEWAY_LAN_IP}/24 + # 由于 dnsmasq 也会提供 DNS 服务,这里不需要单独配置 nameservers +EOF + +sudo netplan try +sudo netplan apply || log_error "应用 Netplan 配置失败,请检查网卡名称和IP地址。" +log_info "内网网卡 ${LAN_INTERFACE} 已配置 IP: ${GATEWAY_LAN_IP}。" + +log_info "开启 IP 转发功能..." +sudo sed -i '/^#net.ipv4.ip_forward=1/s/^#//' /etc/sysctl.conf || true # 取消注释 +echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf > /dev/null # 确保添加 +sudo sysctl -p > /dev/null || log_error "应用 sysctl 配置失败。" +log_info "IP 转发已开启。" + +# ============================================================================== +# 2. 配置 DHCP 服务器 (ISC DHCP Server) +# ============================================================================== +log_info "配置 ISC DHCP Server..." +# 确保 DHCP 服务器只监听内网接口 +sudo sed -i "s/^INTERFACESv4=.*/INTERFACESv4=\"${LAN_INTERFACE}\"/g" /etc/default/isc-dhcp-server || log_error "配置 isc-dhcp-server 监听接口失败。" + +# 备份 DHCP 主配置文件 +sudo cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 dhcpd.conf 失败。" + +# 清空并写入新的 DHCP 配置 +cat < /dev/null +# DHCPD CONFIGURATION FILE + +# The ddns-update-style parameter to use for DDNS updates. +ddns-update-style none; + +# Default lease time +default-lease-time ${DHCP_LEASE_TIME}; +max-lease-time ${DHCP_LEASE_TIME}; + +# If this DHCP server is the official DHCP server for the local network segment +# then uncomment the authoritative directive. +authoritative; + +# A slightly different configuration for an internal subnet. +subnet {{ gateway_lan_cidr%.* }}.0 netmask 255.255.255.0 { + range ${DHCP_START_IP} ${DHCP_END_IP}; + option routers ${GATEWAY_LAN_IP}; + option domain-name-servers ${GATEWAY_LAN_IP}; # DNS 指向网关主机本身 + option broadcast-address {{ gateway_lan_cidr%.* }}.255; + default-lease-time ${DHCP_LEASE_TIME}; + max-lease-time ${DHCP_LEASE_TIME}; +} + +# No service will be given on this subnet, but DECLINEs from hosts on this +# subnet will be processed. +# subnet 192.168.1.0 netmask 255.255.255.0 { +# } +EOF + +sudo systemctl restart isc-dhcp-server || log_error "重启 isc-dhcp-server 服务失败。" +sudo systemctl enable isc-dhcp-server || log_error "启用 isc-dhcp-server 服务失败。" +log_info "ISC DHCP Server 已配置并启动,在 ${LAN_INTERFACE} 上提供 DHCP 服务。" + + +# ============================================================================== +# 3. 启动 SSH SOCKS5 代理 (保持不变) +# ============================================================================== +log_info "启动 SSH SOCKS5 代理 (${REMOTE_SSH_USER}@${REMOTE_SSH_IP}:${REMOTE_SSH_PORT})..." +# 检查 SSH 密钥是否存在,否则可能需要密码或生成密钥 +if [ ! -f "$HOME/.ssh/id_rsa" ] && [ ! -f "$HOME/.ssh/id_dsa" ]; then + log_warn "SSH 密钥文件不存在。SSH 连接可能需要输入密码或先生成密钥对。" + log_warn "请确保可以无密码SSH登录到远程服务器,或手动输入密码完成连接。" +fi + +# 确保旧的代理进程被杀死 +sudo pkill -f "ssh -D ${LOCAL_SOCKS5_PORT}" || true + +# 启动 SSH SOCKS5 代理,后台运行 +sudo ssh -D ${LOCAL_SOCKS5_PORT} -N -f -p ${REMOTE_SSH_PORT} ${REMOTE_SSH_USER}@${REMOTE_SSH_IP} -o ExitOnForwardFailure=yes +if [ $? -ne 0 ]; then + log_error "SSH SOCKS5 代理启动失败。请检查远程SSH服务器信息和连接。" +fi +log_info "SSH SOCKS5 代理已在 127.0.0.1:${LOCAL_SOCKS5_PORT} 启动。" +# 验证代理是否在监听 (需要一点时间启动) +sleep 2 +if ! ss -tnlp | grep ":${LOCAL_SOCKS5_PORT}" &>/dev/null; then + log_error "SSH SOCKS5 代理端口 ${LOCAL_SOCKS5_PORT} 未在监听。请手动检查 SSH 进程。" +fi + +# ============================================================================== +# 4. 配置 redsocks2 (透明 SOCKS5 代理) (保持不变) +# ============================================================================== +log_info "配置 redsocks2 透明代理..." +# 备份现有配置 +sudo cp /etc/redsocks.conf /etc/redsocks.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 redsocks.conf 失败。" + +cat < /dev/null +base { + log_debug = off; + log_info = on; + log = "syslog:daemon"; + daemon = on; # 后台运行 + redirector = iptables; +} + +redsocks { + local_ip = 127.0.0.1; + local_port = ${REDSOCKS_PORT}; + + ip = 127.0.0.1; # SSH SOCKS5 代理的地址 + port = ${LOCAL_SOCKS5_PORT}; # SSH SOCKS5 代理的端口 + + type = socks5; # SOCKS5 代理 +} +EOF + +sudo systemctl restart redsocks2 || log_error "重启 redsocks2 服务失败。" +sudo systemctl enable redsocks2 || log_error "启用 redsocks2 服务失败。" +log_info "redsocks2 透明代理已配置并启动,监听 127.0.0.1:${REDSOCKS_PORT}。" + +# ============================================================================== +# 5. 配置 dnsmasq 智能 DNS 转发 (保持不变) +# ============================================================================== +log_info "配置 dnsmasq 智能 DNS 转发..." +# 备份现有配置 +sudo cp /etc/dnsmasq.conf /etc/dnsmasq.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 dnsmasq.conf 失败。" +sudo rm -f /etc/dnsmasq.d/* # 清理旧的额外配置 + +cat < /dev/null +# 指定监听地址,只监听内网IP和本地环回 +listen-address=${DNSMASQ_LISTEN_IP},127.0.0.1 + +# 不使用 /etc/resolv.conf 中的 DNS 服务器 +no-resolv +no-poll + +# 国内域名直连公共DNS +server={{ domestic_dns }} + +# 国外域名通过 gfwlist2new 生成的配置文件转发到代理 +# dnsmasq will read additional configuration files from /etc/dnsmasq.d +conf-dir=/etc/dnsmasq.d +EOF + +# 注意:DHCP 服务器功能由 isc-dhcp-server 提供,dnsmasq 不再提供 DHCP。 +# 确保 dnsmasq 不会干扰 DHCP +echo "dhcp-option=option:router,${GATEWAY_LAN_IP}" | sudo tee -a /etc/dnsmasq.conf > /dev/null +echo "dhcp-option=option:dns-server,${GATEWAY_LAN_IP}" | sudo tee -a /etc/dnsmasq.conf > /dev/null + +sudo systemctl restart dnsmasq || log_error "重启 dnsmasq 服务失败。" +sudo systemctl enable dnsmasq || log_error "启用 dnsmasq 服务失败。" +log_info "dnsmasq 智能 DNS 转发已初步配置。" + +# ============================================================================== +# 6. 安装和配置 gfwlist2new (保持不变) +# ============================================================================== +log_info "安装和配置 gfwlist2new 工具..." + +if [ ! -d "${GFWLIST2NEW_DIR}" ]; then + log_info "克隆 gfwlist2new 仓库..." + sudo git clone "{{ gfwlist2new_repo }}" "${GFWLIST2NEW_DIR}" || log_error "克隆 gfwlist2new 仓库失败。" +fi + +cd "${GFWLIST2NEW_DIR}" +log_info "安装 gfwlist2new 依赖..." +sudo pip3 install -r requirements.txt || log_error "安装 gfwlist2new 依赖失败。" + +log_info "配置 gfwlist2new 的配置文件 config.conf..." +# 备份现有配置 +sudo cp config.conf config.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 gfwlist2new config.conf 失败。" + +# 更新 config.conf 以适应我们的需求 +# gfwlist2new 默认会将 gfwlist 中的域名指向代理IP +# IPSET_NAME 是 ipset 列表的名称 +# DNSMASQ_CONF_PATH 是 dnsmasq 配置文件路径 +# SOCKS5_SERVER 是代理服务器的IP和端口 (这里是 redsocks2 监听的IP和端口) +cat < /dev/null +[DEFAULT] +# IPSet Config +IPSET_NAME = gfwlist +IPSET_FILE = /etc/ipset/gfwlist.conf + +# Dnsmasq Config +DNSMASQ_CONF_PATH = /etc/dnsmasq.d/gfwlist_router.conf +DNSMASQ_LOG_PATH = /var/log/dnsmasq.log +DNS_PROXY_SERVER = 127.0.0.1#${REDSOCKS_PORT} # 这里的端口要指向 redsocks2 + +# Proxy Config +SOCKS5_SERVER = 127.0.0.1:${LOCAL_SOCKS5_PORT} # 这里的端口是SSH SOCKS5代理 + +# Other +GITHUB_RAW_URL = raw.githubusercontent.com +EOF + +log_info "执行 gfwlist2new 生成 ipset 和 dnsmasq 规则..." +# 第一次运行,生成规则并应用 +sudo python3 gfwlist2new.py -s "${SOCKS5_SERVER}" -f "{{ foreign_dns }}" -d "{{ domestic_dns }}" || log_error "gfwlist2new 运行失败。" +# 重启 dnsmasq 应用新规则 +sudo systemctl restart dnsmasq || log_error "重启 dnsmasq (gfwlist) 失败。" + +log_info "设置 gfwlist2new 定时更新任务 (每天凌晨 3:00)..." +(sudo crontab -l 2>/dev/null; echo "0 3 * * * cd ${GFWLIST2NEW_DIR} && sudo python3 gfwlist2new.py -s ${SOCKS5_SERVER} -f {{ foreign_dns }} -d {{ domestic_dns }} && sudo systemctl restart dnsmasq") | sudo crontab - +log_info "gfwlist2new 配置完成并设置定时更新。" +cd - > /dev/null + +# ============================================================================== +# 7. 配置 IPTABLES 规则实现透明代理和 NAT (保持不变) +# ============================================================================== +log_info "配置 IPTABLES 规则 (NAT 和透明代理)..." + +# 清除现有规则,避免冲突 +sudo iptables -F +sudo iptables -X +sudo iptables -t nat -F +sudo iptables -t nat -X +sudo iptables -t mangle -F +sudo iptables -t mangle -X + +# 1. 开启 NAT (使内网访问外网) +sudo iptables -t nat -A POSTROUTING -o {{ wan_interface }} -j MASQUERADE +log_info "已配置 NAT 规则。" + +# 2. IP 转发规则 (允许内网到外网,并允许已建立连接回传) +sudo iptables -A FORWARD -i {{ lan_interface }} -o {{ wan_interface }} -j ACCEPT +sudo iptables -A FORWARD -o {{ lan_interface }} -i {{ wan_interface }} -m state --state RELATED,ESTABLISHED -j ACCEPT +log_info "已配置 IP 转发规则。" + +# 3. 透明代理规则 (使用 redsocks2 和 ipset) +# 重定向到 redsocks2 的流量,不处理来自 redsocks2 本身的流量 +sudo iptables -t nat -A PREROUTING -i {{ lan_interface }} -p tcp -s {{ gateway_lan_cidr }} -m set --match-set gfwlist dst -j REDIRECT --to-ports ${REDSOCKS_PORT} +log_info "已配置透明代理重定向规则 (所有端口,目标IP在 gfwlist 中的内网TCP流量到 redsocks2)。" + +# 排除 redsocks2 自身流量循环 +# 这一步非常重要,避免 redsocks2 自己产生流量又被 iptables 捕获 +sudo iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport ${REDSOCKS_PORT} -j RETURN +sudo iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport ${LOCAL_SOCKS5_PORT} -j RETURN +log_info "已配置排除 redsocks2 自身流量循环的规则。" + +# 保存 iptables 规则 +sudo netfilter-persistent save || log_error "保存 iptables 规则失败。" +sudo systemctl enable netfilter-persistent || log_error "启用 netfilter-persistent 服务失败。" +log_info "IPTABLES 规则已配置并保存。" + +log_info "----------------------------------------------------------------------------------" +log_info "网关主机配置完成!" +log_info "请检查 redsocks2 和 ssh -D 进程是否正常运行。" +log_info "请注意:SSH SOCKS5 代理可能需要手动输入密码或配置免密登录才能持久运行。" +log_info "内网主机现在应该可以自动通过 DHCP 获取 IP 并访问网络。" +log_info "----------------------------------------------------------------------------------" diff --git a/scripts/render.py b/scripts/render.py new file mode 100644 index 0000000..c8cbe72 --- /dev/null +++ b/scripts/render.py @@ -0,0 +1,48 @@ +from jinja2 import Environment, FileSystemLoader + +# 1. 定义你的客制化参数字典 +config_vars = { + # 全局变量 + "gateway_lan_ip": "192.168.10.1", + "gateway_lan_cidr": "192.168.10.0/24", + "wan_interface": "eth0", # 网关主机外网网卡 + "lan_interface": "eth1", # 网关主机内网网卡 + "client_lan_interface": "eth0", # 内网主机网卡 + + # 网关主机独有变量 + "dhcp_start_ip": "192.168.10.100", + "dhcp_end_ip": "192.168.10.200", + "dhcp_lease_time": "7200", + "remote_ssh_user": "your_remote_user", + "remote_ssh_ip": "your_remote_server_ip", # !!! 替换为你的远程SSH服务器IP + "remote_ssh_port": "22", + "local_socks5_port": "1080", + "redsocks_port": "12345", + "domestic_dns": "223.5.5.5,114.114.114.114", + "foreign_dns": "8.8.8.8,1.1.1.1", + "gfwlist2new_repo": "https://github.com/louisabrahams/gfwlist2new.git", + "gfwlist2new_dir": "/opt/gfwlist2new", +} + +# 2. 设置 Jinja2 环境 +# 假设你的 .j2 模板文件与这个 Python 脚本在同一目录下 +env = Environment(loader=FileSystemLoader('.')) + +# 3. 渲染 gateway_config.sh +gateway_template = env.get_template('gateway_config.sh.j2') +rendered_gateway_script = gateway_template.render(config_vars) + +with open('gateway_config.sh', 'w') as f: + f.write(rendered_gateway_script) +print("Generated gateway_config.sh") + +# 4. 渲染 client_config.sh +client_template = env.get_template('client_config.sh.j2') +rendered_client_script = client_template.render(config_vars) # 客户端脚本也需要一些全局变量 + +with open('client_config.sh', 'w') as f: + f.write(rendered_client_script) +print("Generated client_config.sh") + +print("\n!!! 请务必检查生成的 .sh 文件,特别是 SSH 代理和网卡名称等配置。") +print("在执行前,给它们添加可执行权限:chmod +x gateway_config.sh client_config.sh")