从一台没有外网连接的阿里云主机上访问微信API

1. 背景

公司的生产环境是部署在阿里云上的。Zabbix最初装在一台没有外网连接的ECS上,通过Nginx进行访问。但是后来发现如果需要报警,需要从Zabbix这台主机访问微信公众号服务器(qyapi.weixin.qq.com)。运维的兄弟最初选择了固定带宽的网络,但是考虑到费用问题,后期想切换到按量付费的网络,但是阿里云居然不支持这种切换(这里吐槽一下阿里的设计)。

由于该主机是按照包月包年方式购买的,退掉重新申请主机安装Zabbix显然不划算。在主机到期之前,必须想办法实现在没有外网的情况下访问微信服务器(考虑到费用问题,原先固定带宽已经被修改成了0M,也就没有费用了)。

2. 解决

考虑到整个生产环境中有多台能够访问Internet的主机,所以自然的想到是不是可以通过其他主机中转这种访问呢?

2.1 Nginx

最先想到的方案是通过Nginx,通过Nginx做一个正向代理,代理到微信公众号服务器( https://qyapi.weixin.qq.com ),然后通过从Zabbix访问Nginx代理后的站点。但是通过搜索发现,Nginx不支持这种代理方式(Forward Proxy)连接https服务器。具体的可以看附录中的连接。以下是Nginx作者的答复:

1
2
> Is there any schedule to support the feathure, forward proxy ?
Not in near future: there is alreay good forward proxy Squid.

2.2 iptables

然后又想到是不是可以使用iptables进行端口转发呢?但是可能是我对iptables还不够熟悉,按照附录4的命令总也调试不成功,总是会返回超时。这个方法没有解决问题。

2.3 socat

这时想起以前用过一个小工具socat,用它将一个unix socket转换成一个tcp socket,它的用处就是将一个流转换成另一个流。经过尝试果然可以。命令也很简单:

1
socat TCP-LISTEN:443,fork,reuseaddr TCP:140.207.127.79:443

然后在Zabbix主机上使用curl访问试试:

1
2
root@prd-zabbix:~# curl https://192.168.61.64
curl: (51) SSL: certificate subject name 'qy.weixin.qq.com' does not match target host name '192.168.61.64'

已经可以访问了,说明服务已经中转到微信的服务器上了。

3. 后续操作

3.1 域名

上面使用curl出现的提示表明,我们必须使用域名进行访问才能够被正确处理。这一点可以通过本地域名映射进行解决。可以通过修改/etc/hosts增加映射的方式解决。因为我们本地的Zabbix是通过Docker运行的,这个修改就要通过Docker运行。我们使用的是docker-compose,在docker-compose.yaml中增加如下配置即可:

1
2
extra_hosts:
- "qyapi.weixin.qq.com:192.168.61.64"

这样,在Zabbix运行的容器中,域名qyapi.weixin.qq.com指向了192.168.61.64这台主机。而对这台主机上的443端口的访问被重定向到了140.207.127.79:443,从而实现了对微信公众号服务器的访问。

3.2 多服务器

使用nslookup查询可以知道对应于域名qyapi.weixin.qq.com,微信是提供了多台服务器的:

1
2
3
4
5
6
7
8
9
10
root@prd-zabbix:~# nslookup qyapi.weixin.qq.com
Server: 100.100.2.138
Address: 100.100.2.138#53

Non-authoritative answer:
qyapi.weixin.qq.com canonical name = qy.weixin.qq.com.
Name: qy.weixin.qq.com
Address: 140.207.189.106
Name: qy.weixin.qq.com
Address: 140.207.127.79

上面的解决方案中只指向了一台服务器,这样如果这台服务器出现问题的时候可能会导致访问失败。可以考虑的一个方案是在另一台主机上使用socat做同样的转发工作,只是转发给另一台主机。这样应该可以解决部分问题。但是还不是最完美的解决方案。有待于以后继续研究。

3.3 自启动

需要考虑服务器重启的情况下,要自动启动socat。这个在Ubuntu上可以通过修改/etc/rc.local文件解决:

1
2
socat TCP-LISTEN:443,fork,reuseaddr TCP:140.207.127.79:443 &
exit 0

附录A. 参考资料

热评文章