Как создать правило nftables, которое принимает соединения из одной и той же подсети IPv6, если префикс IPv6 является динамическим?
Для IPv4 легко создать правило, которое принимает соединения, например, только от хостов одной подсети (при условии, что мой компьютер192.168.42.2
, и входящее соединение192.168.42.20
):
table ip firewall {
chain incoming {
type filter hook input priority 0; policy drop;
ip saddr 192.168.42.0/24 tcp dport 8080 accept
}
}
Как это сделать для IPv6? Я знаю, что всегда есть локальный адрес ссылки, и теоретически это правило должно работать:
ip6 saddr fe80::/64 tcp dport 8080 accept
Проблема теперь в том, что у меня настроен mDNS, и возвращаемый адрес является глобально маршрутизируемым адресом, что-то вроде2001:db8::1234
. По этой причине все пакеты, которые я получаю от других хостов (несмотря на то, что они находятся в одной подсети), имеютip6 saddr
с2001:db8
префикс, который блокируется брандмауэром.
Я не могу просто добавить правило, которое соответствует2001:db8::/64
, поскольку этот префикс предоставляется интернет-провайдером и время от времени меняется. Настройка ULA так, чтобы я получал предсказуемый префикс, также не представляется возможной, поскольку маршрутизатор настроен интернет-провайдером, а интерфейс конфигурации, который он имеет для IPv6, до боли пуст.
Итак... вот почему я ищу что-то похожее на это:
ip6 saddr & ffff:ffff:ffff:ffff:: == ip6 daddr & ffff:ffff:ffff:ffff:: tcp dport 8080 accept
Но nftables, похоже, этого не принимает. Могу ли я что-то сделать, чтобы обойти эту проблему, или я что-то упускаю?
1 ответ
Эта функция не существует... пока?
В настоящее время nftables может использовать только один регистр (в своем виртуальном конечном автомате): он применяет побитовые операции в левой части (LHS) для сравнения результата с константой или набором в правой части (RHS). Он не может использовать два переменных операнда (то есть оба из пакета) в LHS и RHS.
В следующих сериях патчей есть незавершенная работа по улучшению этой проблемы (они еще не приняты): nf-next libnftnl nftables:
В настоящее время побитовые логические операции (AND, OR и XOR) могут иметь только один переменный операнд. [...] Мы добавляем поддержку оценки этих операций непосредственно в пространстве ядра в одном регистре и либо непосредственном значении, либо во втором регистре .
Вероятно, это потребует дополнительных итераций и некоторого времени, прежде чем оно станет доступным (идея витает в воздухе с 2019 года, а может быть и раньше, но она до сих пор недоступна). После этого можно представить точное правило этого ОП:
ip6 saddr & ffff:ffff:ffff:ffff:: == ip6 daddr & ffff:ffff:ffff:ffff:: tcp dport 8080 accept
будет работать так, как ожидалось.
Обходной путь
Тем не менее, что можно сделать сегодня?
Можно использовать внешний инструмент, который реагирует на изменение адреса хоста и обновляет набор, чтобы в качестве контента содержалась сеть/сетевая маска IPv6 хоста. Набор можно использовать в качестве RHS. В конце концов, он не динамичен, как переменные операнды , но все же достаточно динамичен для нужд.
Добавьте набор в набор правил ОП (не буду помещать полный набор правил, это выходит за рамки вопроса. Просто помните, что обычноct state related,established accept
а также разрешить наличие интерфейса обратной связи и таблицу семействаinet
скорее, чемip6
можно было бы объединить некоторые правила для IPv4 и IPv6, если это необходимо).
ip6firewall.nft
:
table ip6 firewall #for idempotence
delete table ip6 firewall #for idempotence
table ip6 firewall {
set myip6net {
typeof ip6 saddr
flags interval
}
chain acceptmyip6netsrc {
ip6 saddr @myip6net counter accept
}
}
Это можно вызвать из базовой цепочки ввода с помощью:
tcp dport 8080 jump acceptmyip6netsrc
Я предполагаю, что имя сетевого интерфейсаeth0
. Приведенный ниже сценарий использует очень простой цикл событий и будет продолжать работать: используйте его как службу, а не в crontab. Он срабатывает всякий раз, когда происходит событие адреса (большую часть времени бесполезно, когда происходит объявление маршрутизатора, которое обновляет таймауты и ничего не меняет).ip monitor
вывод непросто разобрать, поэтому просто игнорируйте его и используйтеip -json addr
для получения фактических значений. Сценарий имеет возможности для улучшения, но свою задачу он выполняет.
Требуются инструменты, которые обычно доступны в дистрибутивах:
jq
для эффективного анализа JSONnetmask
(корректно обрабатывает любое сокращение адреса IPv6, поэтому2001:db8::4:5:6:7:8/64
правильно преобразуется в2001:db8:0:4::/64
).
updatemyip6net.sh
:
#!/bin/sh
{ echo init; ip -6 -o monitor address dev eth0; } | while read dummy; do
myip6addr=$(ip -json -6 addr show dev eth0 scope global |
jq -j '.[].addr_info[] | if .local then .local,"/",.prefixlen,"\n", halt else empty end'
)
myip6net=$(netmask $myip6addr)
nft -f - <<EOF
flush set ip6 firewall myip6net
add element ip6 firewall myip6net { $myip6net }
EOF
done
Выше,
ip -json -6 addr show dev eth0 scope global | jq -j '.[].addr_info[] | if .local then .local,"/",.prefixlen,"\n", halt else empty end'
довольно длинный, но заменив его на более простой:
ip -j -6 route get 2001:4860:4860::8888 | jq -r '.[].prefsrc'
не получает сетевую маску, и следует избегать жесткого кодирования /64 везде.