Как создать правило 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для эффективного анализа JSON
  • netmask(корректно обрабатывает любое сокращение адреса 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 везде.

Другие вопросы по тегам