Я хочу ограничить доступ к серверу определенными IP-адресами с помощью iptables, но:
Возможно ли, чтобы IPtables разрешал доступ к порту, если dynamic.example.org разрешает этот IP-адрес?
Моя текущая идея состоит в том, чтобы настроить модуль systemd, который периодически разрешает dynamic.example.org и соответствующим образом корректирует iptables . Однако для этого также необходимо знать старый IP-адрес (чтобы его где-то сохранить), чтобы удалить его из белого списка.
Есть ли более простой способ сделать это, уже встроенный в iptables?
iptables
работает на IP-адресах, а не на именах хостов. В качестве аргументов можно использовать имена хостов, но они будут разрешены при вводе команды. Выполнение DNS поиска для каждого проходящего пакета было бы слишком медленным.
Ваша идея по настройке правил, таким образом, является единственным подходом. Это может быть либо по обычному расписанию, управляемому программой типа systemd или cron, либо лучше, если вы сможете управлять получением уведомлений при изменении IP адреса.
Вам не нужно хранить старый адрес, просто сделайте цепочку iptables для вашего правила и замените правило. Смотрите опцию -R
для iptables
. Чтобы иметь заменяющее правило при первой проверке, просто добавьте фиктивное правило, чтобы при первой проверке было заменено правило.
Можно также избежать дополнительной цепочки и заменить правило на определенную позицию в INPUT
или FORWARD
, но это гораздо более трудоемкая задача, поскольку номер позиции меняется всякий раз, когда вы добавляете или удаляете правила.
Я делаю это следующим образом:
Предполагая, что у вас есть только 1 IP-адрес в этом ipset, подойдет следующий скрипт:
#!/bin/bash
# Update ipset to let my dynamic IP in
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 $host; 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 $host
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
Еще проще с помощью 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 и просто сбросить пользовательскую цепочку перед добавлением нового правила.
Создайте пользовательскую цепочку, если она не существует, и сбросьте цепочку.:
iptables -N myDDNS 2>/dev/null;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
Просто соберите все вместе в один лайнер и вызывайте скрипт из cron каждые N минут.
iptables -N myDDNS 2>/dev/null;iptables -F myDDNS && iptables -I myDDNS -p tcp -s $(dig +short yourdomain.ddns.net) -m state --state NEW -m tcp -j ACCEPT
Добавление &&
междуiptables -F
(flush)и предшествующим правилом гарантирует, что правило будет добавлено только в том случае, если команда flush возвращает 0