2010-01-12

FreeBSD IPsec VPN

Итак, на очередной халтурке встала задача организовать vpn между двумя сетями. OpenSSL vpn (а ля openvpn) надоел, хочется пачку experience, поэтому vpn я задумал на IPsec. Начитавшись хэндбука и статьи у многоуважаемого Лиссяры, а также теории насчет IPsec и isakmp, решил потренировацца на кошках. То есть на VirtualBox'е. В итоге всё получилось :-)

Значитца так, первым делом надо сделать сертификаты. Проводить первую фразу с помощью psk мне не захотелось, по сертификатом прикольнее как-то =)

Скажу честно, не осилил ман по openssl, многа букаф, разбираться было влом, поэтому я взял скрипты из папки easy-rsa для openvpn :-D Единственная подстава для тру-админов в том, что те скрипты требуют bash (Или может у меня руки не из того места), но bash я и так всегда ставлю, поэтому для меня сложностей это не составило.

С их помощью нагенерил себе сертификатов. Сначала генерим certificate authority, потом сами сертификаты. Сертификаты действительны в течении 10 лет вроде. Прописываем пути в vars, прописываем значения для экспорта ключенй KEY_*.
Далее:
gw2# pwd
/root/easy-rsa
gw2# vim vars
....
export KEY_DIR='/usr/local/etc/ipsec_certs/'
....
export KEY_COUNTRY=RU
export KEY_PROVINCE=SPB
export KEY_CITY=Saint-Petersburg
export KEY_ORG="example"
export KEY_EMAIL="root@example.ru"
:wq
[root@gw2 ~/easy-rsa]# . vars
NOTE: when you run ./clean-all, I will be doing a rm -rf on /usr/local/etc/ipsec_certs/
[root@gw2 ~/easy-rsa]# ./build-ca
Generating a 1024 bit RSA private key
....................++++++
.....++++++
writing new private key to 'ca.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [RU]:
State or Province Name (full name) [SPB]:
Locality Name (eg, city) [Saint-Petersburg]:
Organization Name (eg, company) [example]:
Organizational Unit Name (eg, section) []:IT
Common Name (eg, your name or your server's hostname) []:gw2.example.ru
Email Address [root@example.ru]:
[root@gw2 ~/easy-rsa]# ./build-key gw2.example.ru
Generating a 1024 bit RSA private key
....++++++
.............................++++++
writing new private key to 'gw2.example.ru.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [RU]:
State or Province Name (full name) [SPB]:
Locality Name (eg, city) [Saint-Petersburg]:
Organization Name (eg, company) [example]:
Organizational Unit Name (eg, section) []:IT
Common Name (eg, your name or your server's hostname) []:gw2.example.ru
Email Address [root@example.ru]:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /root/easy-rsa/openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'RU'
stateOrProvinceName   :PRINTABLE:'SPB'
localityName          :PRINTABLE:'Saint-Petersburg'
organizationName      :PRINTABLE:'example'
organizationalUnitName:PRINTABLE:'IT'
commonName            :PRINTABLE:'gw2.example.ru'
emailAddress          :IA5STRING:'root@example.ru'
Certificate is to be certified until Dec 28 13:11:49 2019 GMT (3650 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
Аналогично, генерим сертификат для второго шлюза. С сертификатами всё, копируем сертификаты машин и ca, а также ключи на машины в папку, указанную в конфиге ракуна. Надо пересобрать ведро для IPsec. Начиная, если я не ошибаюсь, с 7 ветки фряхи, для IPsec достаточно добавить следующие значения в конфигурацию ядра:
# IPSEC
options         IPSEC
# Debug необязательно, но рекомендуется.
options         IPSEC_DEBUG
device crypto
Собираем значит ядро, ставим. Обновляем порты и ставим ipsec-tools.
В итоге почти все. Итак, сейчас все делаем на машине, у которой следующее:
[root@gw2 ~]# ifconfig -a
re0: flags=8843 metric 0 mtu 1500        options=389b
        ether 00:26:18:ed:bc:16
        inet 10.10.2.1 netmask 0xffffff00 broadcast 10.10.2.255
        media: Ethernet autoselect (100baseTX )
        status: active
xl0: flags=8843 metric 0 mtu 1500
        options=9
        ether 00:01:02:6e:e2:de
        inet 195.208.xx.zz netmask 0xffffffc0 broadcast 195.208.xx.zzz
        media: Ethernet autoselect (100baseTX )
        status: active
lo0: flags=8049 metric 0 mtu 16384
        inet 127.0.0.1 netmask 0xff000000
Поехали
[root@gw2 ~]# vim /usr/local/etc/racoon/racoon.conf
# Только опции, которые менял йа
....
path certificate "/usr/local/etc/racoon/certs/";
....
log debug2;
listen
{
        isakmp 195.208.xx.yy [500];
}
# Здесь адрес другого шлюза
remote 85.114.aa.bb
{
        exchange_mode main,aggressive;
        my_identifier asn1dn;
        certificate_type x509 "gw2.example.ru.crt" "gw2.example.ru.key";
        peers_identifier asn1dn;
        peers_certfile x509 "gw1.example.ru.crt";
        verify_identifier on;
        ca_type x509 "ca.crt";
        lifetime time 8 hour;
        nonce_size 16;
        initial_contact on;
        proposal_check strict;  # obey, strict, or claim
        proposal {
                encryption_algorithm 3des;
                hash_algorithm sha1;
                authentication_method rsasig;
                lifetime time 30 sec;
                dh_group 2;
        }
}

sainfo anonymous
{
        pfs_group 2;
        lifetime time 10 hour;
        encryption_algorithm 3des;
        authentication_algorithm hmac_sha1;
        compression_algorithm deflate;
}
Все, racoon настроили. Теперь для него еще надо настроить записи IPsec SPD (Security Policy Database)

И вот тут я запутался. Вообще ipsec может работать в двух режимах: transport и tunnel.
Соответственно вариантов настройки несколько. Если использовать tunnel mode, то надо указать, что шифровать будем трафик между двумя LAN, создавая туннель между реальными ip машин. Как-то так:
[root@gw2 ~]# vim /usr/local/etc/racoon/setkey.conf
flush;
spdflush;
# 10.10.2.0 - LAN. Соответственно, она идет в out. Это для исходящего трафика
# 10.10.1.0 - это LAN на другом шлюзе. Эта сетка соответственно in
# require означает, что для прохождения трафика нужна соответствующая SA,
# то есть должно быть установлено шифрованное соединение, как я понял
spdadd 10.10.2.0/24 10.10.1.0/24 any -P out ipsec esp/tunnel/195.208.xx.zz-85.114.aa.bb/require;
spdadd 10.10.1.0/24 10.10.2.0/24 any -P in  ipsec esp/tunnel/85.114.aa.bb-195.208.xx.zz/require;

Но это еще не все. Если запустить ракуны в такой конфигурации, то мы уже получим IPsec VPN в котором все пакеты между сетями будут шифроваться. Но в этом случае расшифрованные пакеты IPsec сует прямо в сетевой стек, как их выцепить файрволом я не понял. Не гут. Чтобы как раз фаером выцеплять пакеты, используют gif туннели. Весь трафик между внутренними сетями идет через gif туннель. То есть трафик вываливается из gif'a в виде инкапсулированных пакетов ipencap, и дальше этот ipencap шифруется и передаётся в туннеле, который построил джек IPsec.

Но тут тоже интересный момент. Нахрена еще один лишний туннель, построенный с помощью IPsec? Как у Лиса, и в англ. версии хэндбука. Я вот как-то не вкурил. Настроил spd на транспортный режим, чтобы шифровать трафик, который вываливается из gif:
[root@gw2 ~]# vim /usr/local/etc/racoon/setkey.conf
flush;
spdflush;
spdadd 195.208.xx.zz 85.114.aa.bb ipencap -P out ipsec esp/transport//require;
spdadd 85.114.aa.bb 195.208.xx.zz ipencap -P in  ipsec esp/transport//require;

Работает. Чтобы все поднималось после ребута машин, надо добавить следующее в /etc/rc.conf
[root@gw2 ~]# vim /etc/rc.conf
....
# IPSEC
racoon_enable="YES"
cloned_interfaces="gif0"
gif_interfaces="gif0"
gifconfig_gif0="195.208.xx.zz 85.114.aa.bb"
ifconfig_gif0="inet 10.10.2.1 10.10.1.1 netmask 0xffffffff mtu 1480"
ipsec_enable="YES"
ipsec_file="/usr/local/etc/racoon/setkey.conf"
static_routes="srv2"
route_srv2="10.10.1.0/24 -interface gif0"
На другой машине соответственно зеркально наоборот. Посмотреть, что ракуны договорились, можно командой setkey -D. Она ничего не покажет, если ракуны не договорились. Если ракуны не договорились - пакеты не будут бегать, т.к. spd запрещает передачу ipencap без шифрования.
Кто объяснит разницу в использовании туннеля и транспорта - тому ставлю пиво :-)