[О блоге] [наверх] [пред] [2016-12-03 17:23:12+03:00] [2fe7ed63a6726a694abb6031699132cb3b585a2b]
Темы: [ipsec][ipv6][tip]

Мой опыт настройки IPv4/IPv6 IPsec туннеля в домашних условиях

Тут я хочу поделиться своими настройками IPsec IPv4/IPv6 сети между
домашними серверами. Для меня это всё было не тривиальной задачей, хотя
сейчас она выглядит конечно просто.

У меня есть два сервера, один из которых подключён к Интернету и
является шлюзом, маршрутизатором, NAT и тому прочее. Второй сервер имеет
только Ethernet подключение к шлюзу. Между ними хочется иметь
зашифрованный трафик.

На шлюзе есть один внешний IPv4 адрес на и IPv6 /64 сеть. Хочется чтобы
второй сервер мог "выходить" в Интернет. Для IPv4 само собой нужен NAT
(ненавижу), для IPv6 просто выделение однго из адресов сети.

------------------------ >8 ------------------------

Без шифрования настройка сети на сервере выглядит так:

    # ifconfig igb0 inet 192.168.20.2/24 mtu 1500 up
    # ifconfig igb0 inet6 2001:470:1f0b:10ad::d/64
    # route add default 192.168.20.1
    # route -6 add default 2001:470:1f0b:10ad::1

------------------------ >8 ------------------------

Первая часть задачи: сделать туннель вместо использования Ethernet для
передачи IPv4/IPv6 пакетов напрямую. Для меня когда-то это было не
понятно зачем: IPsec можно легко настроить так, чтобы он шифровал пакеты
без каких-либо туннелей. Но проблема в MTU: после шифрования и
аутентификации пакет становится большего размера. Как сказать что через
Ethernet интерфейс максимальный размер IPsec ESP пакетов может быть
таким, а пакетов до обработки IPsec-ов должен быть меньше? Только имея
два интерфейса -- в одном из которых ходит трафик до IPsec, а в другом
(Ethernet) уже сами IPsec пакеты.

Поэтому делаем следующий туннель:

    # ifconfig gif0 create up
    # ifconfig gif0 inet 192.168.20.2/32 192.168.20.1
    # route add default 192.168.20.1

в нём имеется наш локальный 192.168.20.2 адрес соединённый с .1. Сервер
на данный момент знает только два адреса (свой .2 и .1 шлюза) -- это
хорошо тем, что без лишних телодвижений обращение к другим адресам сети
192.168.20/24 будет автоматически идти через шлюз.

Свой собственный IPv6 адрес задаём на loopback устройстве (сервер теперь
знает ещё один IPv6) и говорим что до IPv6 адреса шлюза можно
достучаться через наш виртуальный интерфейс:

    # ifconfig lo0 inet6 2001:470:1f0b:10ad::d/128 alias
    # route -6 add 2001:470:1f0b:10ad::1 -iface gif0
    # route -6 add default 2001:470:1f0b:10ad::1

Работать это сразу же будет потому-что у каждого интерфейса 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 адрес и задаём точки
соединения туннеля:

    # ifconfig igb0 inet6 fc00::98f1/64
    # ifconfig gif0 inet6 tunnel fc00::98f1 fc00::2752

Тут моя основная претензия к 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 на пакет.

    # ifconfig gif0 mtu 1372
    # ifconfig gif0
    gif0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1372
            options=80000<LINKSTATE>
            tunnel inet6 fc00::98f1 --> fc00::2752
            inet 192.168.20.2 --> 192.168.20.1  netmask 0xffffffff
            inet6 fe80::be5f:f4ff:fedd:98f1%gif0 prefixlen 64 scopeid 0x4
            nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
            groups: gif

Все те же самые действия проделываем на шлюзе, зеркально заменяя адреса.
И мы получим IPv6 туннель между fc00::X адресами, внутри которого ходит
IPv4 трафик между .2 <-> .1 и IPv6 link-local трафик в адресах которого
будет ::1 (шлюз) или ::d (второй сервер).

------------------------ >8 ------------------------

Вторая часть задачи: сама аутентификация сторон, согласование ключей,
шифрование и аутентификация трафика. Из коробки делается очень просто.

Задаём security policy: говорим что пакеты от fc00::98f1 (конец туннеля
на сервере) отправляемые на fc00::2752 (второй конец туннеля) должны
быть обёрнуты в ESP (encapsulating security payload) туннель IPsec. Это
один security policy. И второй -- зеркальный.

    # cat /usr/local/etc/racoon/setkey.conf
    flush;
    spdflush;

    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 из
минимального конфига.

    # cat /usr/local/etc/racoon/racoon.conf
    path pre_shared_key "/usr/local/etc/racoon/psk.txt";
    log notify;

    remote anonymous
    {
            exchange_mode main;
            lifetime time 24 hour;
            proposal {
                    encryption_algorithm blowfish;
                    hash_algorithm sha512;
                    authentication_method pre_shared_key;
                    dh_group 14;
            }
    }

    sainfo anonymous
    {
            pfs_group 14;
            lifetime time 24 hour;
            encryption_algorithm blowfish;
            authentication_algorithm hmac_sha1;
            compression_algorithm deflate;
    }

    padding {
            randomize off;
    }

Используется 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:

    ifconfig_lo0_alias0="inet6 2001:470:1f0b:10ad::d/128"
    ifconfig_igb0_ipv6="inet6 fc00::98f1/64"

    cloned_interfaces="gif0"
    ifconfig_gif0="inet 192.168.20.2/32 192.168.20.1 mtu 1372 up"
    ifconfig_gif0_ipv6="inet6 tunnel fc00::98f1 fc00::2752"

    ipv6_static_routes="gw"
    ipv6_route_gw="2001:470:1f0b:10ad::1 -iface gif0"

    defaultrouter="192.168.20.1"
    ipv6_defaultrouter="2001:470:1f0b:10ad::1"

    ipsec_enable="YES"
    racoon_enable="YES"

    [оставить комментарий]