Мы — долго запрягаем, быстро ездим, и сильно тормозим.
www.lissyara.su —> статьи —> FreeBSD —> Security —> IPSec

IPSec. Теория и практика.

Автор: Al.


Немного предисловия. Началось с того, что pptp перестал устраивать, т.к. многие провайдеры не очень, мягко говоря, дружат с gre. Поэтому рещено было перейти на l2tp. Решение оправдало себя, скажу я вам. Если по- быстрому, то меняем все pptp на l2tp в mpd и все работает. Как клиент, так и сервер. Статей по настройке mpd куча, дублировать не вижу смысла. Тут вопрос возник с вендой. В ХР впн только двух вариантов - pptp и l2tp/IPSec. На скорую руку решается созданием ключа

ProhibitIpSec в 
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet \Services\RasMan\Parameters\

типа DWORD со значением "1".

Это просто отключает IPSec на венде и она работает как обычный l2tp клиент. Но раз есть IPSec, надо юзать.

Итак, IPSec. Долго думал, какая же все-таки связь между IPSec и l2tp. Искал строчки с названием первого в конфигах второго и наоборот. Раз позиционируется как одно, значит, связь должна же быть. Ответ оказался прост. Связь примерно такая же, как у стола с табуреткой. Вроде, и можно использовать вместе, но обе совершенно независимые вещи, не имеющие никакой реальной связи между собой. Может быть l2tp/IPSec, может быть smtp/IPSec, telnet/IPSec, rdp/IPSec и, даже, окошмар, icmp/IPSec. На кой хрен в венде это как одно нераздельное - непонятно.
Вообще IPSec вещь универсальная. Если в кратце, то ядро берет пакеты соответствующие определенным правилам и шифрует их перед отправкой в сеть. А другая сторона, получив эти пакеты, расшифровывает и отдает приложению так, как будто они пришли по сети нешифрованными. Физически это можно представить, как если бы из вашего сервера сетевой шнурок шел в черную коробочку, а из нее уже в свич. И у соседа так же. Ваша черная коробочка шифрует пакеты, исходящие от вас и расшифровывает пакеты, приходящие от соседа, которые в свою очередь шифрованы его коробочкой. Коробочки аутентифицируются, обмениваются ключами, параметрами шифрования и т.п. сами, а вы с соседом видите друг друга, как если бы были просто в одном свиче (ну, или просто оба воткнуты в интернет). Дак вот, эти коробочки и есть IPSec. Какой-то трафик пропускается так, какой-то шифруется. Что и как - вы устанавливаете сами. Отсюда и нет никакой прямой связи с тунелями или еще чем-либо. Для приложений работа IPSec прозрачна. В самом простом исполнении тунелей с IPSec создается простой нешифрованный тунель с помощью gif интерфейсов и в правилах IPSec-у указывается, что трафик этого тунеля (или просто - весь трафик между машинами,создающими тунель) надо шифровать.
Я для работы с ipsec использовал ipsec-tools. Еще немного теории.
У IPSec есть 2 режима работы - тунель и транспорт.
В режиме тунеля шифруется пакет целиком, включая заголовки. Т.е. создается виртуальный тунель. В конечном итоге невозможно понять, откуда и куда идет инкапсулированный пакет. Но тут возникает проблема, если клиент находится за натом. Т.к. нат меняет заголовки пакета, то все перестает работать. Решение - NAT-T. На семерку есть патч, на восьмерке.1 нужно просто собрать с ним ядро. При этом не забудьте собрать с поддержкой порт.
В режиме транспорта шифруется только часть с данными и проблем с натом быть не должно.Я использую транспорт.
Раз уж про ядро, то в ядро добавляем

options         IPSEC  # непосредственно поддержка.
options         IPSEC_ESP    # Encapsulating Security Payload — 
                             # инкапсуляция зашифрованных данных. 
options         IPSEC_DEBUG  # дебаг. кому надо - кому нет.
device          crypto       # лишним не будет.

Это касаемо шестерки. В восьмерке появляется IPSEC_NAT_T и исчезает IPSEC_ESP
Собрали-пересобрали-поставили.

Конфиг racoon (порт ipsec-tools). Конфиг сервера. Впринципе, его же можно и на клиента. Только вместо anonymous пишем адрес сервера.
racoon/racoon.conf

path pre_shared_key "/usr/local/etc/racoon psk.txt"; 
#path pidfile "/var/run/racoon/racoon.pid";
#path certificate "/etc/ssl/certs";        
#path script "/etc/ssl/certs";    

log info;

# Не останавливается.
#privsep {           
#       user 2000;   
#       group 2000;  
#}                   

# Задает параметры формирования пакетов
# Лучше не трогать. man(c)                   
padding {                              
        maximum_length 20;             
        randomize off;                 
        strict_check off;              
        exclusive_tail off;            
}                                      


# Что слушаем и как. Что б не забивалось на тунельные адреса
listen {                                         
        isakmp 1.1.1.1;                     
        isakmp_natt 1.1.1.1 [4500];         
}                                                

# Значения таймеров. 
# На всех серверах должны быть идентичны. 
# Иначе возможны варианты зависиния тунеля.
timer {                                  
        # Максимальное количество повторов
        counter 5;                        
        # Интервал между повторами        
        interval 20 sec;                  
        # Количество передаваемых пакетов за "посылку"
        persend 1;                                    

        # Максимальное время для завершения фазы 1
        phase1 30 sec;                            
        # Максимальное время для завершения фазы 2
        phase2 15 sec;                            
        #natt_keepalive 10sec;                    
}                                                 

# Удаленные клиенты. Можно написать ip, для остальных anonymous.
# Указываем IKE phase 1 для каждого удаленного клиента. Если    
# указан anonymous секция будет применена к каждому клиенту,    
# не попавшему под конкретную секцию (адрес вместо anonymous)   
remote anonymous {                                              
        # Метод обмена, если мы инициаторы. Так же принимать данный 
        # вид, если клиент. Можно несколько через запятую.          
        exchange_mode aggressive,main;                              
        # exchange_mode aggressive;                                 
        # means to use IPsec DOI as specified in RFC 2407.  You can   
        # omit this statement.                                      
        # doi- область интерпретации ISAKMP (IKE).                  
        # В данном случае для ipsec.                                
        # doi ipsec_doi;                                            
        # Переидентификация узлов и сравнение политик раз в         
        # 24 часа.                                                  
        # !! На обоих узлах должно быть одинаково. win - ? !!       
        # !! Иначе тонель виснет. фаза1 > фаза2. !!                 
        lifetime time 24 hour;                                      
        # Если не хотим инициировать согласование, ставим off.      
        # Применимо только для сервера.                             
        passive off;                                                
        # Генерировать policy из proposal. Это используется для     
        # клиентов с динамическими адресами. (on | off | require | unique)
        # Имеет смысл только на сервере.                                  
        generate_policy on;
        # The below line makes it work with other devices that propose    
        #  a lifetime longer than the racoon default of 28800.            
        # Alternatively, set a longer lifetime.  "proposal_check claim" may
        #  work suboptimally with frequent renegotiation of phase 2.       
        proposal_check obey;                                               

        # Включаем вкомпиленный в ядро NAT-T для клиентов за натом.
        # Можно сделать force для всех.                            
        nat_traversal on;                                          
        # Включаем IKE фрагментацию на принимающей стороне. В случае, если 
        # работает через кривой фаер, имеющий проблемы с фрагментацией udp.
        ike_frag on;                                                       

        # Методы аутентификации - шифрования клиентов.
        proposal {                                    
                # Алгоритм для фазы 1. Можно использовать
                # des, 3des, blowfish, cast128, aes, camellia
                encryption_algorithm 3des;                   
                # Хэш-алгоритм для фазы 1. Можно использовать
                # md5, sha1, sha256, sha384, sha512          
                hash_algorithm sha1;                         
                # Метод аутентификации для фазы 1. Можно использовать
                # pre_shared_key, rsasig (for plain RSA authentication), 
                # gssapi_krb, hybrid_rsa_server, hybrid_rsa_client, 
                # xauth_rsa_server, xauth_rsa_client, 
                # xauth_psk_server or xauth_psk_client.
                authentication_method pre_shared_key; 
                # Определяем группу для Diffie-Hellman exponentiations. 
                # Группа должна быть одной из: modp768, modp1024, modp1536, 
                # modp2048, modp3072, modp4096, modp6144, modp8192.  
                # Или можно использовать 
                # 1, 2, 5, 14, 15, 16, 17, или 18 в качестве DH-номера группы. 
                # Если используется aggressive mode, необходимо 
                # установить иодинаковую группу
                # для каждого proposal.
                dh_group 2;
        }
}

# construct phase 2 proposals by combining sainfo specifications in
# racoon.conf, and policies in the kernel.
sainfo anonymous {
        # encryption_algorithm aes, 3des;
        encryption_algorithm 3des;
        # sha1 - немного секюрней, md5 немного быстрее. Можно
        # принудительно назначить какой-либо.
        # see http://www.ciscopress.com/articles/article.asp?p=25473
        authentication_algorithm hmac_md5, hmac_sha1;
        # Поскольку фаза 2 отвечает за ключи шифрования - время ее жизни
        # час. Раз в час меняем ключи.
        # !! На обоих узлах должно быть одинаково. win - ? !!
        # !! Иначе тонель виснет. фаза1 > фаза2. !!
        lifetime time 1 hour ;
        compression_algorithm deflate;
}

Данный конфиг заточен под конфиг венды, так же без проблем работает с юниксом, если его же перекинуть на клиента - что б совпадали все методы шифрования. Правда, пробовал только 1 клиента за раз.
Теперь по конфигу.
Коменты сверху и секция privsep для запуска ракуна под непривилегированным пользователем. Но штатный rc.d скрипт под это не рассчитан. Он его тупо не тормозит.
Все строки с natt коментим, если его нет в ядре. Или если собрано без его поддержки.
Секция timer. Тут интересно, что такое фаза 1 и 2.
Фаза 1.
узлы договариваются о методе шифрования, алгоритме, хэш-алгоритме и группе Diffie Hellman. Так же идентификация. Путем обмена 3 нешифрованными пакетами - aggressive mode, или шестью - main mode. В результате создается SA первой фазы (IKE SA).
Фаза 2.
Генерятся ключи, узлы договариваются о используемой политике. Все пакеты этой фазы шифруются. В результате создается phase 2 SA (IPSec SA).
SA - связь (ассоциация) безопасности. Это термин IPSec для обозначения соединения.

remote anonymous - мы предполагаем, что не знаем, с какого ип к нам будут конектиться. Если знаем, вместо anonymous пишем ип.
generate_policy on - заслуживает отдельного внимания, но о нем в разделе setkey.
proposal - может быть сколько угодно. Я для венды и фри использую одинаковые параметры.

Ну вот, собственно, по конфигу и все. Для аутентификации можно использовать сертификаты, можно гибридную аутентификацию, можно парольную фразу. Начнем с наиболее простого.
И так, парольная фраза. Алгоритм работы примерно таков. Узлы идентифицируют друг друга по парольной фразе, генерят сертификаты, обмениваются ими и создают шифрованный тунель. Обмен ключами осуществляется по протоколу IKE - итнтернет кей эксчейндж. Так же осуществляется переодическая смена ключей. Таким образом, если врагом будет получен один ключ, то расшифровать им он сможет только часть трафика до смены ключей. После смены старый ему никак не поможет, в .т.ч и в получении или перехвате нового. Парольные фразы хранятся в /usr/local/etc/racoon/psk.txt
В виде соответствия хост (ip) - пароль. Пример идет вместе с портом. например,
2.2.2.2 password

Тут возникает некая засада. А если клиенты на динамических адресах? Тут скажу спасибо некому человеку, который придумал следующее. К сожалению, автора не нашел. делаем make extract порта и правим:

# IMPORTANT:If you really need wildcard PSK (Pre-Shared Key) 
# as well as main mode IP address identity support during PSK authentication
# (for Microsoft Windows XP clients etc.), you may want to apply 
# the following changes to ipsec-toolsbefore compiling the package.
#
# Wildcard PSK (An asterisk "*" matches all IPs)
#
# In ipsec-tools/src/racoon/localconf.c, locate the function named 
# "getpsk(str, len)"; then in the while #loop, find the line that looks like:
#
# if (strncmp(buf, str, len) == 0 && buf[len] == '\0') {
# Replace it with:
# if(strcmp(buf, "*")==0||(strncmp(buf,str,len)==0&& buf[len]=='\0')){
# Save and exit.
# Recompile and reinstall if necessary.
#
# Main mode IP address identity
#
# In ipsec-tools/src/racoon/ipsec_doi.c, locate the function named "
# ipsecdoi_checkid1(iph1)"; then find #the lines that look like:
#
# if (iph1->etype == ISAKMP_ETYPE_IDENT &&
# iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_PSKEY) {
# if (id_b->type != IPSECDOI_ID_IPV4_ADDR
# && id_b->type != IPSECDOI_ID_IPV6_ADDR) {
# plog(LLV_ERROR, LOCATION, NULL,
# "Expecting IP address type in main mode, "
# "but %s.\n", s_ipsecdoi_ident(id_b->type));
# return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
# }
# }
#
# Change LLV_ERROR into LLV_WARNING; 
# comment out the return statement using /* */.
#
# Save and exit.
#
# Recompile and reinstall if necessary.

После этого вместо ip-адреса можно написать '*'. И пароль будет для всех. Не забудьте сделать права доступа 600 на файл с паролем.
И так, мы настроили конфиг, создали парольную фразу. Можно попробовать запустить ракун и убедиться,что все работает. Не забудьте добавить racoon_enable="YES" и racoon_create_dirs="YES" в rc.conf.
Теперь еще немного теории. Есть 2 базы. SAD и SPD. С обеими работаем через setkey. Лучше использовать тот, что идет вместе с портом - /usr/local/sbin/setkey, т.к. стандартный неккоректно работал с SAD лично у меня.

SA [/usr/local/sbin/setkey -D] - связь (ассоциация) безопасности. Это термин IPSec для обозначения соединения.При установленном соединении для каждого используемого протокола создается пара SA. Пара, т.к. SA - это днонаправыленное соединение, а данные передаются в обоих направлениях. SA- пары хранятся на каждом узле. Если есть SA - соединение установлено. На практике же можно шифровать трафик только в одном направлении. Просто попробовал ради интереса. От меня шел ESP (шифрованный), ко мне шел обычный l2tp. И тунель нормально работал)

SPD [/usr/local/sbin/setkey -PD] - база политик безопасности. Политики безопасности указывают, какой именно трафик надо шифровать. И какой трафик приходит шифрованным. Если на одном сервере укажем, например, что исходящий трафик на порту 1701 надо шифровать, а на соседе не указаем, что на порт 1701 приходит шифрованный трафик, то них работать не будет.
Данная база может заполняться из setkey.conf путем setkey -f setkey.conf. Как говорил выше, в конфиге есть интересная опция generate_policy on;. Если на СЕРВЕРЕ ее поставить в on, то на сервере будут создаваться политики, соответствующие политикам на клиенте. Например, если на клиенте мы укажем, что исходящий трафик на порт 1701 шифровать, то на сервере автоматом создастся правило, что входящий трафик с данного хоста(клиента) на порт 1701 будет шифрованным. Для начала рекомендую поставить on и оставить политики на сервере пустыми. Они заполнятся в соответствии с клиентом. На клиенте же необходимо заполнять вручную. Если взаимодействие политик будет настроено неправильно, шифрование не заработает и SA не создастся, насколько я помню.
Пример файла на клиенте.
setkey.conf
# Чистим все.
flush;
spdflush;

# А вот и сами правила, что шифровать.
# добавляем правило на трафик от клиента(ип 2.2.2.2) с любым портом на сервер 
# (ип 1.1.1.1) порт 1701 по протоколу udp, трафик является исходящим, 
# шифруется, используется esp в режиме транспорта, обязательно.
spdadd 2.2.2.2[0] 1.1.1.1[1701] udp -P out  ipsec esp/transport//require;
# то же самое, только уже входящий (обратный) трафик. Параметры те же. 
# Если убрать одну их этих строк - шифрование будет только в одном направлении.
spdadd 1.1.1.1[1701] 2.2.2.2[0] udp -P in  ipsec esp/transport//require;

На всякий случай, конфиг ракуна для клиента.


path pre_shared_key "/usr/local/etc/racoon/psk.txt"; 

log info; 

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

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

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

remote 1.1.1.1 {
        exchange_mode aggressive,main;
        lifetime time 24 hour;
        my_identifier address;
        peers_identifier address;
        passive off;
        generate_policy off;
        proposal {
                encryption_algorithm 3des;
                hash_algorithm sha1;
                authentication_method pre_shared_key;
                dh_group 2;
        }
}

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

adminsock Нужен для управления ракуном через racoonctl (при этом ракун должен быть собран с соответствующей опцией). Ничо интересного там нету, но можно попробовать установить соединение через него.

Не забываем указать одинаковый пароль на клиенте и сервере в psk.txt.
При появлении трафика, попадающего под соответствующее правило, создадутся ключи и правила на сервере. Для теста, на клиенте можно попробовать racoonctl vc 1.1.1.1 (если собрано с админским контролем), а лучше запустить mpd или сгенерировать трафик, который собираемся шифровать. Сделать это необходимо на клиенте, т.к. о политиках шифрования знает только клиент. При попадании трафика под правило он обменяется ими с сервером.
Что б писались логи, добавляем в /etc/syslog.conf
!racoon
*.*                                            /var/log/racoon.log


И рестартим syslogd
Как правило,в логах все есть.
Пример баз на работающем клиенте
/usr/local/sbin/setkey -D

2.2.2.2 1.1.1.1
        esp mode=transport spi=176271203(0x0a81af63) reqid=0(0x00000000)
        E: 3des-cbc  c81782e2 cb844f5b 78c44a17 a9f1f815 a1a0e116 e0922af7
        A: hmac-md5  d8702bf1 ac42b5d9 aa93e0a6 d29be244
        seq=0x000000a3 replay=4 flags=0x00000000 state=mature
        created: Dec  8 11:06:28 2010   current: Dec  8 11:23:54 2010
        diff: 1046(s)   hard: 3600(s)   soft: 2880(s)
        last: Dec  8 11:23:45 2010      hard: 0(s)      soft: 0(s)
        current: 19344(bytes)   hard: 0(bytes)  soft: 0(bytes)
        allocated: 163  hard: 0 soft: 0
        sadb_seq=1 pid=1389 refcnt=2
1.1.1.1 2.2.2.2
        esp mode=transport spi=78827330(0x04b2cf42) reqid=0(0x00000000)
        E: 3des-cbc  4878e5af fa657ba6 38524d4f 0ef4781a a7897aec f1d9b5fe
        A: hmac-md5  f30c6fae ad7d3235 84d18049 8d380ea8
        seq=0x00000057 replay=4 flags=0x00000000 state=mature
        created: Dec  8 11:06:28 2010   current: Dec  8 11:23:54 2010
        diff: 1046(s)   hard: 3600(s)   soft: 2880(s)
        last: Dec  8 11:23:39 2010      hard: 0(s)      soft: 0(s)
        current: 5246(bytes)    hard: 0(bytes)  soft: 0(bytes)
        allocated: 87   hard: 0 soft: 0
        sadb_seq=0 pid=1389 refcnt=1

/usr/local/sbin/setkey -DP
1.1.1.1[1701] 2.2.2.2[any] udp
in ipsec
esp/transport//require
created: Dec 8 11:24:44 2010 lastused: Dec 8 11:25:10 2010
lifetime: 0(s) validtime: 0(s)
spid=16407 seq=1 pid=1543
refcnt=2
2.2.2.2[any] 1.1.1.1[1701] udp
out ipsec
esp/transport//require
created: Dec 8 11:24:44 2010 lastused: Dec 8 11:25:10 2010
lifetime: 0(s) validtime: 0(s)
spid=16406 seq=0 pid=1543
refcnt=2e

Тут с нулевым life-time, если у вас он установлен, должно добавится еще пару строк.
А tcpdump на внешнем инт-е показывает

10:27:07.106514 IP 2.2.2.2 > 1.1.1.1: ESP(spi=0x0348e9f1,seq=0x54), length 140
10:27:07.106617 IP 1.1.1.1 > 2.2.2.2: ESP(spi=0x02e88b28,seq=0x4c), length 140
10:27:08.107413 IP 2.2.2.2 > 1.1.1.1: ESP(spi=0x0348e9f1,seq=0x55), length 140
10:27:08.107524 IP 1.1.1.1 > 2.2.2.2: ESP(spi=0x02e88b28,seq=0x4d), length 140
10:27:09.108299 IP 2.2.2.2 > 1.1.1.1: ESP(spi=0x0348e9f1,seq=0x56), length 140
10:27:09.108390 IP 1.1.1.1 > 2.2.2.2: ESP(spi=0x02e88b28,seq=0x4e), length 140
10:27:10.109189 IP 2.2.2.2 > 1.1.1.1: ESP(spi=0x0348e9f1,seq=0x57), length 140
10:27:10.109311 IP 1.1.1.1 > 2.2.2.2: ESP(spi=0x02e88b28,seq=0x4f), length 140

При активности внутри l2tp.

Некоторые мелочи.

Для работы через фаерволы необходимо следующее:
порт isakmp для обмена ключами (IKE) и протокол esp для передачи трафика.
Правила в pf.conf на сервере.

pass in inet proto {tcp,udp} from 2.2.2.2 to 1.1.1.1 port \
isakmp flags S/SA keep state
pass in inet proto esp from 2.2.2.2 to 1.1.1.1 keep state

Для мониторинга "мертвых" соединений можно использовать подобие keep-alive
В секцию remote добавляем:

# DPD - dead peer detection. Обнаружение мертвых соседей.
# keep-alive раз в 10 сек.
dpd_delay 10;
# Сколько ждать ответа на keep-alive. По истечении - следующий. 
dpd_retry 5;
# Макс. количество неудач, после которых сосед считается мертвым.
dpd_maxfail 5;

Описания принципов работы не нашел, но, судя по логике, удаляет SA, если соединение умерло.

База SP (SPD - правила, которые указывают, какой трафик шифруем, загружается через setkey) обнуляется при перезагрузках, переодически при рестарте и т.п., что вызывает некие проблемы, если есть куча удаленных серверов, которые переодически вырубаются из-за отключения света и т.п. Решение следующее.

ipsec_enable="YES"
ipsec_file="/usr/local/etc/racoon/setkey.conf"

Это стандартный фряшный стартовый скрипт, который просто загружает правила из ipsec_file, чистит базу и т.п. и больше ничего не делает.

Если все заработало, но хочется чего-то большего, можно поиграться с аутентификацией.
В общем, есть 4 способа аутентификации:

1. PSK - аутентификация по паролю. Если пароль подошел, генерятся сертификаты и сервера обмениваются ими автоматически по IKE. Это мы уже сделали.
2. plain_rsa - для каждого направления трафика генерится пара ключей (не сертификатов!) - публичный и приватный. Публичный раздается для шифрования - приватный оставляем для расшифровки. Если соединение unix-unix, то использовать х509 сертификаты тут не имеет смысла. Но, подозреваю, что венда не умеет работать с чистыми ключами и поэтому при работе с вендой все же придется использовать х509 сертификаты. Но корневой сертификат тут будет абсолютно не при чем. В общем, это даже не аутентификация, а метод создания ключей. Ключи эти генерятся одной командой
plainrsa-gen -b 2048 -f /var/tmp/boston.keys

И из выходного файла копируется отдельно публичнй ключ. Гораздо проще, чем Х509. Если работаем с вендой, можно заюзать сертификаты Х509 как в 4-м пункте. Ничо не изменится. Многие, на мой взгляд, ошибочно принимают этот метод за аутентификацию.
Подробное описание по ключам в /usr/local/share/doc/ipsec-tools/README.plainrsa
Если в вашем конфиге по сертификатам есть только эти строки
        certificate_type x509 "client.crt" "client.key";
        peers_certfile x509 "server.crt";

То это именно этот метод. 90% конфигов именно такие, и люди думают, что у них аутентификация по сертификатам. Поверьте, это не так. Повторюсь, никакой аутентификации по сертификатам, по большому счету, нет. Ее можно сделать косвенно, если не разрешать серверу выдавать свой публичный ключ клиенту и хранить его на клиенте сразу. Но, по-умолчанию, выдача разрешена. С другой стороны, можно не копировать на клиента публичный ключ сервера, а заставить его просто стягивать с сервера при соединении. И не обязательно,что б у клиента и сервера был один СА. Сертификаты х509 используются как обычные рандомные ключи. Т.е., говоря простым языком, клиент может постучаться на сервер, сказать, что у него есть свой хзоткудавзятый публичный и приватный ключ, запросить у сервера его публичный ключ и отдать ему свой публичный.
По крайней мере, я взял клиента, сгенерил на нем пару ключей, не имея корневого с сервера и поднял тунель.
Если кто-то аргументированно опровергнет - с удовольствием выслушаю.

3. hybrid_rsa. Описывается в семплах, идущих с портом в каталоге /usr/local/share/examples/ipsec-tools/roadwarrior. Конфиги очень просты, рассматривать подробно смысла не вижу. Суть работы примерно как у ssh. Аутентифицируется не только клиент на сервере, но и сервер на клиенте, т.е. клиент проверяет подлинность сервера. На сервере хранится пара сертификата и ключа х509, а клиентам раздается корневой сертификат. Клиент аутентифицируется на сервере по логин-паролю, а сервер на клиенте по сертификатам. Если сертификаты сервера не подписаны корневым сертификатом клиента, доверия этому серверу нет. Примеры конфигов есть, там все тоже достаточно прозрачно.

4. Реальнай аутентификация по сертификатам с корневым сертификатом и списком отзыва.
И так, погнали.
Первое,что необходимо сделать, это создать сертификаты.
Есть много способов, но этот, на мой взгляд, один из самых прозрачных.

Для аутентификации по сертификатам необходим корневой сертификат (CA), ключ с паролем
для него и клиентский сертификат. Корневой сертификат один и мы принимаем все клиентские,
подписанные им. Для подписания клиентского сертификата мы используем корневой и ключ с паролем.
Для облегчения жизни создаем конфиг openssl.conf в текущей директории
# 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             = RU
countryName_min                 = 2
countryName_max                 = 2

stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = my_city obl

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

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

# 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  = my_unit

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

emailAddress                    = Email Address
emailAddress_max                = 64
emailAddress_default            = admin@my_company.ru

Генерим сертификаты.
1. Создаем папку /usr/local/etc/racoon/cert и переходим в нее. Так же создаем папки из конфига. Генерим  секретный ключ для корневого сертификата. Так же ключ необходимо запаролить. Этот пароль используется для подписания клиентских сертификатов, так что от его  сложности, по большому счету, зависит стойкость всех клиентских сертификатов. Пароль не восстанавливается, поэтому при его утере генерить новые клиентские сертификаты не получится.
openssl genrsa -des3 -out ca/ca.key 1024 -config openssl.conf

-des3      - метод шифрования.
-out ca.key - сохраняем ключ в файл ca.key текущей директории.
1024        - длина ключа в битах.

2. Генерим сам сертификат. Сертификат будет самоподписанным, т.к. CA у нас свой, а сертификат первый
  в иерархии.
openssl req -new -x509 -days 1095 -key ca/ca.key -out ca/ca.crt\
 -config openssl.conf

 req -new    - запрос на новый сертификат
 -x509       - тип сертификата (с открытым ключом - несимметричное шифрование).
 -days 1095 - "срок годности" сертификата, начиная с момента генерации (3 года).
 -key ca.key - файл с ключом для корневого сертификата.
 -out ca.crt - в этот файл сохраняем полученный корневой сертификат.

Эти действия проделываются 1 раз. Как правило, корневой сертификат один и им подписывается множество лиентских. При замене корневого так же будет необходимо заменить все клиентские.

Клиентские сертификаты.
Сертификаты для сервера и для клиента генерятся одинаково, т.к. отдельно шифруется исходящий и входящий трафик.
Пример для сервера, клиент аналогичен.

1. Генерим секретный ключ.
openssl genrsa -out server/server.key 1024 -config openssl.conf

2. Генерим запрос на сертификат.
openssl req -new -key server/server.key -out server/server.csr \
-config openssl.conf

3. Выдаем сертификат и подписываем его корневым. Тут вводим пароль к ключу корневого сертификата.
openssl x509 -req -days 365 -in server/server.csr -CA ca/ca.crt \
-CAkey ca/ca.key -CAcreateserial -out server/server.crt

В результате мы получаем клиентский сертификат для сервера - server.crt. Запрос на создание - server.csr можно удалить.

Сертификат для клиента. Метод создания аналогичен серверному.

openssl genrsa -out client/client.key 1024 -config ./openssl.conf
openssl req -new -key client/client.key -out client/client.csr \
-config ./openssl.conf
openssl x509 -req -days 365 -in client/client.csr -CA ca/ca.crt \
-CAkey ca/ca.key -CAcreateserial -out client/client.crt

Что бы можно было установить сертификат на венду, его надо преобразовать в формат PKCS(Public Key Cryptography Standards). Установку его на венду оставлю любителям оной, вроткампот эту ос.
openssl pkcs12 -export -inkey client/client.key \
-certfile ca/ca.crt -in client/client.crt -out client/win-client.p12

Проверить и посмотреть сертификаты можно так:
openssl req -in server/server.csr  -noout -verify -key server/server.key
openssl x509 -noout -issuer -subject -dates -in client.crt
openssl x509 -text -in client.crt

Certificate Revocation List - список отозванных сертификатов. Если секртификат скомпрометирован, можно, конечно, поменять СА и все сертификаты, подписанные им, перестанут работать. Но есть менее радикальный способ - сертификат можно просто отозвать.Создаем список отозванных сертификатов.

1. Генерим список отозванных сертификатов. После добавления сертификата в список его необходимо переинициализировать ентой же командой. Не звабываем установить серийник.
cat > ca/crlnumber
01
^D
touch ./ca/index.txt
openssl ca -gencrl -keyfile ca/ca.key -cert ca/ca.crt \
-out ca/crl.pem -config opensss.conf

2. Отзываем сертификат. Это уже реальный отзыв, так что не надо свежесозданный сертификат тут же машинально отзывать)
openssl ca -revoke server.crt -crl_reason affiliationChanged \
-config opensss.conf

3. Смотрим список отозванных сертификатов.
openssl crl -in ca/crl.pem  -text -noout

4. Что б ракун его увидел, делаем такую штуку
cd /usr/local/etc/racoon/cert
ln -s ca/crl.pem `openssl crl -noout -hash < ca/crl.pem`.r0


И так, сертификаты есть, делаем аутентификацию по сертификатам.
Серверный сертификат с ключом кладем на сервер, клиентский на клиента. Таким образом, мы получаем, что множество клиентов могут логиниться по секции anonymous с различных адресов каждый со своим сертификатом.
(не надо на сервере указывать peers_certfile, т.к. эта запись может быть только одна на секцию) При установлении соединения, сервер запрашивает у клиента его публичный ключ, а клиент у сервера. Локально мы их не храним. Все. сервера обменялись ключами. У сервера свой публичный и приватный + полученный публичный от клиента, у клиента аналогично. Далее аутентификация. На сервере мы указываем корневой СА сертификат, по которому он проверяет клиентские и включаем эту проверку. На клиенте можно сделать аналогично, но смысла не вижу, поэтому клиент просто доверяет сертификату, пришедшему с сервера. И так, если клиентский сертификат подписан тем же СА, что указан на сервере, аутентификация пройдена. Конфиги:
Сервер
remote anonymous {
        exchange_mode aggressive,main;
        doi ipsec_doi;
        lifetime time 24 hour;
        passive on;
        generate_policy on;
        proposal_check obey;
        nat_traversal on;
        ike_frag on;
        my_identifier asn1dn;
        peers_identifier asn1dn;
        verify_identifier on;
        # Посылаем запрос на сертификат
        send_cr on;
        # Проверяем пришедший сертификат.
        verify_cert on;
        # Разрешаем отправку своего сертификата 
        send_cert on;
        # Непосредственно сертификат и ключ сервера
        certificate_type x509 "server/server.crt" "server/server.key";
        # Корневой сертификат для проверки
        ca_type x509 "ca/ca.crt";
        #peers_certfile x509 "client/client.crt";

        proposal {
                encryption_algorithm 3des;
                hash_algorithm sha1;
                authentication_method rsasig;
                dh_group 2;
        }
}

На клиенте соответствующий кусок.

        verify_identifier on;
        #
        verify_cert off;
        send_cert on;
        send_cr on;
        certificate_type x509 "client.crt" "client.key";
        #peers_certfile x509 "server.crt";

        proposal {

Не забываем дать права на чтание сертификатов пользователю, от которого запущен ракун.
Проверяем. Соединение должно нормально установиться. Далее можно попробовать поменять клиентский сертификат на левый, с другим СА, или непосредственно СА-ключ. В логах получаем примерно следующее
ERROR: the peer's certificate is not verified.

А на клиенте
ERROR: fatal INVALID-CERT-AUTHORITY notify messsage, phase1 should be deleted

Пробуем отозвать сертификат. Не забываем после отзыва пересоздать базу - пункт 1 (только строчка с gencrl)
В логах получаем следующее.
ERROR: certificate revoked(23) at depth:0

В качестве дополнения. Причины отзыва сертификатов:
CRLReason ::= ENUMERATED {
    unspecified             (0),
    keyCompromise           (1),
    cACompromise            (2),
    affiliationChanged      (3),
    superseded              (4),
    cessationOfOperation    (5),
    certificateHold         (6),
    removeFromCRL           (8),
    privilegeWithdrawn      (9),
    aACompromise           (10)}

Как удалить сертификат из списка отзыва средствами openssl не нашел. По-деревенски можно просто удалить строку из ca/index.txt и пересобрать базу. removeFromCRL вроде как не работает..

Если хочется запускать ракун не от рута, добавялем непривелигированного пользователя и его uid-gid прописываем в секции privsep, а так же снимаем коменты с верхних строк и делаем папку с pid-ом доступной для записи этим пользователем. Поскольку в данном режиме запускается 2 процесса ракуна - один главный под рутом, второй под нашим пользователем (он слушает порты), то штатный rc.d скрипт не может с ним корректно работать. На скорую руку сделал так
cat rc.d/racoonperuser
#!/bin/sh            

# Start or stop racoon
# $FreeBSD: ports/security/ipsec-tools/files/racoon

# PROVIDE: racoon
# REQUIRE: DAEMON
# BEFORE: LOGIN  
# KEYWORD: shutdown

# Define these racoon_* variables in one of these files:
#       /etc/rc.conf                                    
#       /etc/rc.conf.local                              
#       /etc/rc.conf.d/racoon     
# 
# DO NOT CHANGE THESE DEFAULT VALUES HERE 
#      
[ -z "$racoonperuser_enable" ] && racoonperuser_enable="NO"

. /etc/rc.subr

name="racoon"
rcvar=`set_rcvar racoonperuser_enable`
command="/usr/local/sbin/${name}" 
pidfile="/var/run/${name}/${name}.pid"
socketfile="/var/db/racoon/${name}.sock"
required_files="/usr/local/etc/${name}/${name}.conf" 
required_dirs="/var/db/racoon" 
stop_cmd="racoon_stop" 
start_precmd="racoon_cleanup"
stop_postcmd="racoon_cleanup"               

racoon_cleanup() {
        /bin/rm -f ${pidfile}
        /bin/rm -f ${socketfile}
}

racoon_stop() {
 # Команду остановки пишем свою, т.к. стандартная не
 # останавливает под непривелегированным пользователем.
 result=`pgrep racoon`
 if [ -z "$result" ] ; then
        echo "was racoon running?"
        return 0;
 fi

 kill -TERM `pgrep racoon`
 sleep 1;
 result=`pgrep racoon`
 if [ -n "$result" ] ; then
        echo "preblem while stopping racoon - \"$result\""
        return 1;
 fi
 echo "racoon succesfully stopped"
 return 0;

}

load_rc_config $name

if [ "$1" = start ] && checkyesno "${name}_create_dirs"; then
        /bin/mkdir -p $required_dirs
fi

run_rc_command "$1"

Писал уже после того, как сделал, поэтому возможны неточности. Добавляйте-исправляйте)

Понадобилось еще подключать вендовых ipsec\l2tp клиентов. Еще раз убедился, что если не знаешь, лучше не лезть, но, т.к. вендовый админ в командировке, пришлось делать самому. Короче, венда.
На венду копируем win-client.p12 и ca.crt. Насчет последнего - попрос, т.к. р12 уже, по идее, его содержит. А вот дальше самое интересное. Зачем так сделано, я так и не понял. Сертификаты можно установить, просто кликнув на них как на .exe. Но не тут-то было. Установленные таким образом сертификаты с l2tp работать не будут. Ошибка 781 при подключении. Делаем пуск->выполнить->mmc. Добавляем центр управления сертификатами. В местере указываем локальный компьютер. А вот мастер установки при щелкании по сертификату ставит его "для текущего пользователя" и l2tp клиент его не видит.
Сгрыз не один монитор, пока понял. Добавляем клиентский сертификат в личные и, если хотим, что б все было кошерно, корневой в доверенные центры. Дальше настраиваем обычное vpn соединение мастером. В свойствах тип указываем l2tp/ipsec. Другие параметры, как то: ставить парольную фразу, делать аутентификацию по сертификатам и т.п., менять не надо. Не забываем, что IPSec работает отдельно, а l2tp клиент отдельно.

Немного по поводу клиента за натом и nat-t. Венда, начиная со второго сервис-пака, начиает его понимать. Но соединиться из-за ната не полуается. Кому интересны подробности - в форуме на 2-ой странице по ссылке снизу. Там же и подробные симптомы. Вкратце, отсутствует поддержка NAT-OA (rfc3947), который позволяет обновлять чек-суммы пакетов при получении (не забываем, нат меняет пакеты). Для tcp эта проверка обязательна. Для udp (в т.ч. l2tp) проверку можно отключить. Но с mpd это не помогает. В результате пакеты дропаются как не прошедшие проверку чек-сумм. Проверяем так:
netstat -s -p udp
...
        61 with bad checksum
        1316 with no checksum

...

PS. Я уже просто охренел исправляя
лишком длинная (92 символов) строка внутри тегов [code]:

поэтому, если где-то двойные коменты, закоментированы нужные параметры или кикнута невзначай часть текста  - просьба отписываться. Для вас старался)
Имена и фамилии изменены (с)
PS. с удовольствием выслушаю здоровую критику или дополнения в форуме.



размещено: 2010-12-13,
последнее обновление: 2010-12-15,
автор: Al


mak_v_, 2010-12-13 в 15:44:27

Статья однозначно полезная.
Пы.Сы. У себя отказался от айписега вообще и навсегда. В какой-то момент (после 30-го филиала) вылезла вся паршивость айписека, после этого перехали на openvpn и забыли про головняки. Но это сугубо личное мнение.

Al, 2010-12-13 в 15:58:36

Можно поподробнее про паршивость?) И, желательно, по ссылке в форум. http://forum.lissyara.su/viewtopic.php?f=14&t=30106&start=0

cth`uf, 2010-12-13 в 21:51:24

Отличная статья! Полгода назад бился-бился с l2tp/ipsec и так и не смог заставить работать ipsec.
Спасибо!

Mizev, 2010-12-14 в 18:23:01

За статью спасибо! Было дело боролся с l2tp/ipsec, но так и не поборол. Точнее одолел, если без NAT, всё просто супер, а вот ч-з NAT уговорить не удалось...
Сейчас пользую openvpn.

mak_v_, 2010-12-14 в 18:43:24

именно.... паршивости - работа его через прокси,  работа в пределах 1 tcp или udp коннекции , бриджинг с арп (очень закрутились пока построили), да и уязвимостей поболее и пожёстче находят в нём. Но решать Вам. Статья однозначно полезная.

Al, 2010-12-14 в 19:38:11

На сайте микрософт в траблшутингах IPSec нашел практически дословно следующее: "если не устанавливается IPSec, проверьте, нет ли маршрутизаторов, использующих NAT на пути соединения".
Не знаю, насколько это устарело, но, походу, в XP поддержки NAT-T нет. Остается надеяться, что IPSec в транспортном режиме пройдет через нат. Завтра буду пробовать.

RaUs, 2011-10-24 в 15:52:52

Спасибо большое ! Отличная статья !
Для аутентификации по pre_shared_key и сертификатам X.509 в качестве клиента для Windows XP использовал VPN Client 2.1.7 от Shrew Soft. Для него в частности нет необходимости конвертировать сертификаты в формат .p12 и добавлять в Центр Управления Сертификатами - Shrew и так понимает сертификаты в формате .crt

sheva.sv, 2011-12-13 в 12:34:00

Когда есть IPSEC тунель и нужно в него завернуть определенный трафик с локальной сети через NAT, в качестве нат-а лучше использовать ng_nat. У меня с ipfw nat не получилось.
FreeBSD 8.2-RELEASE-p4
net.inet.ip.fw.one_pass: 0

Ieshua, 2012-07-19 в 10:17:51

sheva.sv,очень интересно, как Вам это удалось. Вы использовали какой режим туннельный или транспортный? Какие опции ядра использовали? IPSEC_FILTERTUNNEL? Было бы неплохо Ваш опыт изложить в виде статьи, я думаю многим это было бы интересно.

Al, 2012-07-19 в 10:29:07

Ieshua, нет никакой разницы, какой трафик заворачивать в IPSec. Не важно, трафиг генерит сам сервер (поднятие тунеля) или пакеты проходят сквозь него (нат). В случае с тунелем все равно- обычный тунель или c использованием IPSec. Статей про поднятие тунелей полно. Как наложить сверху IPSec на любой трафик (в т.ч. тунель), написано в статье.
Если шифровать просто трафик, преобразованный правилом nat, тоже самое. Если трафик проходит через nat и все работает, как наложить шифрование на любой трафик между двумя серверами написано в этой статье.



 

  Этот информационный блок появился по той простой причине, что многие считают нормальным, брать чужую информацию не уведомляя автора (что не так страшно), и не оставляя линк на оригинал и автора — что более существенно. Я не против распространения информации — только за. Только условие простое — извольте подписывать автора, и оставлять линк на оригинальную страницу в виде прямой, активной, нескриптовой, незакрытой от индексирования, и не запрещенной для следования роботов ссылки.
  Если соизволите поставить автора в известность — то вообще почёт вам и уважение.

© lissyara 2006-10-24 08:47 MSK

Время генерации страницы 0.0867 секунд
Из них PHP: 54%; SQL: 46%; Число SQL-запросов: 86 шт.
Исходный размер: 108424; Сжатая: 25214