连接到 VPN 时 Debian iptables 配置错误

连接到 VPN 时 Debian iptables 配置错误

总结

Debian 路由器工作正常,直到我连接 VPN,然后 iptables(或路由表)中的一些错误配置导致路由器本身无法通过 tun0 ping IP/域,因此无法通过 DNS 服务器。我需要路由器通过 tun0(WAN)ping 外部 IP,然后它才能完全正常运行。

我的目标

因此,在尝试了原厂路由器固件,然后尝试了 DD-WRT 等之后,我得出的结论是,我需要一台在定制的 MicroATX PC 上直接运行 Debian 的路由器,因为我将它用作整个网络的 VPN 路由器。我想要标准 VPN Linux 应用程序(在本例中为 NordVPN)的真正自动连接功能,因为任何标准路由器固件都要求我选择一个服务器,如果我想要不同的服务器,我必须手动切换它。

我目前所做的

我成功地将它作为路由器运行,WAN 和 LAN 接口设置正确(或至少功能正常)。我使用 DNSMasq 和 iptables,它运行良好。然后我尝试在我的 iptables 中实现 VPN(tun0 作为 WAN),它有点作品。

问题

当我连接VPN并运行后sudo service netfilter-persistent reload,我从 LAN 接口上的机器 ping IP 地址非常顺利。我不能ping 域名,所以我推断这只是一个 DNS 问题。其实问题比这更深刻一些,因为在调试过程中我发现当连接到 VPN 时,我的路由器无法 ping 通其 WAN 子网之外的 IP/域。这可以解释为什么我无法在 LAN 网络上的任何机器上使 DNS 工作,除非我在该机器上手动设置 DNS(这使其 100% 正常运行)。

我的问题

我搞砸了 iptables (或其他任何东西) 的哪一部分,导致这种情况发生?我需要添加/删除什么才能允许路由器 (本地主机) ping 外部世界,以便当它充当 DNS 服务器时,客户端计算机可以正确满足请求?

iptables

请注意,我想允许在 VPN 开启或关闭的情况下进行路由,因此有一些重复的规则(我还不完全理解 iptables)。另请注意,我有一些未经测试端口转发规则已到位。欢迎提供相关提示,但如果我遇到问题,我可能应该单独提问。

# WAN interface: eno1
# VPN interface: tun0
# LAN interfaces: enp3s0, enp4s0

*filter
# Allow all outgoing, but drop incoming and forwarding packets by default
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-I OUTPUT -o tun0 -j ACCEPT
-A OUTPUT -o eno1 -j ACCEPT

# Custom per-protocol chains
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]

# Acceptable UDP traffic

# Acceptable TCP traffic

# Acceptable ICMP traffic

# Boilerplate acceptance policy
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT

# Drop invalid packets
-A INPUT -m conntrack --ctstate INVALID -j DROP

# Pass traffic to protocol-specific chains
## Only allow new connections (established and related should already be handled)
## For TCP, additionally only allow new SYN packets since that is the only valid
## method for establishing a new TCP connection
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
-A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP

# allow services (ssh, dhcp, dns)
-A INPUT -i enp3s0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i enp3s0 -p udp -m udp --dport 22 -j ACCEPT
-A INPUT -i enp4s0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i enp4s0 -p udp -m udp --dport 22 -j ACCEPT
-A INPUT -i eno1 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i eno1 -p udp -m udp --dport 22 -j ACCEPT
-I INPUT -i enp3s0 -p udp -m udp --dport 67:68 -j ACCEPT
-I INPUT -i enp4s0 -p udp -m udp --dport 67:68 -j ACCEPT
-I INPUT -i enp3s0 -p tcp -m tcp --dport 53 -j ACCEPT
-I INPUT -i enp3s0 -p udp -m udp --dport 53 -j ACCEPT
-I INPUT -i enp4s0 -p tcp -m tcp --dport 53 -j ACCEPT
-I INPUT -i enp4s0 -p udp -m udp --dport 53 -j ACCEPT


# allow incoming traffic to the outgoing connections,
# et al for clients from the private network
-I FORWARD -i tun0 -o enp3s0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-I FORWARD -i enp3s0 -o tun0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-I FORWARD -i tun0 -o enp4s0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-I FORWARD -i enp4s0 -o tun0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i eno1 -o enp3s0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i enp3s0 -o eno1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i eno1 -o enp4s0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i enp4s0 -o eno1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# allow LAN to WAN
-I FORWARD -i enp3s0 -o tun0 -j ACCEPT
-I FORWARD -i enp4s0 -o tun0 -j ACCEPT
-A FORWARD -i enp3s0 -o eno1 -j ACCEPT
-A FORWARD -i enp4s0 -o eno1 -j ACCEPT

# accept initial connections (port forwarding)
-A FORWARD -i eno1 -o enp3s0 -p udp --dport 4134 -m conntrack --ctstate NEW -j ACCEPT
-A FORWARD -i eno1 -o enp4s0 -p udp --dport 4134 -m conntrack --ctstate NEW -j ACCEPT
-A FORWARD -i eno1 -o enp3s0 -p tcp --syn --dport 1022 -m conntrack --ctstate NEW -j ACCEPT
-A FORWARD -i eno1 -o enp3s0 -p udp --dport 1022 -m conntrack --ctstate NEW -j ACCEPT
-A FORWARD -i eno1 -o enp4s0 -p tcp --syn --dport 1022 -m conntrack --ctstate NEW -j ACCEPT
-A FORWARD -i eno1 -o enp4s0 -p udp --dport 1022 -m conntrack --ctstate NEW -j ACCEPT

# allow subsequent traffic after initial connection (port forwarding)
-I FORWARD -i tun0 -o enp3s0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-I FORWARD -i enp3s0 -o tun0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-I FORWARD -i tun0 -o enp4s0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-I FORWARD -i enp4s0 -o tun0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i eno1 -o enp3s0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i enp3s0 -o eno1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i eno1 -o enp4s0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i enp4s0 -o eno1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# prohibit everything else incoming
-I INPUT -i tun0 -j DROP
-A INPUT -i eno1 -j DROP

# Reject anything that's fallen through to this point
## Try to be protocol-specific w/ rejection message
-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
COMMIT

*nat
-A PREROUTING -i eno1 -p udp -m udp --dport 4134 -j DNAT --to-destination 10.0.1.2:4134
-A PREROUTING -i eno1 -p tcp -m tcp --dport 1022 -j DNAT --to-destination 10.0.1.2:1022
-A PREROUTING -i eno1 -p udp -m udp --dport 1022 -j DNAT --to-destination 10.0.1.2:1022
-A POSTROUTING -o enp4s0 -p tcp --dport 1022 -d 10.0.1.2 -j SNAT --to-source 10.0.1.1
-A POSTROUTING -o enp4s0 -p tcp --dport 1022 -d 10.0.1.2 -j SNAT --to-source 10.0.1.1
-I POSTROUTING -o tun0 -j MASQUERADE
-A POSTROUTING -o eno1 -j MASQUERADE
COMMIT

*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*security
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT

DNSMasq

为了简单起见,我删除了其中的大量注释行。

interface=enp3s0
interface=enp4s0

listen-address=127.0.0.1

dhcp-range=10.0.1.2,10.0.1.254,12h

路由表

指南解读以下信息:

[WAN=eno1, VPN=tun0, LAN=enp3s0 and enp4s0]
WAN Subnet: 10.0.0.0/24
LAN Subnet: 10.0.1.0/24
WAN IP for Debian router: 10.0.0.140
LAN IP for Debian router: 10.0.1.1

断开 VPN 连接后:

default via 10.0.0.1 dev eno1 
10.0.0.0/24 dev eno1 proto kernel scope link src 10.0.0.140 
10.0.1.0/24 dev enp4s0 proto kernel scope link src 10.0.1.1 linkdown 
10.0.1.0/24 dev enp3s0 proto kernel scope link src 10.0.1.1 linkdown

连接 VPN 后:

0.0.0.0/1 via 10.8.3.1 dev tun0 
default via 10.0.0.1 dev eno1 
10.0.0.0/24 dev eno1 proto kernel scope link src 10.0.0.140 
10.0.1.0/24 dev enp4s0 proto kernel scope link src 10.0.1.1 linkdown 
10.0.1.0/24 dev enp3s0 proto kernel scope link src 10.0.1.1 linkdown 
10.8.3.0/24 dev tun0 proto kernel scope link src 10.8.3.25 
128.0.0.0/1 via 10.8.3.1 dev tun0 
209.95.36.142 via 10.0.0.1 dev eno1

跟踪路由

当 Debian 路由器上的 VPN 断开连接时,对我的网关进行跟踪路由(因此不是带有 iptables 的 Debian 路由器,而是其 WAN 接口 eno1 上上方的路由器)会产生:

traceroute to 10.0.0.1 (10.0.0.1), 30 hops max, 60 byte packets
 1  box.local (10.0.0.1)  0.516 ms  0.686 ms  0.766 ms

但是在连接 VPN 的情况下再次运行它会挂起一段时间,直到最终产生以下结果:

traceroute to 10.0.0.1 (10.0.0.1), 30 hops max, 60 byte packets
 1  10.0.0.1 (10.0.0.1)  0.500 ms  0.785 ms  0.813 ms

因此看起来部分(但不是全部)返回信息以某种方式被丢失了。

我使用的资源

我用了文章,以及文章以及一些 Google-Fu,尝试正确设置我的 iptables。

请注意,欢迎在评论中提出有关如何改进风格或消除冗余的提示/建议。这是我第一次使用 iptables。

答案1

我写了一些可以满足你的基本需求的规则:

*filter

-N CM_REJECT
-A CM_REJECT -p tcp -j REJECT --reject-with tcp-reset
-A CM_REJECT -p udp -j REJECT --reject-with icmp-port-unreachable
-A CM_REJECT -j REJECT --reject-with icmp-proto-unreachable

-N TCP
-A TCP -p tcp --syn -j ACCEPT
-A TCP -g CM_REJECT

-P FORWARD DROP

-N NEW

-N FWD_OUT
-A FWD_OUT -o tun0 -j ACCEPT
-A FWD_OUT -o eno1 -j ACCEPT

-N FWD_IN
-A FWD_IN -i enp3s0 -g FWD_OUT
-A FWD_IN -i enp4s0 -g FWD_OUT

-A NEW -s 10.0.1.0/24 -j FWD_IN

# rules for wan-to-lan port forwarding begin #

-N FWDPS
-A FWDPS -p udp --dport 4134 -d 10.0.1.2 -j ACCEPT
-A FWDPS -p udp --dport 1022 -d 10.0.1.2 -j ACCEPT
-A FWDPS -p tcp --dport 1022 -d 10.0.1.2 -g TCP

-N LAN
-A LAN -o enp3s0 -g FWDPS
-A LAN -o enp4s0 -g FWDPS

-A NEW -i eno1 -j LAN

# rules for wan-to-lan port forwarding end #

# Maybe? The -j in NEW (instead of -g) are for this btw.
#-A NEW -g CM_REJECT

-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -m conntrack --ctstate NEW -g NEW

COMMIT

*nat

-N DNS_SERVER
-A DNS_SERVER -j DNAT --to-destination 8.8.8.8

-N DNS
-A DNS -p udp --dport 53 -g DNS_SERVER
-A DNS -p tcp --dport 53 -g DNS_SERVER

-A PREROUTING -d 10.0.1.1 -g DNS

# rules for wan-to-lan port forwarding begin #

-N FWDDST_A
-A FWDDST_A -j DNAT --to-destination 10.0.1.2

-N FWDPS
-A FWDPS -p udp --dport 4134 -g FWDDST_A
-A FWDPS -p udp --dport 1022 -g FWDDST_A
-A FWDPS -p tcp --dport 1022 -g FWDDST_A

-A PREROUTING -d 10.0.0.140 -g FWDPS

# rules for wan-to-lan port forwarding end #

-N MASQ
-A MASQ -o eno1 -j SNAT --to-source 10.0.0.140
-A MASQ -o tun0 -j MASQUERADE

-A POSTROUTING -s 10.0.1.0/24 -g MASQ

COMMIT

表中的规则filter应允许源 IP 在10.0.1.0/24通过任一 LAN 接口进入的范围内的流量通过 WAN 接口或 VPN 隧道出去。回复流量也将被允许。其他转发流量将被丢弃。(请注意,这里不涉及输入和输出流量,因为它们与“路由器”工作无关。)

表中已完成 NAT(过载)(也仅针对源 IP 在范围内的流量10.0.1.0/24natSNAT选择为 ,eno1因为 WAN IP 被认为是静态的。MASQUERADE选择为 ,tun0因为 VPN IP 被认为是动态的。

我还编写了规则,将以路由器为目标的 DNS 请求(更准确地说,是到 tcp/udp 端口​​ 53 的流量)转发到真正的 DNS 服务器。您可能想要或不需要它们。(您可以改为使用 dnsmasq 在 DNS 级别执行此操作。不过,您的配置对我来说似乎有点不对劲。)

更新:已添加处理您显然需要的端口转发的规则;除非此路由器 ( ) 不是目标主机的默认网关,否则您无需对这些流量执行SNAT/ 。另请注意,如果您需要在路由器连接到 VPN 时使端口转发工作,则可能需要执行策略路由 ( ),否则来自主机的回复将被路由到隧道中。MASQUERADE10.0.1.1fwmark

边注:在我看来,您可能想要创建一个桥并创建enp3s0enp4s0其端口。在这种情况下,您应该放弃FWD_IN链和LAN链(并制定规则,使-g它们-g成为下一个链,即和FWD_OUTFWDPS应该在之前添加-i/匹配)。-o $bridge_name-g

更新 2:以下规则适用于INPUT链。将它们插入到第一个COMMIT或之前-P FORWARD DROP

-P INPUT DROP

-N DNS
-A DNS -p udp --dport 53 -j ACCEPT
-A DNS -p tcp --dport 53 -g TCP

-N DHCP
# Shouldn't need 68 here
-A DHCP -p udp --dport 67 -j ACCEPT

-N SSH
-A SSH -p tcp --dport 22 -j ACCEPT

# This chain is intentionally created with -j rules instead of -g rules
-N MYJOBS
# Irrelevant to the requests that are being forwarded by the DNAT rules
#-A MYJOBS -j DNS
-A MYJOBS -j SSH
-A MYJOBS -j DHCP

-N MYPORTS
-A MYPORTS -i lo -j ACCEPT
-A MYPORTS -i eno1 -j SSH
-A MYPORTS -i enp3s0 -j MYJOBS
-A MYPORTS -i enp4s0 -j MYJOBS

# Maybe?
#-A MYPORTS -p icmp -j ACCEPT
#-A MYPORTS -g CM_REJECT

-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -m conntrack --ctstate NEW -g MYPORTS

顺便说一句,除了匹配之外,您还可以在链中添加-s和/或-d匹配规则。但不确定它们有多重要。同样,如果这些规则是同一桥的桥端口,则可以用规则替换它们。MYPORTS-ienp{3,4}s0$bridge_name

相关内容