如何使用 IPtables 阻止 (几乎) 所有非 VPN 流量到 Docker 服务?

如何使用 IPtables 阻止 (几乎) 所有非 VPN 流量到 Docker 服务?

我有一台服务器,只希望通过 SSH 在公共互联网上访问,但我希望它能够连接到 VPN,并将服务绑定到其 VPN 接口。

我正在使用 Linode 来托管 Ubuntu 16.04.2 LTS VM、使用 ZeroTier 作为我的 VPN 层,并使用 Swarm 模式(只有一个节点)的 Docker 来提供服务。

ifconfig -a在 VM 上运行将返回此结果(为保护隐私,公共 IP 已被删除):

docker0   Link encap:Ethernet  HWaddr 02:42:f7:e7:e6:1e
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

docker_gwbridge Link encap:Ethernet  HWaddr 02:42:ad:82:56:0f
          inet addr:172.18.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:adff:fe82:560f/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6083 errors:0 dropped:0 overruns:0 frame:0
          TX packets:16927 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:6364682 (6.3 MB)  TX bytes:49725061 (49.7 MB)

eth0      Link encap:Ethernet  HWaddr f2:3c:91:a7:c3:ac
          inet addr:<REDACTED>  Bcast:<REDACTED>  Mask:255.255.255.0
          inet6 addr: <REDACTED>/64 Scope:Global
          inet6 addr: <REDACTED>/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:94675 errors:0 dropped:0 overruns:0 frame:0
          TX packets:47778 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:59390529 (59.3 MB)  TX bytes:14765979 (14.7 MB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:54171 errors:0 dropped:0 overruns:0 frame:0
          TX packets:54171 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:6243573 (6.2 MB)  TX bytes:6243573 (6.2 MB)

veth8323309 Link encap:Ethernet  HWaddr 82:b3:ec:d8:8c:9b
          inet6 addr: fe80::80b3:ecff:fed8:8c9b/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:15 errors:0 dropped:0 overruns:0 frame:0
          TX packets:538 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:1248 (1.2 KB)  TX bytes:22932 (22.9 KB)

veth602daf7 Link encap:Ethernet  HWaddr 82:7e:cf:b3:cb:a4
          inet6 addr: fe80::807e:cfff:feb3:cba4/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3065 errors:0 dropped:0 overruns:0 frame:0
          TX packets:11154 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:6187902 (6.1 MB)  TX bytes:794193 (794.1 KB)

zt0       Link encap:Ethernet  HWaddr 42:08:c2:e8:fc:93
          inet addr:192.168.196.106  Bcast:192.168.196.255  Mask:255.255.255.0
          inet6 addr: fe80::4008:c2ff:fee8:fc93/64 Scope:Link
          inet6 addr: fc7b:7a95:194:6f84:bf9a::1/40 Scope:Global
          UP BROADCAST RUNNING MULTICAST  MTU:2800  Metric:1
          RX packets:442 errors:0 dropped:0 overruns:0 frame:0
          TX packets:279 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:52165 (52.1 KB)  TX bytes:46603 (46.6 KB)

我的 Docker 撰写文件如下所示:

version: "3"
services:
  jenkins:
    image: jenkins/jenkins:lts
    ports:
      - "80:8080"
    volumes:
      - jenkins_home:/var/jenkins_home
    deploy:
      replicas: 1
volumes:
  jenkins_home:

但是,我似乎无法正确设置 iptables 规则。我原本希望能够这样做:

# Allow established connections, including outbound connections this server initiated, to continue to receive replies
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow SSH on eth0
iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT

# Allow UDP 9993 on eth0 (required for ZeroTier)
iptables -A INPUT -i eth0 -p udp --dport 9993 -j ACCEPT
iptables -A OUTPUT -p udp --dport 9993 -j ACCEPT

# Reject everything that hasn't already been accepted (rules are applied in order)
iptables -A INPUT -i eth0 -j REJECT

但是,当我部署并启动 Docker Swarm 服务时,我最终能够从不在 VPN 上的远程主机通过 eth0 上的地址访问该服务,从 VPN 上的远程主机通过 zt0 上的 VPN 地址访问该服务,但是不是来自虚拟机本身的本地主机。

我在这里做错了什么?如何获取 iptables 规则以阻止来自公共互联网的流量但仅允许来自 ZeroTier VPN 的流量?

答案1

经过进一步研究,我发现了我的问题。

其要点是 Docker 使用自己的链来转发数据包。Docker 守护进程启动时以及部署新容器时,它会修改DOCKERDOCKER-INGRESS链,但不会修改DOCKER-USER用于管理员定义的防火墙规则(我想要应用的那种类型)的链。

有了这些知识,我就能通过以下 iptables 规则得到我想要的东西:

#!/bin/sh

set -e
set -u

# REMINDER: DROP vs REJECT:
# a DROP operation blackholes the packet, likely causing client to wait dozens of seconds for a timeout
# a REJECT operation sends an ICMP response letting the client know the packet was rejected

# REMINDER: IPv4 vs IPv6
# `iptables` only applies IPv4 rules; `ip6tables` only lapplies IPv6 rules.

## System firewall rules

# Allow any established sessions from our container host - including replies to outbound queries - to receive traffic
iptables --append INPUT -m conntrack --ctstate ESTABLISHED,RELATED --jump ACCEPT
ip6tables --append INPUT -m conntrack --ctstate ESTABLISHED,RELATED --jump ACCEPT

# Allow SSH on eth0
iptables --append INPUT --in-interface eth0 --protocol tcp --dport 22 --jump ACCEPT
ip6tables --append INPUT --in-interface eth0 --protocol tcp --dport 22 --jump ACCEPT

# Allow UDP 9993 on eth0 (required for ZeroTier)
iptables --append INPUT --in-interface eth0 --protocol udp --dport 9993 --jump ACCEPT
ip6tables --append INPUT --in-interface eth0 --protocol udp --dport 9993 --jump ACCEPT
iptables --append OUTPUT --protocol udp --dport 9993 --jump ACCEPT
ip6tables --append OUTPUT --protocol udp --dport 9993 --jump ACCEPT

# Reject everything to eth0 that hasn't been accepted by a previous rule
iptables --append INPUT --in-interface eth0 --jump REJECT
ip6tables --append INPUT --in-interface eth0 --jump REJECT

## Docker firewall rules

# NOTE: Docker does not enable IPv6 networking by default, so there is no need for `ip6tables` here
# <https://docs.docker.com/engine/userguide/networking/default_network/ipv6/>

# NOTE: Docker promises not to modify the DOCKER-USER chain so that we can do this type of filtering

# Create the chain we need if Docker hasn't done so already
iptables --new DOCKER-USER 2>/dev/null || true

# Allow any established sessions from our containers - including replies to outbound queries - to receive traffic
iptables --append DOCKER-USER --match conntrack --ctstate ESTABLISHED,RELATED --jump ACCEPT

# Reject packets that would be forwarded to Docker when they come in over eth0 (the public interface)
iptables --append DOCKER-USER --in-interface eth0 ---jump REJECT

请注意,其中一些规则并非特定于 Docker,而是用于拒绝发往我的 Docker 服务器的数据包 - 我只想接受发往服务器本身的 VPN 和 SSH 数据包。

相关内容