总结
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/24
)nat
。SNAT
选择为 ,eno1
因为 WAN IP 被认为是静态的。MASQUERADE
选择为 ,tun0
因为 VPN IP 被认为是动态的。
我还编写了规则,将以路由器为目标的 DNS 请求(更准确地说,是到 tcp/udp 端口 53 的流量)转发到真正的 DNS 服务器。您可能想要或不需要它们。(您可以改为使用 dnsmasq 在 DNS 级别执行此操作。不过,您的配置对我来说似乎有点不对劲。)
更新:已添加处理您显然需要的端口转发的规则;除非此路由器 ( ) 不是目标主机的默认网关,否则您无需对这些流量执行SNAT
/ 。另请注意,如果您需要在路由器连接到 VPN 时使端口转发工作,则可能需要执行策略路由 ( ),否则来自主机的回复将被路由到隧道中。MASQUERADE
10.0.1.1
fwmark
边注:在我看来,您可能想要创建一个桥并创建enp3s0
和enp4s0
其端口。在这种情况下,您应该放弃FWD_IN
链和LAN
链(并制定规则,使-g
它们-g
成为下一个链,即和FWD_OUT
;FWDPS
应该在之前添加-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
-i
enp{3,4}s0
$bridge_name