Shadowsocks/Relay

Shadowsocks/Relay

中继服务器

可以在任意 Linux 机器上简单设置几条 iptables 规则架设 shadowsocks 服务器的中继服务器(Relay server)。

需要中继服务器具有公网IP,并且开启 IPV4 转发 net.ipv4.ip_forward=1

参考 iptables 规则:(具体原理请参考 Netfilter

SS_IP=1.2.3.4
SS_PORT=443
LOCAL_IP=1.2.3.4 # Relay server ip
LOCAL_PORT=80 # Relay server port
iptables-save | grep -v "__ssrelaylan__" | iptables-restore
iptables -t nat -I PREROUTING 1 -p tcp -d $LOCAL_IP --dport $LOCAL_PORT -j DNAT --to $SS_IP:$SS_PORT -m comment --comment __ssrelaylan__
iptables -t nat -I PREROUTING 1 -p udp -d $LOCAL_IP --dport $LOCAL_PORT -j DNAT --to $SS_IP:$SS_PORT -m comment --comment __ssrelaylan__
iptables -t nat -I POSTROUTING 1 -d $SS_IP -p tcp --dport $SS_PORT -j SNAT --to $LOCAL_IP -m comment --comment __ssrelaylan__
iptables -t nat -I POSTROUTING 1 -d $SS_IP -p udp --dport $SS_PORT -j SNAT --to $LOCAL_IP -m comment --comment __ssrelaylan__

使用时在 shadowsocks 客户端的连接配置里使用中继服务器的 IP 和端口信息(LOCAL_IP && LOCAL_PORT)即可。这种情况下 shadowsocks 服务器“看到的”连接请求均显示为来源于中继服务器IP,建议在 shadowsocks 服务器配置里设置 ACL 规则将中继服务器 IP 加入白名单。

家庭宽带路由器作为ss中继

如果中继服务器是动态公网 IP(比如家庭宽带),那么不能直接使用 -d $LOCAL_IP 和 --to $LOCAL_IP,而应该:

  • PREROUTING 里使用 -i 指定 incoming 网卡名。
  • POSTROUTING 里使用 -j MASQUERADE 代替 SNAT。
  • (推荐)在 -t mangle PREROUTING 里对流量打标记(mark),在 -t nat POSTROUTING 里匹配标记。(防止MASQUERADE时匹配到其他家庭网络里访问SS服务器的流量)。

参考配置:

SS_IP=1.2.3.4
SS_PORT=443
LOCAL_PORT=21
WAN_IF=pppoe-wan # WAN 网卡名

ip rule add fwmark 0x1/0x1 lookup main prio 1
ip rule add fwmark 0x4/0x4 lookup main prio 1

# 启用 connmark
iptables -I PREROUTING -t mangle  -j CONNMARK --restore-mark           
iptables -I POSTROUTING -t mangle -j CONNMARK --save-mark 

iptables -t mangle -I PREROUTING -i pppoe-wan -m conntrack --ctstate NEW -j MARK --set-mark 0x1/0x1
iptables -t mangle -I PREROUTING 1 -i $WAN_IF -p tcp --dport $LOCAL_PORT -j MARK --set-mark 0x4/0x4
iptables -t mangle -I PREROUTING 1 -i $WAN_IF -p udp --dport $LOCAL_PORT -j MARK --set-mark 0x4/0x4
iptables -I FORWARD 1 -m mark --mark 0x4/0x4 -j ACCEPT
iptables -t nat -I PREROUTING 1 -i $WAN_IF -p tcp --dport $LOCAL_PORT -j DNAT --to $SS_IP:$SS_PORT
iptables -t nat -I PREROUTING 1 -i $WAN_IF -p udp --dport $LOCAL_PORT -j DNAT --to $SS_IP:$SS_PORT
iptables -t nat -I POSTROUTING 1 -m mark --mark 0x4/0x4 -j MASQUERADE

Tips

  • -t nat PREROUTING 里不能仅用 -m mark --mark 匹配作为条件。-j DNAT 要求必须指定协议和端口。(但 POSTROUTING 里的 SNAT / MASQUERADE 无此要求)
  • 如果路由器设置了全局翻墙,必须在路由表里添加中国IP直连规则。
  • 路由器需要配置 DDNS,然后在 ss 客户端里直接使用路由器的公网 IP 绑定的 DDNS 域名连接。

OpenWrt 版

OpenWrt 的路由器可以使用 zone_lan_prerouting 等 OP 防火墙自定义链表以简化规则:

SS_IP=1.2.3.4
SS_PORT=443
LOCAL_PORT=21
WAN_IF=pppoe-wan # WAN 网卡名

iptables -t mangle -I PREROUTING -i $WAN_IF -p tcp --dport $LOCAL_PORT -j MARK --set-mark 0x4/0x4
iptables -t mangle -I PREROUTING -i $WAN_IF -p udp --dport $LOCAL_PORT -j MARK --set-mark 0x4/0x4
iptables -I forwarding_wan_rule -m mark --mark 0x4/0x4 -j ACCEPT
iptables -t nat -I prerouting_wan_rule -p tcp --dport $LOCAL_PORT -j DNAT --to $SS_IP:$SS_PORT
iptables -t nat -I prerouting_wan_rule -p udp --dport $LOCAL_PORT -j DNAT --to $SS_IP:$SS_PORT
iptables -t nat -I postrouting_wan_rule -m mark --mark 0x4/0x4 -j MASQUERADE

内网访问

上面的适用于家庭路由器上的 shadowsocks 中继 iptables 规则有一个缺点:只能从公网访问。家庭局域网内通过 "路由器公网IP:转发端口" 这个地址是无法转发到远程 ss 服务器上的。

如果需要支持从家庭内网里也能直接通过 "路由器公网IP:转发端口" 访问 ss,还需要在路由器 wan 的 up / down 事件里执行自定义脚本以动态添加/删除 iptables 规则。对于 OpenWrt 路由器,参考脚本:

/etc/hotplug.d/iface/60-ssrelay (脚本文件名可自定义)

之所以必须用脚本是因为来自 LAN 的 PREROUTING 流量不能把所有 dport == LOCAL_PORT 的都当作是转发给SS_SERVER的。

注意脚本需要配合上面的防火墙规则使用(需要里面的 set mark 和 MASQUERADE 规则)。

#!/bin/sh

SS_IP=1.2.3.4
SS_PORT=443
LOCAL_PORT=21

if [ "$INTERFACE" != "pppoe-wan" ]
then
        exit
fi

case "$ACTION" in
ifup)
iptables-save | grep -v "__ssrelaylan__" | iptables-restore
IP=`ubus call network.interface.wan status | grep \"address\" | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}';`
iptables -t nat -I PREROUTING -i br-lan -p tcp -d $IP --dport $LOCAL_PORT -j DNAT --to $SS_IP:$SS_PORT -m comment --comment "__ssrelaylan__"
iptables -t nat -I PREROUTING -i br-lan -p udp -d $IP --dport $LOCAL_PORT -j DNAT --to $SS_IP:$SS_PORT -m comment --comment "__ssrelaylan__"
;;
ifdown)
iptables-save | grep -v "__ssrelaylan__" | iptables-restore
;;
esac

说明:

  • br-lan 是路由器 lan 虚拟网卡。
  • pppoe-wan 是路由器 wan 网卡。
  • 1.2.3.4:443 是 ss 服务器的 IP 和端口。
  • 脚本通过 iptables 规则的 comment 在宽带断开连接时批量清除所有旧规则。

内网访问 OpenWrt 版

/etc/hotplug.d/iface/60-ssrelay

#!/bin/sh

SS_IP=1.2.3.4
SS_PORT=443
LOCAL_PORT=21

if [ "$INTERFACE" != "pppoe-wan" ]
then
        exit
fi

case "$ACTION" in
ifup)
iptables-save | grep -v "__ssrelaylan__" | iptables-restore
IP=`ubus call network.interface.wan status | grep \"address\" | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}';`
iptables -t nat -I prerouting_lan_rule -p tcp -d $IP --dport $LOCAL_PORT -j DNAT --to $SS_IP:$SS_PORT -m comment --comment "__ssrelaylan__"
iptables -t nat -I prerouting_lan_rule -p udp -d $IP --dport $LOCAL_PORT -j DNAT --to $SS_IP:$SS_PORT -m comment --comment "__ssrelaylan__"
;;
ifdown)
iptables-save | grep -v "__ssrelaylan__" | iptables-restore
;;
esac

其他方式

可以利用 Haproxy 等 TCP 代理软件架设 shadowsocks 的中继服务器。不推荐,安装和配置麻烦,占用资源多,性能上也没有优势。


Last update: 2020-07-14 02:31:17 UTC