让 GCE 「科学上网」

Author Avatar
Yuuta 1月 03, 2019
  • 在其它设备中阅读本文章

博主前一段把服务从 DigitalOcean 迁移到了 GCE,但是遇到了一个非常恶心的问题——GCE 是不允许发邮件的。准确地说,它封了 25 端口的出站流量。作为一个 Linux 和网络小白感觉很无奈,求教了一些大佬后,才慢慢解决了这个问题,这里记录一下解决的过程。

注:本文是博主踩了一些坑子之后总结而成的,并非真实操作顺序,所以仅供参考。

前提

先说一下当前的环境:

  • GCE 机器一台,邮件服务跑在 Docker 内(下文称作 A 机

  • 美国另外一台小鸡,用于代理。(下文称作 B 机

  • 之前已经把 A 机的所有入站流量都走了 B 机代理(为了节省流量费),主要是通过 iptables 的方式:

    上图请忽略 125、1587、1465,由于它已经过时了,不是最新配置。所有端口都没有经过 Patch。

踩到的小坑

辣鸡 Google 毁我信仰 ,GCE 文档中对封杀邮件端口的描述 有偏差,害得我折腾了半天:

中文文档说封杀了三个端口

中文文档说封杀了三个端口

英文文档说只封杀了一个端口,并建议用其它端口或 VPN

英文文档说只封杀了一个端口,并建议用其它端口或 VPN

(其实只封杀了 25 的,Google 报道这么有偏差要负泽任

由于只封杀 25 的出站连接。所以我们的关注点就放在 25 出站上。

理清思路

现在,我们的思路分成两个部分:

接收邮件

接收邮件,就是客户端从其电脑上的一个端口向我们的某 POP3/IMAP 端口发起连接(并不是 25)。如果没有 B 机的代理,流程就是这样的:

上图采用 PowerPoint 制作。

流程非常简单。只要保证端口没有修改过,且接受客户端的入站流量就行。(请修改 GCE 防火墙设置)

我们可以使用 netcat 程式测试端口是否联通:

1
2
$ nc -zv mail.yuuta.moe 143
Connection to mail.yuuta.moe 143 port [tcp/imap2] succeeded!

这样的话,就可以接收邮件了。

加入代理 B 机

为了节省宝贵的流量,我们在中间加入 第三者 B 机做代理,那么流程就是这样的:

加入 B 机

流程依旧比较简单,仅需要进行 iptables 端口转发即可。这个以前已经做好了,这里贴出命令供参考:

1
2
3
4
# DNAT
$ iptables -t nat -A PREROUTING -d <B IP> -p tcp --dport <B port> -j DNAT --to-destination <A IP>:<A Port>
# SNAT
$ iptables -t nat -A POSTROUTING -d <A IP> -p tcp --dport <B port> -j SNAT --to-source <B IP>

别问我上面两行命令具体怎么回事, 都说了我不懂

这里解释一下参数,B IP 就是代理机 B 的 IP,可以用 curl 自动获取以节省时间:

1
$ B_IP=$(curl -s ifconfig.co)

A IP,顾名思义就是 A 机器的 IP。而 B PORT 是 B 机暴露给客户端的 Port(上图 B 机左侧箭头的端口),不能改。最后的 A PORT 就是 A 机暴露给 B 机的端口,可以随便改。不过为了方便,我还选用同样端口。

再次测试端口可用性即可,不出意外是可用的。

发送邮件

接收邮件弄完之后,就可以看看怎么发邮件了。发邮件主要是通过 A 机的邮件程式向收件人的 25 端口发起连接,差不多这样:

发邮件流程(不考虑 B 机代理)

就是这么简单,我们可以用 netcat 测试一下 Gmail 的 25 是不是通的:

1
2
$ nc -zv smtp.gmail.com 25
Connection to smtp.gmail.com 25 port [tcp/smtp] succeeded!

可见是通的。不过到了万恶的 GCE 手下,就不行了。

搭建 Shadowsocks 服务

我的解决方案主要是在 A 机和 B 机之间搭建一个 VPN 网络,将 A 的 25 出站请求代理到 B 上,从而实现突破。最终流程如下:

有没有注意到,和上面(收邮件)部分的箭头相反了?没错,这里是从 A 到 B,而不是以往的从 B 到 A。

让 SS 只代理 25

由于几乎没有门槛,搭建 SS 服务直接略过。这里只讲一下如何在 A 上让 25 的出站流量走 SS,其它不走。

shadowsocks-libev 提供的 ss-rediriptables 做透明代理可以解决这个问题,那么就开始吧。

使用这个命令在后台运行 ss-redir

1
$ ss-redir -v -c <config json> -f <pid file>

然后用 iptablesOUTPUT 链加入 REDIRECT

1
$ iptables -t nat -A OUTPUT -p tcp -m tcp --dport 25 -j REDIRECT --to-port <SS Redir port>

上面的 25 就是被代理的端口,而 SS Redir port 是 SS 配置文件中的 local_port

执行后就可以使用 25 端口了,可以用 netcat 自行测试一下。

解决 Docker 问题

在 Host 上成功破解 25 后,发现 Docker 内的邮件程式还是用不了。原来只设定 OUTPUT 链的话对 Docker 是无效的,Haruue 大佬建议我给 PREROUTING 也加上这个 Rule。可是加入后,Docker 内测试由原来的卡死变成了 Connection refused?????

喵喵喵???

最终,用了大佬给的方案,直接把 SS 和 iptables 设定放在 Docker container 内部。这确实是一个好方案,无需对 Host 进行修改,不过还得自己改 Image。

个人原本想用共享 Network 的方式,在另一个 Container 内改 iptables,发现并不生效,于是做出如下 Dockerfile,不需要多解释了吧(Talk is cheap, show me the code. – Linus):

1
2
3
4
5
6
7
8
9
10
11
FROM tvial/docker-mailserver
ADD entry.sh /app/entry.sh
WORKDIR /app
RUN chmod a+x /app/entry.sh && \
sh -c 'printf "deb http://httpredir.debian.org/debian jessie-backports main" > /etc/apt/sources.list.d/jessie-backports.list' && \
apt update && \
apt install -t jessie-backports -y shadowsocks-libev && \
apt install -y iptables && \
rm -rf /var/cahce/apt && \
rm -rf /var/lib/apt/lists
CMD [ "/app/entry.sh" ]

和相应 Shell 脚本:

1
2
3
4
5
6
7
#!/bin/sh
echo "Redirecting 25"
iptables -t nat -A OUTPUT -p tcp -m tcp --dport 25 -j REDIRECT --to-port 7890
ss-redir -v -c /app/ss.json -f ss_daemon_pid
echo "Starting original service"
# 这里的命令来自原 Mail image。
supervisord -c /etc/supervisor/supervisord.conf

便可给 Docker 内的程式科学上网了。

总结

从换 VPS 到现在彻底解决邮件问题已经过去快半个月了,能解决对于博主这种小白来说还是很兴奋的。最后直接贴出相应脚本,方便伸手党取用:

从 B 到 A,端口转发处理用户请求

1
2
$ iptables -t nat -A PREROUTING -d <B IP> -p tcp --dport <B port> -j DNAT --to-destination <A IP>:<A Port>
$ iptables -t nat -A POSTROUTING -d <A IP> -p tcp --dport <B port> -j SNAT --to-source <B IP>

透明代理指定端口

1
2
$ ss-redir -v -c <config json> -f <pid file>
$ iptables -t nat -A OUTPUT -p tcp -m tcp --dport 25 -j REDIRECT --to-port <SS Redir port>

好了,今天份的水博客就这么多(是 2019 第一篇诶…..


References

参考如下资料:

特别感谢 Haruue Icymoon 提供关于 Iptables、Linux 和其它方面的宝贵经验。

本文为原创文章,谢绝转载。如果对您有帮助,欢迎不吝打赏 yuuta.moe/#donate
本文链接:https://blog.yuuta.moe/2019/01/03/send-mail-on-gce/