中国区 AWS 透明翻墙网关的搭建 (未完待续)

多年未更新 blog,今天偶然打开,看到副标题 thoughs worth sharing 深感惭愧,最近繁忙的工作也告一段落,我想是时候该重新拿起博客了,毕竟分享是我改不掉的坏习惯。

背景

中国区 AWS 无论是在光环新网(北京)还是西云数据(宁夏)到海外的链路都十分糟糕,当我们需要在中国区 AWS 上搭建服务,尤其是需要使用到 Google/Github 的依赖时会十分的痛苦,此时就需要一个透明网关,当满足条件时走代理,否则走 AWS 的默认出口。

其中海外机器的条件比较苛刻,如非向电信申请专门的白名单线路,我们在海外的代理服务器很有可能随时被墙,所以本文选择的方案有几个关键需求:

  1. 给定一组域名,比如 proxy1.mydomain.com / proxy2.mydomain.com / … 通过 DDNS 或者人肉的方式使得可以自动更新代理服务器。
  2. 透明翻墙网关的软件需要有 failover 的特性,即代理失效时可以自动进行转移,或者有负载均衡使得所有的 endpoints 均为有效代理。
  3. 为了可以使用廉价的代理订阅服务(假设不是强安全需求),软件需要支持 Shadowsocks/V2Ray 等多种协议。
  4. UDP 也要能完美代理,最愉悦的方式自然是走 tun。

本文意在提供一个开箱即用的解决方案,即复制粘贴即可使用,对其中细节不会深入解释,具体可自行了解。

网络拓扑

配置步骤

根据需求描述,我们这里使用 Clash,注意原作者的版本和我们这里使用的版本并不一样,原作者的 tun 部分实现并未开源,我们暂不考虑非开源方案,这里选用 comzyh 的修改版 [2], 神奇的是,有一个非常漂亮的 one-click 的脚本可以协助我们完成整个过程。

首先我们使用 Ubuntu Server 20.04 LTS (HVM) 这个 AMI (ami-04effa29f4d91541f),笔者尝试过 Amazon Linux 2 AMIUbuntu Server 18.04 LTS (HVM),均出现一些莫名其妙的问题,应该是 AWS 对这俩镜像在网络部分有过魔改导致的,并且 Amazon Linux 2 的 systemd 的版本很神奇,没法直接使用下面这个脚本,系统的关系并不是很大,这里直接选择一个能用的,即 Ubuntu 20.04 这个镜像。

准备配置文件

启好 EC2 以后,连接,新建 /srv/clash 文件夹,新建配置文件 /srv/clash/config.yaml,这里给出一个参考配置,有细力度需求可参考 Clash 的相关文档。

mode: Rule
log-level: info
dns:
  enable: true
  ipv6: false
  enhanced-mode: redir-host
  listen: 0.0.0.0:53
  nameserver:
    - 1.2.4.8
    - 114.114.114.114
    - 223.5.5.5
    # - 内网服务 DNS
  fallback:
    - tls://1.0.0.1:853
    - tls://dns.google:853
proxies:
  - name: "ss1"
    type: ss
    server: "server1.yourproxyserver.com"
    port: "1080"
    cipher: aes-256-gcm
    password: "yourpassword"
    udp: true
  - name: "ss2"
    type: ss
    server: "server2.yourproxyserver.com"
    port: "1080"
    cipher: aes-256-gcm
    password: "yourpassword"
    udp: true

proxy-groups:
  - name: "Proxy"
    type: select
    proxies: ["ss1", "ss2"]

tun:
  enable: true
  device-url: dev://clash0
  stack: system

rules:
  - DOMAIN-SUFFIX,local,DIRECT
  - IP-CIDR,127.0.0.0/8,DIRECT
  - IP-CIDR,172.16.0.0/12,DIRECT
  - IP-CIDR,192.168.0.0/16,DIRECT
  - IP-CIDR,10.0.0.0/8,DIRECT
  - IP-CIDR,17.0.0.0/8,DIRECT
  - IP-CIDR,100.64.0.0/10,DIRECT

  # Final
  - GEOIP,CN,DIRECT
  - FINAL,Proxy

并且下载 IP 地理位置数据库[4] 到 /srv/clash 下

部署 Clash

注意由于我们的目的是将 Clash 作为透明网关的服务使用, 由于 Clash 自带有 DNS 服务, 所以我们可能要关闭掉系统原有的占用 53 端口的 DNS 服务, 具体系统具体分析, 这里不再赘述.

找个地方如 /tmp 下,依次执行

git clone https://github.com/Kr328/clash-tun-for-linux
cd clash-tun-for-linux && chmod +x *
./install.sh build # 由于墙的原因,建议本地编译好上传到服务器或者直接下作者 prebuilt 的版本[3],改名为 clash 放在该目录下
sudo ./install install 
sudo systemctl daemon-reload && sudo systemctl enable clash-tun && sudo systemctl start clash-tun

systemd 会在启动 clash 之前自动执行路由表的配置,默认内网流量不过 tun,大致上长这样 (我这里执行 ip rule list 的结果)

0:	from all lookup local
32758:	from all uidrange 65534-65535 goto 32766
32759:	from all to 172.31.255.253/30 goto 32767
32760:	from all to 127.0.0.0/8 goto 32766
32761:	from all to 10.0.0.0/8 goto 32766
32762:	from all to 192.168.0.0/16 goto 32766
32763:	from all to 224.0.0.0/4 goto 32766
32764:	from all to 172.16.0.0/12 goto 32766
32765:	from all lookup 354
32766:	from all lookup main
32767:	from all lookup default

此时,Clash 的部署就完成了。接下来我们要考虑按照设计好的网络拓扑在 AWS 进行配置,将其作为一个 NAT 网关。

AWS 配置

本文考虑到受众用户不同,所以采用在 AWS 的 WebUI 下进行操作,读者可使用 aws-cli 进行配置或将整套结构使用 Cloudformation 进行部署。

创建子网

配置网络接口

路由表及防火墙配置

首先我们打开 NAT 实例上的转发

sudo iptables -t nat -A POSTROUTING -j MASQUERADE

保存防火墙配置并且每次重启时应用,首先创建 /etc/network/if-pre-up.d/iptables,并记得给可执行权限

#!/bin/bash

iptables-restore < /etc/iptables.rules

为了能让其在网络 Ready 以后运行,创建 systemd 的 daemon 配置, 新建文件 /etc/systemd/system/iptables-rules.service 写入以下内容

[Unit]
Description = Apply iptables rules 

[Service]
Type=oneshot
ExecStart=/etc/network/if-pre-up.d/iptables

[Install]
WantedBy=network-pre.target

然后让 systemd 的配置生效

sudo systemctl daemon-reload
sudo systemctl enable iptables-rules
sudo systemctl start iptables-rules

然后便是网卡配置

首先要抱怨一下 Ubuntu 的 netplan 真是个大坑, 新加接口以后如果配置从 DHCP 来,则会默认添加 default 路由,而且 Ubuntu 20.04 的 netplan 版本过老不支持 use-routes: false 选项来屏蔽这个行为,所以只能 workaround 这个事情了

首先我们添加一个接口配置, 新建文件 /etc/netplan/60-nat.yaml

network:
    ethernets:
        ens6:
            dhcp4: true
            dhcp4-overrides:
                use-routes: false
            dhcp6: false
            match:
                macaddress: 02:45:b0:69:f4:8c
            set-name: ens6
    version: 2

其中 ens6 是之前创建的网络接口 (eni-XXXXX) 对应在 NAT Instance 上的网卡名称,如果你是第一次附加网络接口,一般为 ens6,如果有多个附加则具体情况具体分析。

为了解决会自动增加 default 路由的问题,笔者这里采用了一个非常 dirty 的方案,即将下列内内容写到 /etc/networkd-dispatcher/routable.d/ 里[6],并给予可执行权限

#!/bin/sh

# Only remove the default route on the second interface, e.g. eth1
[ "$IFACE" != ens6 ] && exit 0

# delete the default route for this interface
ip route del default dev ens6

至此所有的配置均以结束。

参考

  1. https://docs.aws.amazon.com/zh_cn/vpc/latest/userguide/VPC_NAT_Instance.html
  2. https://github.com/comzyh/clash
  3. https://github.com/comzyh/clash/releases/download/20200510/clash-linux-amd64
  4. https://github.com/Dreamacro/maxmind-geoip/releases/latest/download/Country.mmdb
  5. https://github.com/Kr328/clash-tun-for-linux
  6. https://unix.stackexchange.com/questions/517995/prevent-netplan-from-creating-default-routes-to-0-0-0-0-0

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s