只需设置一个 ElasticSearch 容器,与公司的 Laravel 应用程序一起使用即可。创建 docker-compose.yml 并运行它是完美且直接的,但当我想对此进行防火墙保护,以便只能从所述 Laravel 应用程序的一个特定 IP 访问它时,就会出现问题。
在研究过程中,我注意到很多人都遇到了这些问题,即 Docker 转发的端口流量完全暴露给公众,并且他们无法正确地对其进行防火墙保护。
我找到了几个解决方案在过去的 6 小时内,没有一个工作正常。我认为这与 Docker 处理/转发入站流量的方式有关,而且我对 iptables 的了解并不多,因此我无法自己理解发生了什么。
这是我的 docker-compose.yml (物有所值):
version: '3.4'
services:
elasticsearch:
image: khezen/elasticsearch:6.1.1
container_name: elasticsearch
environment:
NETWORK_HOST: 0.0.0.0
HOSTS: 127.0.0.1
CLUSTER_NAME: fd-es
NODE_NAME: node-1
HEAP_SIZE: 31g
HTTP_SSL: "true"
ELASTIC_PWD: "somepasswd"
HTTP_CORS_ENABLE: "true"
HTTP_CORS_ALLOW_ORIGIN: /https?:\/\/localhost(:[0-9]+)?/
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./config/elasticsearch.yml:/elasticsearch/config/elasticsearch.yml
- ./data:/elasticsearch/data
- ./logs:/elasticsearch/logs
ports:
- 9200:9200
networks:
- es-network
restart: always
networks:
es-network:
driver: bridge
这些是我当前使用的 iptables 规则,它们在某种程度上是我想要的,但来自任何客户端到端口 9200 的所有流量仍然可以通过,而不仅仅是从我的应用程序访问:
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [779:162776]
:DOCKER - [0:0]
-A DOCKER -s xxx.xxx.xxx.xxx -p tcp -m tcp --dport 9200 -j ACCEPT
-A DOCKER -o docker0 -p tcp -m tcp --dport 9200 -j ACCEPT
-A DOCKER -p tcp --dport 9200 -j DROP
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p udp -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p tcp -m tcp --dport 44344 -j ACCEPT
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable
-A INPUT -j DROP
我尝试禁用 Docker 的 iptables 支持并禁用桥接网络,并调整了 iptables 规则几十次,但都无济于事。
我将非常感激任何建议和帮助,因为我对这个问题没有想法和搜索结果。
提前致谢!
答案1
对于每个寻求此问题解决方案的人来说,答案是这样的:
*filter
:INPUT ACCEPT [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:FILTERS - [0:0]
:DOCKER-USER - [0:0]
-F INPUT
-F DOCKER-USER
-F FILTERS
-A INPUT -i lo -j ACCEPT
-A INPUT -p icmp --icmp-type any -j ACCEPT
-A INPUT -j FILTERS
-A DOCKER-USER -i ens33 -j FILTERS
-A FILTERS -m state --state ESTABLISHED,RELATED -j ACCEPT
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
-A FILTERS -j REJECT --reject-with icmp-host-prohibited
COMMIT
在这里找到:https://unrouted.io/2017/08/15/docker-firewall/
运行完美。
答案2
我将解释我已经测试过的您想要实现的目标的场景。
我启动了一个 Docker 容器,其中端口 9010 转发到端口 8080:
docker run -p 9010:8080 swaggerapi/swagger-editor
Docker 为 PREROUTING 链创建 DNAT 规则,将流量从端口 9010 转发到端口 8080:
DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:9010 to:172.17.0.5:8080
Docker 还在 DOCKER 链中创建一条规则,允许发送到容器的 IP 地址和端口 8080 的所有流量。
ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.5 tcp dpt:8080
DOCKER 链用于 FORWARD 链,其中发送到 docker0 网桥的所有流量都经过 DOCKER 链中的规则处理。
DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
现在我想过滤发送到端口 8080 的流量(发送到端口 9010 的流量已由 PREROUTING 处理,现在是发送到端口 8080 的流量),阻止所有 IP 地址,同时允许来自 IP 192.168.1.142 的流量。当然,可以将容器的 IP 地址添加到这些规则中以增加粒度。
我在开头添加了以下规则向前链。或者,你可以用 DOCKER 替换 FORWARD。
iptables -I FORWARD -p tcp --dport 8080 -j DROP
iptables -I FORWARD -p tcp -s 192.168.1.142 --dport 8080 -j ACCEPT
由于这些规则,只有 IP 192.168.1.142 可以访问容器使用的 8080 端口。
对于那些访问此答案的人,如果您只想允许一个特定的 IP 地址访问容器,请使用Docker 文档。
答案3
我认为添加这一行可以解决你的问题:
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [779:162776]
:DOCKER - [0:0]
**-A INPUT -p tcp -m tcp --dport 9200 -j DOCKER**
-A DOCKER -s xxx.xxx.xxx.xxx -p tcp -m tcp --dport 9200 -j ACCEPT
答案4
我对接受的答案有一点小问题,那就是它允许访问容器端口,而不考虑已发布的端口。理想情况下,它会起作用,您可以执行以下操作:-p 80:80
或-p 8080:8080
。
如果出现这种情况-p 8080:80
,您必须遵循此处的评论:https://github.com/moby/moby/issues/22054#issuecomment-466663033
基本上,NAT 发生在过滤之前,因此--dport
不起作用,就像这个人问的那样:https://github.com/moby/moby/issues/22054#issuecomment-426655503
因此,我修改了接受的答案如下,按照这个例子:
*filter
:INPUT ACCEPT [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:FILTERS - [0:0]
:DOCKER-USER - [0:0]
-F INPUT
-F DOCKER-USER
-F FILTERS
# BASIC Allow
-A INPUT -i lo -j ACCEPT
-A INPUT -p icmp --icmp-type any -j ACCEPT
# Chain to FILTERS
-A INPUT -j FILTERS
-A DOCKER-USER -i eth0 -j FILTERS
# ALLOW outgoing traffic
-A FILTERS -m state --state ESTABLISHED,RELATED -j ACCEPT
# COMMON FIREWALL RULES
# ALLOW SSH ON THE HOST
-A FILTERS -p tcp -m multiport --dports 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# ALLOW the following ports on the containers as well.
-A FILTERS -p tcp -m conntrack --ctstate NEW,ESTABLISHED --ctorigdstport 80 -j ACCEPT
-A FILTERS -p tcp -m conntrack --ctstate NEW,ESTABLISHED --ctorigdstport 443 -j ACCEPT
# DENY something
#-A FILTERS -p icmp --icmp-type echo-request -j REJECT
# FINAL REJECT
-A FILTERS -j REJECT
COMMIT
注意事项
(因为为什么不呢)
如果您在主机级别允许一个端口,如我上面允许 SSH 的示例一样。
请注意,这将允许在同一端口上运行的任何容器(无论发布的端口是什么)可供所有人访问。
例如:
如果我允许端口 8080,您可以通过运行 ncat 容器进行测试,docker run --rm -p 8081:8080 subfuzion/netcat -l 8080
然后从另一台机器使用 ncat,ncat <docker container host public ip>:8081
您将看到连接通过。
一个解决方法可能是(我还没有测试过)只允许使用端口,--ctorigdstport
但我不确定它是否会将你锁定在你的机器之外,就像这样(这没有经过测试):
-A FILTERS -p tcp -m conntrack --ctstate NEW,ESTABLISHED --ctorigdstport 22 -j ACCEPT