среда, 22 февраля 2017 г.

Использование if_ipsec во FreeBSD

Как многие уже знают, во FreeBSD 12.0-CURRENT был добавлен новый сетевой псевдоинтерфейс if_ipsec(4). На первый взгляд кажется, что он выполняет те же самые задачи что и уже существующий десятилетия if_gif(4). Но это не совсем так.
Оба интерфейса предоставляют виртуальный туннель, в который может маршрутизироваться трафик. Оба интерфейса настраиваются подобным образом: при помощи ifconfig(8) создаётся интерфейс, ему назначаются адреса конечных точек туннеля, настраиваются адреса внутри туннеля, при необходимости добавляются дополнительные маршруты. В случае с if_gif(4) этих действий уже достаточно для работы интерфейса. Он может принимать и передавать инкапсулированные в IPv4 или IPv6 пакеты.
Чтобы зашифровать передаваемый внутри туннеля трафик необходимо настроить политики и ассоциации безопасности, которые будут применять IPsec преобразования к трафику туннеля, т.е. в качестве селектора адресов для политики должны выступать адреса конечных точек туннеля, а селектором протокола - в зависимости от настроек могут быть различные вариации инкапсуляции IP в IP. Можно конечно "матчить" весь трафик между конечными точками туннеля, но это чаще всего неудобно.   Когда таких туннелей нужно создать много, то встаёт задача синхронизации настроек каждого if_gif(4) туннеля и настроек политик безопасности IPsec. К тому же, наличие большого числа политик безопасности не ускоряет работу по поиску подходящей политики для каждого пакета.
В решении части этих проблем и должен помочь if_ipsec(4). Его особенность в том, что он не выполняет инкапсуляцию сам, как это делает if_gif(4), а использует применяемую для туннельного режима "встроенную" в IPsec инкапсуляцию. При настройке туннеля if_ipsec(4) автоматически создаёт нужные политики безопасности, в которых уже указаны адреса конечных точек туннеля. Стоит заметить, что и принцип применения этих политик отличается от используемого для if_gif(4). Создаваемые интерфейсом if_ipsec(4) политики безопасности "матчат" вообще весь IPv4 и IPv6 трафик. Но так как они привязаны к конкретному интерфейсу, то и "матчат" они только трафик, проходящий через интерфейс.
В итоге можно иметь сотни таких политик безопасности в системе, но при прохождении пакетов поиск подходящей политики вообще не выполняется, т.к. каждый if_ipsec(4) интерфейс выбирает свою единственную политику, которой владеет он. 
На маршрутизаторе, где используется только if_ipsec(4), даже fastforwarding будет работать для всего остального трафика, чего нельзя получить с классическим if_gif(4) и глобальными политиками безопасности, т.к. fastforwarding автоматически отключается при наличии таких политик.
Теперь про особенности настройки. Как я уже сказал, с точки зрения конфигурации интерфейса, всё настраивается точно так же как и для if_gif(4). Но есть один дополнительный параметр reqid. Который может назначаться автоматически, но если не предполагается использовать IKE, то лучше назначить его руками. Этот параметр помагает системе различать политики нескольких интерфейсов и выполнять поиск подходящей ассоциации безопасности.
Ещё одна особенность, отличающая if_ipsec(4) от if_gif(4) - при использовании if_ipsec(4) нет возможности "выключить" IPsec и проверить, что всё работает без шифрования.
Ну и конечно же, настройку ассоциаций безопасности никто не отменял. Пароли и ключи шифрования автоматически никто придумывать за вас не будет. Чтобы ассоциация безопасности начала использоваться интерфейсом if_ipsec(4), нужно при её добавлении использовать специальный параметр у утилиты setkey(8) "-u id", где id - это упомянутый ранее reqid интерфейса. 
Пример создания туннеля между двумя тестовыми машинами test15 и test25:
test15# ifconfig ipsec0 create reqid 145
test15# ifconfig ipsec0 inet tunnel 87.250.242.145 87.250.242.144
test15# ifconfig ipsec0 inet 10.0.0.145/24 10.0.0.144
test15# ifconfig ipsec0
ipsec0: flags=8051 metric 0 mtu 1400
 tunnel inet 87.250.242.145 --> 87.250.242.144
 inet 10.0.0.145 --> 10.0.0.144 netmask 0xffffff00 
 inet6 fe80::225:90ff:fef9:3c92%ipsec0 prefixlen 64 scopeid 0x4 
 nd6 options=23
 reqid: 145
 groups: ipsec 
test15# setkey -c
add 87.250.242.145 87.250.242.144 esp 0xbadcafe -u 145 -E rijndael-cbc "1234567890987654" -A hmac-sha2-256 "12345678901234561234567890123456";
add 87.250.242.144 87.250.242.145 esp 0xcafebad -u 145 -E rijndael-cbc "0987654321123456" -A hmac-sha2-256 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa12";
^D

После настройки адресов туннеля на интерфейсе создаются вот такие политики безопасности.
test15# setkey -DP
0.0.0.0/0[any] 0.0.0.0/0[any] any
 in ipsec
 esp/tunnel/87.250.242.144-87.250.242.145/unique:145
 spid=1 seq=3 pid=9427
 refcnt=1
::/0[any] ::/0[any] any
 in ipsec
 esp/tunnel/87.250.242.144-87.250.242.145/unique:145
 spid=3 seq=2 pid=9427
 refcnt=1
0.0.0.0/0[any] 0.0.0.0/0[any] any
 out ipsec
 esp/tunnel/87.250.242.145-87.250.242.144/unique:145
 spid=2 seq=1 pid=9427
 refcnt=1
::/0[any] ::/0[any] any
 out ipsec
 esp/tunnel/87.250.242.145-87.250.242.144/unique:145
 spid=4 seq=0 pid=9427
 refcnt=1
Изменить политики можно только уничтожив интерфейс или изменив его настройки (адреса или reqid). Удалить их при помощи setkey -DPF так же не выйдет.
test25# ifconfig ipsec0 create reqid 144
test25# ifconfig ipsec0 inet tunnel 87.250.242.144 87.250.242.145
test25# ifconfig ipsec0 inet 10.0.0.144/24 10.0.0.145
test15# setkey -c
add 87.250.242.145 87.250.242.144 esp 0xbadcafe -u 144 -E rijndael-cbc "1234567890987654" -A hmac-sha2-256 "12345678901234561234567890123456";
add 87.250.242.144 87.250.242.145 esp 0xcafebad -u 144 -E rijndael-cbc "0987654321123456" -A hmac-sha2-256 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa12";
^D
test15# tcpdump -qnvi ix0 host 87.250.242.144
tcpdump: listening on ix0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:28:25.170705 IP (tos 0x0, ttl 64, id 33289, offset 0, flags [none], proto ESP (50), length 156)
    87.250.242.144 > 87.250.242.145: ESP(spi=0x0cafebad,seq=0x3), length 136
14:28:25.170928 IP (tos 0x0, ttl 64, id 4871, offset 0, flags [none], proto ESP (50), length 156, bad cksum 0 (->d212)!)
    87.250.242.145 > 87.250.242.144: ESP(spi=0x0badcafe,seq=0x1), length 136
14:28:45.288935 IP (tos 0x0, ttl 64, id 21702, offset 0, flags [none], proto ICMP (1), length 84)
    87.250.242.144 > 87.250.242.145: ICMP echo request, id 37388, seq 0, length 64
14:28:45.288955 IP (tos 0x0, ttl 64, id 32712, offset 0, flags [none], proto ICMP (1), length 84, bad cksum 0 (->65ca)!)
    87.250.242.145 > 87.250.242.144: ICMP echo reply, id 37388, seq 0, length 64
test25# ping -c1 10.0.0.145
PING 10.0.0.145 (10.0.0.145): 56 data bytes
64 bytes from 10.0.0.145: icmp_seq=0 ttl=64 time=0.508 ms

--- 10.0.0.145 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.508/0.508/0.508/0.000 ms
test25# ping -c1 87.250.242.145
PING 87.250.242.145 (87.250.242.145): 56 data bytes
64 bytes from 87.250.242.145: icmp_seq=0 ttl=64 time=0.090 ms

--- 87.250.242.145 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.090/0.090/0.090/0.000 ms 

Этот пример показывает как использовать if_ipsec(4) в режиме ручной настройки. Но if_ipsec(4) вполне может использоваться совместно с различными IKE демонами. В этом случае создание политик безопасности от IKE демона не требуется. Ядро запросит у IKEd создание ассоциации безопасности при первой же попытке прохождения пакета через интерфейс, что может инициировать согласование параметров ассоциаций безопасности между IKE.

16 комментариев:

  1. Можно еще pls для наглядности привести вывод "setkey -DP" после "ifconfig ipsec0 create" ?

    ОтветитьУдалить
    Ответы
    1. Политики появляются только после настройки адресов туннеля. Добавил вывод setkey -DP.

      Удалить
    2. Этот комментарий был удален автором.

      Удалить
    3. Спасибо! Я правда не совсем понял механизм, как трафик матчится этими политиками, если в них везде "0.0.0.0/0[any] 0.0.0.0/0[any] any".

      UPD Нет, я неправильно спросил. Под них же любой пакет матчиться будет!

      Удалить
    4. > UPD Нет, я неправильно спросил. Под них же любой пакет матчиться будет!

      В этом и фишка :)
      матчится любой пакет, но только среди тех, которые проходят через интерфейс if_ipsec(4).

      Удалить
    5. Но блин, в SPD штатно нет селектора по имени интерфейса. Как минимум косметически - это бага, в выводе setkey должно быть как-то видно, что это особые записи в SPD.

      Удалить
    6. У меня где-то был патч на возможность фильтрации вывода setkey -DP по типу политик. Я его закоммичу. Ещё посмотрю на сколько сложно будет туда добавить имя интерфейса.

      Удалить
    7. Так данные об интерфейсе в базе SPD вообще есть или нет? Я же вручную не могу создать SPD entry с матчингом по имени интерфейса?

      Удалить
    8. Тут всё несколько сложнее. Данные о политиках из ядра получаются через стандартный интерфейс PF_KEY, там сильно не разгуляешься в добавлении дополнительной информации. Политики интерфейсов хранятся не в SPD, а в самих интерфейсах. Их видимость для пользователя вообще по сути не нужна, но если их не будет видеть пользователь, значит их не будет видеть IKE демон, значит с IKE они работать не будут.
      Отображать информацию об интерфейсе можно на основе reqid в самом setkey.

      Удалить
    9. > Политики интерфейсов хранятся не в SPD, а в самих интерфейсах.

      Политики интерфейсов - это то что появилось с появлением if_ipsec(4)? Сейчас же (например в 11) никаких политик интерфейсов нет?

      > Отображать информацию об интерфейсе можно на основе reqid в самом setkey.

      Тогда может это и не слишком нужно. С другой стороны, надо же как-то в выводе spddump отличать "нормальные" политики от "интерфейсных", иначе это будет ад для администратора.

      Удалить
  2. > Политики интерфейсов - это то что появилось с появлением if_ipsec(4)? Сейчас же (например в 11) никаких политик интерфейсов нет?

    Это внутренняя особенность if_ipsec(4), относительно других интерфейсов ничего не изменилось.
    Я добавил два ключа к setkey -DP:
    -g - показывать только глобальные политики
    -t - показывать только интерфейсные политики туннелей

    Выглядит это примерно так:
    test15# setkey -DPt
    0.0.0.0/0[any] 0.0.0.0/0[any] any
    in ipsec
    esp/tunnel/87.250.242.144-87.250.242.145/unique:145
    spid=7 seq=3 pid=58025 scope=ifnet ifname=ipsec0
    refcnt=1
    ::/0[any] ::/0[any] any
    in ipsec
    esp/tunnel/87.250.242.144-87.250.242.145/unique:145
    spid=9 seq=2 pid=58025 scope=ifnet ifname=ipsec0
    refcnt=1
    0.0.0.0/0[any] 0.0.0.0/0[any] any
    out ipsec
    esp/tunnel/87.250.242.145-87.250.242.144/unique:145
    spid=8 seq=1 pid=58025 scope=ifnet ifname=ipsec0
    refcnt=1
    ::/0[any] ::/0[any] any
    out ipsec
    esp/tunnel/87.250.242.145-87.250.242.144/unique:145
    spid=10 seq=0 pid=58025 scope=ifnet ifname=ipsec0
    refcnt=1
    test15# setkey -DPg
    ::/0 ::/0 icmp6 135,0
    out none
    spid=5 seq=1 pid=58026 scope=global
    refcnt=1
    ::/0 ::/0 icmp6 136,0
    out none
    spid=6 seq=0 pid=58026 scope=global
    refcnt=1

    ОтветитьУдалить
  3. Поддержки запуска/создания интерфейса внутри клеток с виртуализированным сетевым стеком (jail vimage) пока нет?
    Судя по исходникам (если я смотрел туда, куда надо), всё-таки поддержка есть, ну и, конечно, пробовать и "бегать по граблям" мне придётся самостоятельно ;).

    И вопрос немного не в тему, ЕМНИП, dummynet не работал так как надо, при попытке запуска внутри клетки (я давно - пару лет назад смотрел, ну и сама фраза моя не совсем корректна - dummynet всё-таки работает внутри ядра).
    Судя по https://svnweb.freebsd.org/base?view=revision&revision=302054 там много "косяков" починили.

    ОтветитьУдалить
    Ответы
    1. Я никогда не пользовался jail'ами и VIMAGE тоже. Поэтому не могу никак прокомментировать работоспособность. Если раньше вы могли там использовать gif(4) туннели и пользоваться ipsec'ом, то не вижу препятствий для работы if_ipsec(4).

      Удалить
    2. И на этом спасибо!
      Если я смогу более-менее нормально протестировать работу модуля, то я постараюсь отписаться в этой ветке.

      Ну возможность создавать gif(4) туннели - была, хотя сам ipsec не работал на старых версиях ос (возможно с 9-10 релиза уже работает, попросту таких извращенцев, которые всё запускают в клетках - мало).

      Удалить