VPN-сервер для офисной сети

Вступление

Статья представляет из себя набор заметок и примеров конфигурационных файлов офисного vpn-сервера, ничего революционного здесь не предложено, чего бы нельзя было найти в официальной документации. Здесь всего лишь в сжатом виде собрана вся(или не вся) нужная информация для запуска офисного сервера и главное для базового понимания что и как работает.

Основы криптографии

В данном разделе очень кратко и очень грубо описаны базовые криптографические принципы. Этот раздел может содержать множество не точностей и умышленных упрощений в описании тех или иных процессов. Основная задача этого раздела дать базовое понимание принципов работы криптографических протоколов. Для более детального изучения вопроса рекомендуется обратиться к специализированной документации, например есть ссылки в списке литературы.

Итак. Алгоритмы шифрования бывают симметричные и ассиметричные. Симметричный алгоритм представляет из себя один единственный ключ(и/или пароль) с помощью которого некоторый набор данных может быть зашифрован и с помощью этого же ключа он может быть расшифрован. Соответственно этот ключ является серкретным и для безопасного обмена данными должен быть распространен между участниками обмена по безопасным каналам вручную заранее до сеанса обмена. Учитывая ограничения алгоритма, в основном он используется в таких программах, как шифрование диска(truecrypt, diskcryptor) и подобных. Основные представители этого алгоритма: DES, AES, IDEA, RC4. Типичные размеры ключей 64, 128, 192 или 256 бит.

Ассиметричный алгоритм использует пару ключей - открытый и закрытый. Открытый ключ секретом не является и может распространяться свободно, тогда как закрытый ключ должен быть защищен от посторонних. Основная идея такой системы, что с помощью открытого ключа можно зашифровать данные и расшифровать их получится только закрытым ключем. Таким образом, сторона А может передать стороне Б свой открытый ключ и сторона Б сможет зашифровать с помощью него данные и отправить стороне А, которая в свою очередь сможет эти данные расшифровать своим закрытым ключем. У данного алгоритма есть одна интересная особенность, которая используется в цифровых подписях(о них ниже): данные зашифрованные закрытым ключем, могут быть расшифрованы открытым ключем, запомним эту особенность. Основные представители этого алгоритма: RSA, ECC. Типичные размеры ключей RSA 512, 1024, 2048.

Есть еще такая штука как Хеш(контрольная сумма, отпечаток, digest) - это такой алгоритм, который с помощью математики может сгенерить строку фиксированной длины для любого объема данных. Т.е. берутся нужные данные(любого объема) и используя один из алгоритмов вычисляется хеш этих данных, т.е. некоторая строка фиксированной длины, которая не зависит от размера данных. При малейшем изменении исходных данных хеш меняется. Хеши используются для контроля целостности данных, когда шифрование самих данных не нужно. Хеш сам по себе не гарантирует, что по пути данные не были злонамеренно подменены, т.к. злоумышленник может подменить данные и сгенерить новый хеш, но зато он гарантирует что данные не были испорчены по пути из-за технических сбоев, ведь если по пути данные были хоть немного повреждены(либо сам хеш), то на стороне получателя это будет выявлено при проверке. Наиболее известные представители: MD5 (Message Digest 5), SHA-1, SHA-224, SHA-256, SHA-384 и SHA-512. Пример хеша MD5: d29aaa0b9cd402b4bfe2395a805f9ada.

Особенность ассиметричного алгоритма используется для технологии цифровых подписей, т.е. когда данные могут быть зашифрованы закрытым ключем, а расшифрованы его открытым ключем. Цифровые подписи используются не для защиты данных от прочтения посторонними, а для гарантирования того, что данные, во-первых, получены от идентифицированно источника(от того, за кого он себя выдает), во-вторых, в неизмененном виде. Достигается это следующим путем: отправитель с данных снимает отпечаток(хеш) и вместе с данными шифрует закрытым ключем(подписывает). Теперь принимающая сторона, во-первых, расшифровывает данные открытым ключем - данный факт подтверждает, что данные были зашифрованы закрытым ключем, который есть только у отправителя, а значит данные точно получены именно от него, а не от кого другого. Во-вторых, сверяет расшифрованные данные с отпечатком(хешем), если они совпадают, значит данные не были изменены или испорчены по пути. Злоумышленник может перехватить данные и расшифровать их(т.к. открытый ключ свободно доступен), но он не может подменить данные, т.к. для этого ему надо сгенерить новый отпечаток и зашифровать его месте с данным обратно, но закрытого ключа у него нет. Таким образом, цифровая подпись гарантирует, что данные получены в неизменном виде и именно от того, от кого мы ожидали(не рассматривает тот вариант, когда закрытый ключ украден). Наиболее распространенные алгоритмы RSA-MD5, RSA-SHA-1, RSA-SHA-256, RSA-SHA-384, DSA и ECDSA. Есть еще такая разновидность цифровых подписей(как я понял) аутентификационный код сообщения (Message Authentication Code, MAC), тоже бывает разных типов в зависимости от используемого алгоритма хеширования.

Есть еще такая штука, как контейнер X.509, который представляет из себя набор данных: открытый ключ, цифровую подпись удостоверяющего центра(СА), уникальное имя и прочую дополнительную информацию. Этот контейнер называется сертификат. Сертификаты бывают самоподписанные и подписанные удостоверяющим центром(СА). Разница лишь в том, что будет ли на вашем сертификате подпись «авторитетного» центра или нет. Аторитетность центра определяется насколько его корневой сертификат известен в раличных операционных системах. CA - корневой сертификат центра сертификации. Взглянем на это с практической стороны. Допустим у нас есть https сервер, мы устанавливаем с ним сеанс связи и наш компьютер пытается проверить его сертификат. Первым делом он ищет в своей локальной базе доверенных корневых сертификатов корневой сертификат того центра, который подписал сертификат веб-сервера. Если такой находится и сертификат действительно был подписан этим центром и при этом сертификат(веб-сервера) не числится в базе отозванных сертификатов, то считается, что все прошло успешно. Если же не нашлось подходящего корневого сертификата(в случае самоподписанного сертификата, либо сертификат был подписан малоизвестным центром, корневой сертификат которого отсутствует в нашей ОС), то пользователь увидит устрашающее сообщение, что подлинность сертификата сервера не удалось проверить. Но в данном случа можно добаваить сертифика в список доверенных и продолжать работать. Итак, еще раз: известный удостоверяющий центр подписывает наш сертификат своим закрытым ключем, открытый ключ удостоверяющего центра(корневой сетификат) уже есть в нашей ОС, как мы помним, именно с помощью открытого ключа можно проверить, действительно ли данные были подписаны тем, кем надо.

Как уже упоминалось выше, сертификат может фигурировать в базе отозванных сертификатов. Хоть у сертификата есть срок действия, может возникнуть ситуация, когда действие сертификата нужно завершить раньше истечения его срока действия. Существуют различные варианты подобных «черных списков» сертификатов. Самый простой: Certificate Revocation List, CRL - база данных отозванных сертификатов, это обыкновенный файл со списком отозванных сертификатов. Его просто использовать с малым числом сертификатов, но в случае с удостоверяющими центрами, подобная база может достигать внушительных размеров и клиенту каждый раз ее скачивать достаточно накладно. Есть альтернативные варианты, например Online Certificate Status Protocol, OCSP - протокол онлайн проверки валидности сертификатов. Это альтеренатива спискам отозванных сертификатов(CRL). Сервис OCSP обязателен только для EV (Extended Validation) сертификатов, для остальных типов опционален. Вся эта система пользовательских сертификатов, центра сертификации и БД отозванных сертификатов называется PKI - Public Key Infrastructure.

Вообще сам по себе ассиметричный алгоритм для шифрования данных не используется из-за достаточно высокого потребления ресурсов. Он используется только на этапе согласования и установки соединения для идентификации участников, после чего уже сам сеанс связи шифруется симетричным протоколом. Но как известно, у симетричного протокола есть проблема с распространением ключей, т.е. они должны быть предварительно предоставлены все сторонам участникам диалога, а это ломает всю логику установки защищенного соединения между двумя случайными собеседниками. Тут на помощь приходит протокол Diffie-Hellman(DH). С помощью этого протокола две стороны(или более) генерят общий секретный ключ не обмениваясь секретными данными, посредством которого в дальнейшем выполняется шифрование передаваемых данных(одним из алгоритмов симметричного шифрования). Этот протокол избавил от необходимости обмениваться секретными ключами по защищенным каналам связи, наоборот на этапе генерации общего ключа все данные передаются в открытую. Но этот протокол уязвим к атаке «человек по середине», т.е. он надежен только в том случае, если данные на этапе генерации не были подменены третьей стороной. Именно для этих целей и используется асиметричный алгоритм, т.е. с помощью цифровых подписей гарантируется неизменность данных на этапе обмена сторон и генерации общего секретного ключа симетричного шифрования. Ну и соответственно после генерации такого ключа дальше уже обмен данными выполняется в зашифрованном виде. Открытые параметры(DH ключ) как правило генерятся на одной стороне(сервером) и при установке сеанса связи передаются другой.

Ну вот, вроде, и все. Подводя итог: асиметричный алгоритм используется для аунтификации сторон и для контроля идентичности данных на этапе соглосования соединения, а симетричный протокол используется для шифрования потока данных уже после установки соединения. Небольшой абстрактный пример установки сеанса связи(https):

  • Согласование версии протокола шифрования и прочих параметров(сжатие и т.д.)
  • Сервер отсылает свой сертификат с открытым ключем, который клиент может использовать для шифрования сообщений серверверу
  • Клиент вычисляет pre-master key, шифрует его открытым ключем сервера и отправляет серверу. Далее обе стороны независимо вычисляют master key
  • Клиент шифрует все сообщения, полученные в процессе рукопожатия, и отправляет серверу. Шифруются они ранее выбранным алгоритмом шифрования объемных данных и хешируются MAC. Далее сервер пытается это все расшифровать с помощью вычисленного им сеансового ключа, если все получилось, значит все ОК.
  • Аналогично поступает сервер. Если клиент смог все расшифроват вычисленным им сеансовым ключем, значит все ОК.
  • Соединение можно считать установленным. Все сообщения с этого момента будут передаваться зашифрованными алгоритмом шифрования объемных данных и включать MAC.

Криптография в OpenVPN

Еще на шаг ближе к практике. Рассмотрим как все вышеописанное применяется в OpenVPN. Как и положено создается структура PKI, на стороне сервера создается корневой сертификат и закрытый ключ(ca.crt и ca.key). Корневой сертификат раздается всем клиентам, а корневой закрытый ключ используется для подписи сертификата сервера и всех клиентских сертификатов. Хранить его на сервере совсем не обязательно(он используется только подписывания), а если вы параноик, то даже не желательно. Также у сервера есть своя пара сертификат/ключ. Сертификат сервера используется клиентами для его идентификации(в OpenVPN используется двусторонняя аунтификация, т.е. клиент тоже аунтифицирует сервер). Еще у сервера есть DH ключ, как описано выше, с помощью него согласовывается ключ симетричного шифрования. Ну и соответственно у каждого клиента своя пара сертификат/ключ(по сертификату сервер аунтифицирует клиента), в сертификате клиента важное поле CN - его значение принимается за имя клиента. Таким образом, сервер и клиент аунтифицируют друг друга на этапе установки соединения, после чего генерят ключ симетричного шифрования с помощью DH и переходят на шифрование потока данных.

В качестве дополнительной защиты в OpenVPN есть возможность использовать HMAC подписи(опция tls-auth). Для этого генерируется отдельный ключ, который распространяется среди всех клиентов через защищенный канал, затем этот ключ, как я понял, используется на этапе согласования подключения, каждый пакет данных на этом этапе подписывается HMAC и если пакет полученный от клиента имеет не корректную HMAC подпись, то соединение сбрасывается(пакет игнорируется). Данная процедура позволяет избежать некоторых проблем, например DOS-атаки, сканирование портов и т.д.

В итоге, в общем случае клиент имеет три файла: ca.crt(не секрет), client.crt(не секрет) и client.key(секрет). Сервер имеет четыре файла: ca.crt(не секрет), server.crt(не секрет), dh2048.pem(не секрет) и server.key(секрет). В случае использования tls-auth ключей, все участники имеют еще ta.key(секрет).

Настройка сервера

Настройка будет проводиться на примере Debian 7, но в других дистрибудтивах принципиально ничем не отличается. Переходим в каталог /usr/share/doc/openvpn/examples/easy-rsa/2.0 и редактируем файл vars, тут задаются некоторые переменные среды, которые используются скриптами при генерации сертификатов, например указываем каталог, в который генерить сертификаты, увеличиваем длину ключа до 2048 бит и по желанию редактируем срок действия сертификатов(корневого и клиентских):

export KEY_DIR="/etc/openvpn/keys"
export KEY_SIZE=2048
export CA_EXPIRE=3650
export KEY_EXPIRE=3650

В самом конце файла задаются значения по-умолчанию для информационных полей сертификатов, поле KEY_CN(Common Name) желательно оставить пустым, чтобы при каждой генерации сертификата его задавать вручную, т.к. это поле используется как имя клиента и для каждого клиента нужно иметь уникальное KEY_CN, т.к. по этому имени потом можно будет задавать персональные настройки тонеля.

export KEY_COUNTRY="RU"
export KEY_PROVINCE="RU"
export KEY_CITY="Voronezh"
export KEY_ORG="Roga & KO"
export KEY_EMAIL="admin@domain.ru"
export KEY_EMAIL=admin@domain.ru
export KEY_CN=
export KEY_NAME=Roga
export KEY_OU=KO
export PKCS11_MODULE_PATH=changeme
export PKCS11_PIN=1234

Перед тем как выполнять какие-либо действия с сертификатами нужно загрузить переменные среды:

# . ./vars

Запустим скрипт очистки сертификатов, он нам создаст и инициализирует необходимые файлы:

# ./clean-all

В результате должы появиться следующие файлы:

# ls -1 /etc/openvpn/keys
index.txt
serial

Далее генерим корневой сертификат, в качестве Common Name указываем что-то вроде «OpenVPN Server CA»

# ./build-ca

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

Далее генерим сертификат сервера, качестве Common Name указываем что-то вроде «server»:

# ./build-key-server server

Дальше ключ DH(Diffie Hellman):

# ./build-dh

Также в качестве дополнительной защиты(HMAC-firewall) сгенерим еще один ключик:

# openvpn --genkey --secret /etc/openvpn/keys/ta.key

Осталось сгенерить клиентские сертификаты. Генерим такое кол-во, которое нужно и в качестве CN указываем интуитивно понятное имя, чтобы по нему можно было идентифицировать клиента:

./build-key client1
./build-key client2
./build-key client3

При генерации клиентских ключей можно дополнительно защитить их паролем на случай их кражи или потери. Вместо скрипта build-key нужно использовать build-key-pass, тогда при генерации ключа будет запрошен пароль(Enter PEM pass phrase). В этом случае у клиента при каждом подключении будет запрашиваться пароль. Сменить его нельзя и если клиент забыл пароль, то поможет только отзыва сертификата и перевыпуск ключа.

Конфигурационный файл сервера /etc/openvpn/server.conf:

# Адрес на котором слушать, порт, протокол и тип интерфейса(маршрутизируемый)
local 7.7.7.7
port 1194
proto udp
dev tun

# Корневой сертификат сервера
ca keys/ca.crt
# Сертификат сервера
cert keys/server.crt
# Закрытый ключ сервера
key keys/server.key
# БД отозванных сертификатов, пока опция закоментирована, до первого отозванного сертификата
#crl-verify /etc/openvpn/keys/crl.pem
# Статический ключ для дополнительной защиты(на стороне сервера вторая опция 0, на стороне клиента 1)
tls-auth keys/ta.key 0
# Ну и ключ протокола DH
dh keys/dh2048.pem

# Указываем диапазон виртуальных адресов, который будет выдаваться клиентам(также эта опция указывает, что будет запущен многоклиентый сервер). 
server 192.168.0.0 255.255.255.0
# Эта опция указывает на то, что нужно запоминать какие адреса выдавались каким клиентам, для того чтобы при след. подключении дать такие же.
ifconfig-pool-persist ipp.txt
# И опция указывающая на каталог с персональными настройками клиентов
client-config-dir ccd

# Посылать пинги каждые 10 сек. и если в течении 60 сек. ни одного не вернулось, считать соединение мертвым.
keepalive 10 60

# Меняем дефолтный алгоритм симетричного шифрования на более суровый(на клиенте обязательно сделать тоже, иначе не заработает)
cipher AES-256-CBC

# Включаем сжатие трафика и указываем максимальное кол-во клиентов
comp-lzo
max-clients 10

# Для повышения безопасности принуждаем сервер работать от имени непривелегированного пользователя
user nobody
group nogroup

# Не перечитывать ключи и не выполнять up/down скрипты при получении SIGUSR1 или ping-restart
persist-key
persist-tun

# Настраиваем логирование, статус файл по активным соединениям, лог событий с сохранением истории и уровень логирования 3.
status /var/log/openvpn/openvpn-status.log
log-append  /var/log/openvpn/openvpn.log
verb 3

# Ну и включаем интерфейс управления через telnet на локалхосте и порту 7505  
management localhost 7505

Теперь создаем каталог для логов, запускаем сервер и проверяем, что он ждет подключения на порту 1194:

# mkdir /var/log/openvpn
# /etc/init.d/openvpn start
[ ok ] Starting virtual private network daemon: server.

# netstat -lnptu | grep 1194
udp        0      0 7.7.7.7:1194         0.0.0.0:*                           13088/openvpn

Чтобы лог-файл со временем не занял все место создадим такой файл /etc/logrotate.d/openvpn:

/var/log/openvpn/openvpn.log {
        daily
        missingok
        copytruncate
        rotate 14
        compress
        notifempty
}

И в заключении зададим клиенту client1 персональные настройки, для этого создаем файл /etc/openvpn/ccd/client1:

ifconfig-push 192.168.0.241 192.168.0.242
push "dhcp-option DNS 8.8.8.8"
push "route 0.0.0.0 0.0.0.0"

Тут статически задан адрес клиента 192.168.0.241, также переданы ему адрес DNS-сервера и маршрут(в данном случае маршрут по-умолчанию).

Адреса клиентов надо выбирать из этого пула:

[  1,  2] [  5,  6] [  9, 10] [ 13, 14] [ 17, 18]
[ 21, 22] [ 25, 26] [ 29, 30] [ 33, 34] [ 37, 38]
[ 41, 42] [ 45, 46] [ 49, 50] [ 53, 54] [ 57, 58]
[ 61, 62] [ 65, 66] [ 69, 70] [ 73, 74] [ 77, 78]
[ 81, 82] [ 85, 86] [ 89, 90] [ 93, 94] [ 97, 98]
[101,102] [105,106] [109,110] [113,114] [117,118]
[121,122] [125,126] [129,130] [133,134] [137,138]
[141,142] [145,146] [149,150] [153,154] [157,158]
[161,162] [165,166] [169,170] [173,174] [177,178]
[181,182] [185,186] [189,190] [193,194] [197,198]
[201,202] [205,206] [209,210] [213,214] [217,218]
[221,222] [225,226] [229,230] [233,234] [237,238]
[241,242] [245,246] [249,250] [253,254]

Настройка клиента

На стороне клиента также устанавливается OpenVPN. В данном случае в качестве клиента рассмотрим Windows. Устанавливаем OpenVPN с GUI и создаем каталог C:\Program Files\OpenVPN\config. В этом каталоге создаем подкаталог с именем клиента, например client1 и копируем туда сертификаты корневой и сертификат клиента, статический ключ и ключ клиента. Создаем конфигурационный файл client1.ovpn:

# Указываем что мы клиент, тип интерфейса, протокол и адрес сервера. Директив remote с адресом сервера может быть больше одной,
# в этом случае клиент будет последовательно пытаться подключиться к серверам.
client
dev tun
proto udp
remote 7.7.7.7 1194
nobind
# При указании этой дериктивы, клиент будет выбирать сервер из списка случайно, таким образом можно обеспечить балансировку нагрузки.
;remote-random

# Пытаться установить подключение бесконечно. Удобно на компьютерах с непостоянным доступом в интернет.
resolv-retry infinite

# Аналогично, как на сервере, не перечитывать ключи при перезапусках
persist-key
persist-tun

# Может понадобиться раскоментить, если работаете через WiFi
;mute-replay-warnings

# Посылать пинги каждые 10 сек. на протяжении минуты, если ответа нет, соединение считается разорванным.
keepalive 10 60

# Пути к сертификатам и ключам относительно каталога config
ca "client1\\ca.crt"
cert "client1\\client1.crt"
key "client1\\client1.key"
tls-auth "client1\\ta.key" 1

# Алгоритм шифрования данных
cipher AES-256-CBC

# Включаем сжатие
comp-lzo

# И уровень логирования
verb 3

Теперь можно запустить GUI и попробовать подключиться.

Отзыв сертификатов

Иногда может случиться такая ситуация, что выданный сертификат нужно заблокировать раньше, чем истечет срок его действия, для этого существует процедура «отзыва сертификата», .т.е. он просто добавляется в БД отозванных сертификатов и сервер при подключении проверяет это. Допустим мы хотим отозвать сертификат client1, переходим в каталог /usr/share/doc/openvpn/examples/easy-rsa/2.0 и выполняем следующие команды:

# . ./vars

# ./revoke-full client1
Using configuration from /usr/share/doc/openvpn/examples/easy-rsa/2.0/openssl-1.0.0.cnf
Revoking Certificate 02.
Data Base Updated
Using configuration from /usr/share/doc/openvpn/examples/easy-rsa/2.0/openssl-1.0.0.cnf
client1.crt: C = RU, ST = RU, L = Voronezh, O = Roga & KO, OU = Roga, CN = client1, name = Roga, emailAddress = admin@domain.ru
error 23 at 0 depth lookup:certificate revoked

Не смотря на то, что в последнем сообщении содержится «error 23», на самом деле это значит, что операция прошла успешно. В результате выполнения этой команды должен был появиться файл БД /etc/openvpn/keys/crl.pem. Теперь в конфигурационном файле сервера нужно раскоментировать эту строку:

crl-verify /etc/openvpn/keys/crl.pem

И перезагрузить конфигурационный файл:

# /etc/init.d/openvpn reload

С этого момента client1 подключиться не сможет. Если же он уже был подключен, то соединение будет разорвано при ближайшем пересогласовании сертификатов(по умолчании каждый час). Если же соединение нужно разорвать немедленно, то можно воспользоваться управляющим интерфейсом, подключившись через telnet:

# telnet localhost 7505
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
>INFO:OpenVPN Management Interface Version 1 -- type 'help' for more info

kill client1
SUCCESS: common name 'client1' found, 1 client(s) killed

Ссылки

openvpn.txt · Последние изменения: 2014/03/11 16:30 — metallic
 
Recent changes RSS feed