Мой опыт настройки IPv4/IPv6 IPsec туннеля в домашних условиях
Тут я хочу поделиться своими настройками IPsec IPv4/IPv6 сети между
домашними серверами. Для меня это всё было не тривиальной задачей, хотя
сейчас она выглядит конечно просто.
У меня есть два сервера, один из которых подключён к Интернету и
является шлюзом, маршрутизатором, NAT и тому прочее. Второй сервер имеет
только Ethernet подключение к шлюзу. Между ними хочется иметь
зашифрованный трафик.
На шлюзе есть один внешний IPv4 адрес на и IPv6 /64 сеть. Хочется чтобы
второй сервер мог "выходить" в Интернет. Для IPv4 само собой нужен NAT
(ненавижу), для IPv6 просто выделение однго из адресов сети.
Первая часть задачи: сделать туннель вместо использования Ethernet для
передачи IPv4/IPv6 пакетов напрямую. Для меня когда-то это было не
понятно зачем: IPsec можно легко настроить так, чтобы он шифровал пакеты
без каких-либо туннелей. Но проблема в MTU: после шифрования и
аутентификации пакет становится большего размера. Как сказать что через
Ethernet интерфейс максимальный размер IPsec ESP пакетов может быть
таким, а пакетов до обработки IPsec-ов должен быть меньше? Только имея
два интерфейса -- в одном из которых ходит трафик до IPsec, а в другом
(Ethernet) уже сами IPsec пакеты.
в нём имеется наш локальный 192.168.20.2 адрес соединённый с .1. Сервер
на данный момент знает только два адреса (свой .2 и .1 шлюза) -- это
хорошо тем, что без лишних телодвижений обращение к другим адресам сети
192.168.20/24 будет автоматически идти через шлюз.
Свой собственный IPv6 адрес задаём на loopback устройстве (сервер теперь
знает ещё один IPv6) и говорим что до IPv6 адреса шлюза можно
достучаться через наш виртуальный интерфейс:
Работать это сразу же будет потому-что у каждого интерфейса FreeBSD из
коробки включён IPv6 с link-local адресами -- из коробки любой интерфейс
может общаться по IPv6.
Но, этот туннель ещё не соединён ни с кем. Не знаю почему, но у меня
никак не вышло использовать link-local адреса для этого. То есть
туннелировать IPv4-over-IPv4, IPv4-over-IPv6, IPv6-over-IPv4,
IPv6-over-IPv6 можно без проблем, но вот IP*-over-IPv6-link-local не
работает.
Поэтому, туннель должен быть между какими-то ещё дополнительными
адресами, никуда не деться. В IPv6 есть site-local адреса (аналог
192.168., 10., 172.16. сетей IPv4) которые в Интернет не
маршрутизируются, но и не являются link-local с которым gif не работает.
Добавляем на Ethernet интерфейс site-local адрес и задаём точки
соединения туннеля:
Тут моя основная претензия к man-ам FreeBSD для ifconfig: до сих пор я
нигде не вижу где сказано как задаётся туннель для IPv4 и IPv6 адресов.
Примеры везде такие: ifconfig iface tunnel ADDR1 ADDR2. Но, как
оказалось, это ifconfig iface inet tunnel ADDR1 ADDR2. inet
по-умолчанию, но вместо него можно вставить inet6. Без этого сделать
туннель -over-IPv6 было бы невозможно и я долгое время считал что gif в
FreeBSD и годится только для IPv4-over-IPv4 и IPv6-over-IPv4.
Не забываем уменьшить MTU. В данном случае у меня 1372 байта
(эмпирически вышло, не в притык), что ощутимо, по сравнению с GoVPN
добавляющим 25 байт overhead на пакет.
Все те же самые действия проделываем на шлюзе, зеркально заменяя адреса.
И мы получим IPv6 туннель между fc00::X адресами, внутри которого ходит
IPv4 трафик между .2 <-> .1 и IPv6 link-local трафик в адресах которого
будет ::1 (шлюз) или ::d (второй сервер).
Вторая часть задачи: сама аутентификация сторон, согласование ключей,
шифрование и аутентификация трафика. Из коробки делается очень просто.
Задаём security policy: говорим что пакеты от fc00::98f1 (конец туннеля
на сервере) отправляемые на fc00::2752 (второй конец туннеля) должны
быть обёрнуты в ESP (encapsulating security payload) туннель IPsec. Это
один security policy. И второй -- зеркальный.
spdadd fc00::98f1 fc00::2752 any -P out ipsec
esp/tunnel/fe80::be5f:f4ff:fedd:98f1%igb0-fe80::be5f:f4ff:fedd:2752%igb0/require;
spdadd fc00::2752 fc00::98f1 any -P in ipsec
esp/tunnel/fe80::be5f:f4ff:fedd:2752%igb0-fe80::be5f:f4ff:fedd:98f1%igb0/require;
В данном случае, IPsec туннель должен быть между какими-то сторонними
(не endpoint-ы gif туннеля) адресами. Но дополнительно выделять из
адресов ничего не надо, так как, опять же из-за IPv6, мы уже имеем
link-local в Ethernet сети. Именно они выше и прописаны.
Демон занимающийся созданием security association (инициализацией IPsec
туннеля) -- racoon. Его конфигурация в моём случае проста и тривиальна:
прописываются правила для анонимных пользователей, просто copy-paste из
минимального конфига.
Используется Diffie-Hellman 2048bit, Blowfish шифрование, HMAC-SHA1 и
SHA512 (при согласовании ключей). Задавать другой HMAC я не хочу
исключительно из целей экономии трафика, MTU. compression_algorithm
задан, потому-что оставить его пустым нельзя, но в sysctl сжатие трафика
явно отключено (бессмысленно в в домашней гигабитной сети и медленно,
так как это не LZO или LZ4). padding.randomize отключён для экономии MTU
и ресурсов.
После создания этих файлов, загружаем security policy в ядро и запускаем
racoon: service ipsec start; service racoon start. Теперь gif трафик
fc00:: адресов шифруется через туннель между fe80:: адресами. В
локальной сети ходит только IPv6 link-local ESP IPsec.
Все строчки которые я добавил для конфигурирования сервера в rc.conf: