我想使用 iptables 将对服务器的访问限制为某些 IP,但是:
- 其中一个 IP 是动态的,是正常的 ISP 家庭连接,它会不时变化。
- 当 IP 发生变化时,子域名(例如 dynamic.example.org)会使用类似于 dyndns 的服务自动更新。
如果 dynamic.example.org 解析为该 IP,是否可以让 IPtables 允许访问端口?
我目前的想法是设置一个 systemd 单元,定期解析 dynamic.example.org 并相应地调整 iptables。但是,这还需要知道旧 IP 地址(因此将其存储在某处)才能将其从白名单中删除。
是否有更简单的方法可以做到这一点,并且已内置于 iptables 中?
答案1
iptables
适用于 IP 地址,而不适用于主机名。您可以使用主机名作为参数,但它们将在输入命令时解析。对每个通过的数据包进行 DNS 查找会太慢。
因此,调整规则是唯一的方法。这可以定期进行,由 systemd 或 cron 等程序控制,或者如果您能够设法在 IP 地址更改时收到通知,那就更好了。
您不必存储旧地址,只需为您的规则创建一个 iptables 链并替换该规则即可。请参阅选项-R
。iptables
要在第一次检查时替换规则,只需添加一条虚拟规则,以便在第一次检查运行时有一条规则可以替换。
INPUT
您还可以避免多余的链并替换或中的特定位置的规则FORWARD
,但这需要更多的维护工作,因为每当您添加或删除规则时位置编号都会发生变化。
答案2
我这样做的方法是:
- 每 x 分钟从 crontab 运行一次脚本来更新“ipset”
- 让 IPtables 使用 ipset
假设此 ipset 中只有 1 个 IP 地址,则以下脚本可以执行:
#!/bin/bash
# Update ipset to let my dynamic IP in
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
set=whitelist
host=myhost.dynamic.example.com
me=$(basename "$0")
ip=$(dig +short $host)
if [ -z "$ip" ]; then
logger -t "$me" "IP for '$host' not found"
exit 1
fi
# make sure the set exists
ipset -exist create $set hash:ip
if ipset -q test $set $ip; then
logger -t "$me" "IP '$ip' already in set '$set'."
else
logger -t "$me" "Adding IP '$ip' to set '$set'."
ipset flush $set
ipset add $set $ip
fi
我crontab
每 5 分钟调用一次此脚本:
*/5 * * * * root /usr/local/bin/ipset-update-dyn
在 iptables 中,使用 ipset 的规则如下所示:
-A INPUT -p tcp -m set --match-set whitelist src -m state --state NEW -j ACCEPT
答案3
使用 dig 甚至更简单:
iptables -I INPUT -p tcp -s $(dig +short yourdomain.ddns.net) -m state --state NEW -m tcp -j ACCEPT
解释:
dig+short yourdomain.ddns.net
在返回 IPv4 IP 的子进程中启动。此子进程的输出将传递给 iptables 二进制文件,添加一条新规则。
请注意,我们习惯-I
将规则添加到所有其他规则的前面,如果要附加,请使用-A
iptables -A INPUT -p tcp -s $(dig +short yourdomain.ddns.net) -m state --state NEW -m tcp -j ACCEPT
更新:
正如 MartinV 所述,如果您继续添加规则,最终可能会产生大量重复规则,并且会达到规则限制容量。
最好的方法是创建一个链来保存您的 DDNS 规则,然后在添加新规则之前刷新自定义链。
如果不存在,则创建一个自定义链,将自定义链 myDDNS 添加到 INPUT 链的前面,以防止被 INPUT 链中的最终一般 REJECT 阻止,并刷新链:
iptables -N myDDNS 2>/dev/null;iptables -I INPUT -j myDDNS;iptables -F myDDNS
如果链存在,则会引发错误,但自定义链不会重复。将错误重定向到 /dev/null 将防止在 STDIN 上抛出错误。
将您的规则添加到您的自定义链中:
iptables -I myDDNS -p tcp -s $(dig +short yourdomain.ddns.net) -m state --state NEW -m tcp -j ACCEPT
只需将所有内容放在一行中,然后每 N 分钟从 cron 调用一次脚本。
iptables -N myDDNS 2>/dev/null;iptables -I INPUT -j myDDNS;iptables -F myDDNS && iptables -I myDDNS -p tcp -s $(dig +short yourdomain.ddns.net) -m state --state NEW -m tcp -j ACCEPT
&&
在 (flush) 和规则前置之间添加iptables -F
将确保仅当 flush 命令返回 0 时才添加规则