VPN часть 1: IPSec/L2TP + сертификаты между двумя FreeBSD 9.0

Задача: 2 сервера FreeBSD 9.0 RELEASE, между ними нужно поднять туннель IPSec/L2TP
Инструменты: /usr/ports/security/ipsec-tools (racoon)
/usr/ports/net/mpd5


Для начала нужно собрать ядро с поддержкой IPSEC. Описание сборки ядра есть в другой статье на этом сайте, перечислю только, что нужно добавить:

options IPSEC #IP security
options IPSEC_NAT_T
device crypto
device enc
options IPSEC_DEBUG #debug for IP security

У меня через нат не завелось, чексуммы ббются, может когда еще буду это дело изучать. А может и не буду 🙂 Но в ядро рекомендую поддержку вкомпилить, на всякий пожарный.
Далее обираем из портов ракуна и мпд.
Если хочется, чтобы прешаред-кей на сервере можно было использовать для клиента с динамическим ипом, находим в сорцах ракуна файл ./src/racoon/localconf.c, около 210 строки ищем
if (strncmp(buf, str, len) == 0 && buf[len] == '') {,
комментируем и заменяем на:
if (strcmp(buf, "*") == 0 || (strncmp(buf, str, len) == 0 && buf[len] == '')) {
После в файле psk.txt можно указать так:
* my_secret_key
Далее углубляться в конфигурирование PSK не будем, т.к. от лукавого это. Нехорошо всех клиентов одним ключем авторизовать, а статические адреса клиентов в моих краях редкость. (прим. Большинство статей в гугле рассказывают иминно о настройке ракуна с PSK, ибо это проще)
Далее в мпд на сервере добавляем такое:
default:
load l2tp_server
l2tp_server:
set ippool add pool-l2tp 10.10.17.10 10.10.17.20
create bundle template L2TP-B
set iface idle 0
set iface enable tcpmssfix
set ipcp ranges 10.10.17.254/24 ippool pool-l2tp
create link template L2TP-L l2tp
set link action bundle L2TP-B
set link enable multilink
set link yes acfcomp protocomp
set link no pap chap eap
set link enable chap
set link keep-alive 10 60
load radius
set link mtu 1460
set l2tp self 0.0.0.0
set link enable incoming

Тут авторизация по радиусу, если нету, убираем строчку.
Клиента просим ломиться на сервер, на порт 1701 (L2TP):
default:
load pptp_server
load l2tp_client
l2tp_client:
create bundle static L2TPB
create link static L2TPL l2tp
set link action bundle L2TPB
set auth authname user
set auth password password
set link max-redial 0
set link mtu 1460
set link keep-alive 20 75
set l2tp peer 192.168.55.1 //адрес сервера
open

Можно на этом этапе проверить, чтобы MPD друг на друга коннектились, потом меньше будет непоняток.
Когда это сделано, начинаем настраивать IPSEC.
/etc/rc.conf (на обоих машинах):
mpd_enable="YES"
mpd_flags="-b"

ipsec_enable="YES" # да, это надо, хотя в rc.d нет такого файла
ipsec_program="/usr/local/sbin/setkey"
ipsec_file="/usr/local/etc/racoon/setkey.conf" # конфиг определяет, какой трафик шифровать
racoon_enable="yes" #включаем также енота

Далее конфиги на сервере в каталоге енота /usr/local/etc/racoon (создаем)
Делаем каталоги
/usr/local/etc/racoon/cert
/usr/local/etc/racoon/cert/ca
/usr/local/etc/racoon/cert/client <- тут ключи клиентов
/usr/local/etc/racoon/cert/server <- тут ключи сервера

файл /usr/local/etc/racoon/setkey.conf:
flush;
spdflush;
# L2TP in
spdadd 192.168.55.1/0[1701] 0.0.0.0/0 any -P out ipsec esp/transport//use;
spdadd 0.0.0.0/0 192.168.55.1/0[1701] any -P in ipsec esp/transport//use;

Немного пояснений:
1) очищаем все, далее что шифруем:
2) с нашего адреса, порта 1701, на любой адрес, направление исходящее (out), режим транспорта
2) с любого адреса на наш адрес, порт 1701, направление входящее (in), режим транспорта
Применяется файл при перезагрузке либо, что удобнее, командой:
setkey -f /usr/local/etc/racoon/setkey.conf
На клиенте делаем наоборот входящие и исходящие:

flush;
spdflush;
spdadd 0.0.0.0/0[0] 192.168.55.1/32[1701] udp -P out ipsec esp/transport//require;
spdadd 192.168.55.1/32[1701] 0.0.0.0/0[0] udp -P in ipsec esp/transport//require;

L2TP/IPSec работает через режим транспорта, если что.
Второй файл на сервере racoon.conf:

# "path" affects "include" directives. "path" must be specified before any
# "include" directive with relative file path.
# you can overwrite "path" directive afterwards, however, doing so may add
# more confusion.
path include "/usr/local/etc/racoon";

# the file should contain key ID/key pairs, for pre-shared key authentication.
path pre_shared_key "/usr/local/etc/racoon/psk.txt";

# racoon will look for certificate file in the directory,
# if the certificate/certificate request payload is received.
# !!!! указываем, откуда брать сертификаты
path certificate "/usr/local/etc/racoon/cert";

# "log" specifies logging level. It is followed by either "notify", "debug"
# or "debug2".
# для отладки, потом желательно убрать
log debug2;

# "padding" defines some padding parameters. You should not touch these.
padding
{
maximum_length 20; # maximum padding length.
randomize off; # enable randomize length.
strict_check off; # enable strict check.
exclusive_tail off; # extract last one octet.
}

# if no listen directive is specified, racoon will listen on all
# available interface addresses.
listen
{
#isakmp ::1 [7000];
# адрес, на котором слушает протокол ISAKMP (согласование параметров соединения)
isakmp 192.168.55.1 [500];
# это понадобится для работы NAT-T
isakmp_natt 192.168.55.1 [4500];
}

# Specify various default timers.
timer
{
# These value can be changed per remote node.
counter 5; # maximum trying count to send.
interval 20 sec; # maximum interval to resend.
persend 1; # the number of packets per send.

# maximum time to wait for completing each phase.
phase1 30 sec;
phase2 15 sec;
}

# секция приема входящих соединений с любого адреса
remote anonymous
{
exchange_mode main,aggressive;
lifetime time 24 hour;
doi ipsec_doi;
situation identity_only;
generate_policy on;
# посылаем кипэлайвы
dpd_delay 10;
dpd_retry 5;
dpd_maxfail 5;

# !!! это важно для сертификатов, с идентификатором "адрес" работать не будет
my_identifier asn1dn ;
peers_identifier asn1dn ;

# указываем наши сертификаты
certificate_type x509 "server/server.crt" "server/server.key";
ca_type x509 "ca/ca.crt";

nonce_size 16;
initial_contact on;
proposal_check strict; # obey, strict, or claim

# параметры фазы 1 (ISAKMP SA)
proposal {
encryption_algorithm aes;
hash_algorithm sha1;
authentication_method rsasig;
dh_group 2;
}
}

# параметры фазы 2 (IPSec SA)
sainfo anonymous
{
pfs_group 2;
encryption_algorithm aes 256, aes, 3des;
authentication_algorithm hmac_sha1;
compression_algorithm deflate;
lifetime time 24 hour;
}


На клиенте ответная часть такая:
path certificate "/usr/local/etc/racoon/cert";
path pre_shared_key "/usr/local/etc/racoon/psk.txt";

log debug;

padding {
maximum_length 20;
randomize off;
strict_check off;
exclusive_tail off;
}

listen {
adminsock "/var/db/racoon/racoon.sock";
}

timer {
counter 5;
interval 20 sec;
persend 1;
phase1 30 sec;
phase2 15 sec;
}

remote 192.168.55.1 {
exchange_mode main,aggressive;
lifetime time 24 hour;
# не забываем
my_identifier asn1dn ;
peers_identifier asn1dn ;
passive off;
generate_policy off;
# проверять сервер на вшивость не будем
verify_cert off;
send_cert on;
send_cr on;
# указываем сертификаты клиента, подписанные на сервере
certificate_type x509 "user.crt" "user.key";
proposal {
encryption_algorithm aes;
hash_algorithm sha1;
authentication_method rsasig;
dh_group 2;
}
}

sainfo anonymous {
pfs_group 2;
encryption_algorithm aes 256, aes, 3des;
authentication_algorithm hmac_sha1, hmac_md5;
lifetime time 1 hour ;
compression_algorithm deflate;
}

Теперь пора перейти собственно к сертификатам. Пользовался этой статьей:
http://www.lissyara.su/articles/freebsd/security/ipsec2/
В каталоге /usr/local/etc/racoon/cert создаем файл
openssl.cnf

# Establish working directory.
dir = .

HOME = .
RANDFILE = $ENV::HOME/.rnd

[ ca ]
default_ca = CA_default

[ CA_default ]
serial = $dir/ca/serial
database = $dir/ca/index.txt
# for crl
# the current crl number
crlnumber = $dir/ca/crlnumber
crl = $dir/ca/crl.pem # The current CRL
default_md = sha1 # which md to use.
default_crl_days = 1095 # how long before next CRL.
private_key = $dir/ca/ca.key # The private key
certificate = $dir/ca/ca.crt

[ req ]
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = UA
countryName_min = 2
countryName_max = 2

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Oblast

localityName = Locality Name (eg, city)
localityName_default = Gorod

0.organizationName = Organization Name (eg, company)
0.organizationName_default = Firma

# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd

organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = IT

commonName = Common Name (eg, YOUR name)
commonName_max = 64
commonName_default = Admin

emailAddress = Email Address
emailAddress_max = 64
emailAddress_default = [email protected]

# include a prompt for alternative namesв¦
subjectAltName = Alternative DNS names (comma seperated list)
subjectAltName_default = DNS:domain.com

Заполняем в этом файле _default, чтобы не вводить каждый раз.
Дальше делаем 2 скрипта. Первый
generate-server.sh

#!/bin/sh
openssl genrsa -aes256 -out ca/ca.key 2048 -config openssl.cnf
openssl req -new -x509 -days 1095 -key ca/ca.key -out ca/ca.crt -config openssl.cnf
openssl genrsa -out server/server.key 2048 -config openssl.cnf
openssl req -new -key server/server.key -out server/server.csr -config openssl.cnf
openssl x509 -req -days 365 -in server/server.csr -CA ca/ca.crt -CAkey ca/ca.key -CAcreateserial -out server/server.crt

выполняем без параметров, отвечаем на вопросы, получаем ключи сервера. После этого, если были сгенерированы клиентские сертификаты, их придется перегенерировать!!!
Второй скрипт:
generate-client.sh

#!/bin/sh
openssl genrsa -out client/$1.key 2048 -config ./openssl.cnf
openssl req -new -key client/$1.key -out client/$1.csr -config ./openssl.cnf
openssl x509 -req -days 365 -in client/$1.csr -CA ca/ca.crt -CAkey ca/ca.key -CAcreateserial -out client/$1.crt

Выполняем
generate-client.sh user
отвечаем на вопросы, получаем ключи юзера.
Теперь копируем user.crt и user.key из каталога client на клиентскую машину, в каталог /usr/local/etc/racoon/cert
Такое проделываем для каждого пользователя, подставляя вместо user имя пользователя.
Напоследок:
/etc/syslog.conf
...
!mpd
*.* /var/log/mpd.log
!racoon
*.* /var/log/racoon.log
...

/etc/newsyslog.conf

...
/var/log/mpd.log 600 10 100 * JC
/var/log/racoon.log 600 10 100 * JC
...

Перезапускаем сислог, ньюсислог, енота на обоих машинах, при хорошо расположенных звездах получаем рабочий IPSec/L2TP туннель.
Если звезды отвернулись, ловим косяки сначала в /var/log/racoon.log, а когда там добиваемся строчек:

Dec 24 14:49:26 hostname racoon: INFO: ISAKMP-SA established 192.168.55.1[500]-10.10.0.40[500] spi:xxxxx
и далее
Dec 24 14:49:27 hostname racoon: INFO: IPsec-SA established: ESP/Transport 192.168.55.1[500]->10.10.0.40[500] spi=xxxxxx

проверяем наличие SA командой
setkey -D
и начинаем дебажить MPD

Комментарии: