背景
以前服务器都是直接配置LNMP环境,最近手头正好有一台需要重新配置,想尝试使用docker来配置多PHP版本环境。docker配置十分顺利,感谢有明大佬的小册,和DNMP项目。
待解决
服务器配置好后,在几天的试用过程中,发现如下两个问题:
- NGINX PHP容器中无法获取request的真实IP
- PHP容器中无法访问公网
过程
无法访问公网,无法获取真实IP,首先想到了防火墙的问题;关闭iptables,问题解决。可是iptables不能关呀,虽说现在云服务器都有安全组过滤,但防火墙是最后一道防线,不能在云厂商的怀抱里裸奔啊……
查看了docker的网络部分,docker暴露容器端口是由docker-proxy来实现的,至于docker-proxy是什么略过不表。肯定是iptables影响了docker-proxy导致数据包的源ip发生了改变,无法获取真实IP;无法访问公网,则是数据包找不到出口,被iptables拦截掉了,在内网转圈圈
- 既然docker的各容器处于内网中,于是我想到了iptables的转发功能。
解决
-
假设我的公网IP为 117.25.140.71
-
NGINX容器内网IP为 172.18.0.2
-
PHP容器内网IP为 172.18.0.3 172.19.0.3
把公网来的数据包直接转发给NGINX容器,跳过docker-proxy
添加如下规则
-A PREROUTING -d 117.25.140.71 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:80
-A PREROUTING -d 117.25.140.71 -p tcp -m tcp --dport 443 -j DNAT --to-destination 172.18.0.2:443
复制代码
重启iptables 无法获取真实IP问题解决
将内网的数据包转发到公网
我的PHP容器访问公网,需求大抵是发送短信之类(无邮件发送),用的都是http协议,所以这里我没有选择全部转发,而是只转发目的端口为80和443的数据包
添加如下规则
-A POSTROUTING -s 172.19.0.3 -p tcp -m tcp --dport 80 -j SNAT --to 117.25.140.71
-A POSTROUTING -s 172.19.0.3 -p tcp -m tcp --dport 443 -j SNAT --to 117.25.140.71
复制代码
重启iptables发现仍然无法访问公网,是不是落下了什么。。 对了,既然http协议访问其他域名提供的api服务,怎么能少了DNS解析呢?添加DNS的支持(注意:DNS解析使用的是udp协议)
补充如下规则
-A POSTROUTING -s 172.19.0.3 -p udp -m udp --dport 53 -j SNAT --to 117.25.140.71
复制代码
重启iptables,大功告成!
最后总结几点:
- 之所以能用宿主机iptables实现转发,是因为宿主机是内网的网关
- 我限制了PHP容器访问公网的目的端口,如果需要发送邮件或者访问8080等其他端口,仍需单独添加规则或者不限制具体端口号
- 转发内网数据包到公网,不要使用地址伪装(MASQUERADE),否则会影响本文中添加的PREROUTING规则,仍然无法获取request的真实IP(使用其他方法除外)
- 如果直接service iptables stop,这篇文章就不用看了。。。
所有评论(0)