Недавно мне довелось столкнуться с таском по созданию двухуровневой иерархии Центра Сертификации для OpenVPN на debian. Много раз доводилось поднимать OpenVPN с одним центром сертификации и прекрасно понимал, как должно быть в теории с двумя ЦС, но на практике столкнулся с тем, что не понимаю что и где подписывать. Гугл ответов, которые подошли бы для linux, мне не дал (или я не догуглил) и начал я разбираться с этим. Ниже я привел мануал, который составил в процессе создания и настройки.

Сразу хочу отметить, что тут я больший упор делал на настройку ЦС чем на OpenVPN.

Перед тем как мы начнем, я расскажу кому это может пригодиться на своем примере.

У меня была задача создать систему для большой компании таким образом, чтобы сертификаты сервера OpenVPN подписывались только одним человеком, VPN серверов достаточно много, под каждый отдел поднято по несколько VPN серверов. Сотрудников (клиентов) еще больше и контролировать (выписывать/отзывать) сертификаты всякий раз, когда пришел/ушел сотрудник очень тяжкая ноша (не говоря уже о временных сотрудниках). Сотрудников каждого отдела контролирует начальник отдела, который и выписывает или отзывает сертификаты новым/старым сотрудникам соответственно.

Для чего нужны сертификаты и цифровые ключи очень много говорилось и я не буду повторять других авторов, но если коротко, то:

  • для удостоверения надежности (происходит «двойное рукопожатие») клиент и сервер убеждаются в том кто они, можно ли друг другу доверять и устанавливают соединение;
  • шифрования/дешифрования;
  • исключения «Man in the middle (MITM)» дабы быть уверенным, что кто-то не перехватывает сообщения/трафик;
  • для создания зашифрованных паролей, что повышает безопасность и усложняет доступ злоумышленников на хост.

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



При создании ЦС создается два файла: ca.crt — открытый ключ и ca.key — закрытый ключ.
Закрытый ключ должен быть защищен и не должен передаваться третьим лицам.

Когда надо создать подчиняющийся/подписывающий ЦС, мы создаем на нем закрытый ключ и запрос на подпись у RootCA.

Откуда компьютеры и пользователи по всему миру будут знать, что могут доверять какому-либо сервису или сайту, спросите вы? Все просто (ну в теории), Открытый ключ ЦС (RootCA) помещается на пользовательские компьютеры и эти компьютеры доверяют всем сертификатам, которые были выпущены данным ЦС. На практике это конечно сложнее и не дешево. Но в пределах своей компании достаточно легко осуществимо.

Для реализации нам необходимо три сервера. В данном мануале будем использовать debian 9. Сервера назовем согласно их применению: OpenVPN, SubCA, RootCA.

Все действия выполняем под пользователем не рутом.

Для этого ваш пользователь должен быть в группе sudo.

Если sudo на сервере не установлено, тогда авторизовываемся под root:

# su - root
# apt-get install sudo -y
# usermod -aG sudo username
# exit

На всех серверах устанавливаем необходимые утилиты (утилиты могут отличаться в зависимости от верований и убеждений, обязательной является wget, ufw, vim т.к. тут я привел команды с данными утилитами):

# sudo apt-get update
# sudo apt-get upgrade
# sudo apt-get install wget curl net-tools ufw vim -y
# cd ~
# wget -P ~/ https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.4/EasyRSA-3.0.4.tgz
# tar xvf EasyRSA-3.0.4.tgz

На сервере OpenVPN устанавливаем openvpn:

# sudo apt-get install openvpn -y

Переходим на сервер RootCA. Тут нам надо создать файл, откуда easyrsa будет брать значения переменных:

# mv ~/EasyRSA-3.0.4 ~/easyrsa/
# cd ~/easyrsa/
# cp vars.example vars
# vim vars

Находим блок, удалим # и подставим свои значения. Дабы при подписании сертификатов не вводить наши данные мы их пропишем тут:

#set_var EASYRSA_REQ_COUNTRY	"US"
#set_var EASYRSA_REQ_PROVINCE	"California"
#set_var EASYRSA_REQ_CITY	"San Francisco"
#set_var EASYRSA_REQ_ORG	"Copyleft Certificate Co"
#set_var EASYRSA_REQ_EMAIL	"me@example.net"
#set_var EASYRSA_REQ_OU		"My Organizational Unit"

Далее находим следующие установки, удалим # и редактируем их значения. Эти директивы отвечают за сроки жизни сертификатов (первая — за срок сертификата ЦС, вторая — за срок сертификата, который подписывается):

#set_var EASYRSA_CA_EXPIRE	3650         #-->  3650
#set_var EASYRSA_CERT_EXPIRE	3650         #-->  1825

Далее:

# ./easyrsa init-pki

При запуске следующей команды будет запрошен CN. Можно оставит значение по умолчанию, но лучше ввести имя идентифицирующее хост (RootCA). Значение «nopass» говорит о том, что пароль создавать не надо:

# ./easyrsa build-ca nopass

Переходим на сервер SubCA и выполняем аналогичные шаги с небольшими изменениями:

# mv ~/EasyRSA-3.0.4 ~/easyrsa/
# cd ~/easyrsa/
# cp vars.example vars
# vim vars

Находим блок, удалим # и подставим свои значения:

#set_var EASYRSA_REQ_COUNTRY	"US"
#set_var EASYRSA_REQ_PROVINCE	"California"
#set_var EASYRSA_REQ_CITY	"San Francisco"
#set_var EASYRSA_REQ_ORG	"Copyleft Certificate Co"
#set_var EASYRSA_REQ_EMAIL	"me@example.net"
#set_var EASYRSA_REQ_OU		"My Organizational Unit"

Далее находим следующие установки, удалим # и редактируем их значения:

#set_var EASYRSA_CA_EXPIRE	3650         #-->  1825
#set_var EASYRSA_CERT_EXPIRE	3650         #-->  365

Далее:

# ./easyrsa init-pki

При запуске следующей команды будет запрошен CN. Можно оставит значение по умолчанию, но лучше ввести имя идентифицирующее хост (SubCA). Значение «subca» говорит о том, что мы создаем подчиненный ЦС и нам надо создать запрос на подпись сертификата:

# ./easyrsa build-ca subca nopass

Далее находим файл ~/easyrsa/pki/reqs/ca.req (это и есть тот самый запрос) и передаем его серверу RootCA (можно использовать два способа: WinSCP и scp):

# scp ~/easyrsa/pki/reqs/ca.req user@ip_RootCA:/tmp

Переходим на сервер RootCA и подписываем запрос. Перед тем как подписать запрос, мы должны импортировать его в рабочую директорию. для подписания сертификата для подчиненного ЦС используем атрибут «ca» и имя сертификата (можно его назвать ca, но дабы не путаться, назовем его именем сервера, которому его подписываем, а когда передадим его серверу, переименуем):

# cd ~/easyrsa/
# ./easyrsa import-req /tmp/ca.req SubCA
# ./easyrsa sign-req ca SubCA

Будет запрошено подтверждение, надо ввести «yes».
Возвращаем SubCA подписанный сертификат.

# scp ~/easyrsa/pki/issued/SubCA.crt user@ip_SubCA:/tmp

Переходим на сервер SubCA и перемещаем сертификат в рабочую директорию easyrsa:

# mv /tmp/SubCA.crt ~/easyrsa/pki/ca.crt

На данный момент у нас уже есть корневой ЦС и подписанный корневым вторичный ЦС.
Теперь займемся сервером OpenVPN. В его настройке некоторые предыдущие шаги повторяются. Переходим на сервер OpenVPN.

# cd ~/easyrsa/
# ./easyrsa init-pki

Теперь начнем создавать сертификаты на подпись. Мы создадим ключ Диффи-Хеллмана (dh.pem/dh2048.pem/dh1024.pem), который будет использоваться при обмене ключами, и создадим подпись HMAC (ta.key), для усиления функции проверки целостности TLS.

Сертификаты для сервера OpenVPN мы будем подписывать на RootCA, а сертификаты для пользователей мы будем подписывать на SubCA. Сразу создадим директорию где мы будем складывать ключи, сертификаты и конфигурации клиентов.

# mkdir -p ~/client-configs/files/
# mkdir ~/client-configs/keys/
# chmod 700 ~/client-configs/
# sudo mkdir /etc/openvpn/vpnsrv1/

# ./easyrsa gen-req vpnsrv1 nopass
# ./easyrsa gen-req dumasti nopass
# ./easyrsa gen-dh
# sudo openvpn --genkey --secret ta.key
# cp /home/dumasti/easyrsa/pki/private/dumasti.key ~/client-configs/keys/
# sudo cp /home/dumasti/easyrsa/pki/dh.pem /etc/openvpn/vpnsrv1/
# sudo cp /home/dumasti/easyrsa/ta.key /etc/openvpn/vpnsrv1/
# sudo cp /home/dumasti/easyrsa/ta.key ~/client-configs/keys/
# sudo cp /home/dumasti/easyrsa/pki/private/vpnsrv1.key /etc/openvpn/vpnsrv1/
# scp ~/easyrsa/pki/reqs/vpnsrv1.req user@ip_RootCA:/tmp
# scp ~/easyrsa/pki/reqs/dumasti.req user@ip_SubCA:/tmp

Переходим на сервер RootCA и подписываем сертификат. Для подписания сертификата для сервера используем атрибут «server», для клиента «client»:

# cd ~/easyrsa/
# ./easyrsa import-req /tmp/vpnsrv1.req vpnsrv1
# ./easyrsa sign-req server vpnsrv1

Будет запрошено подтверждение, надо ввести «yes».

# scp ~/easyrsa/pki/issued/vpnsrv1.crt user@ip_OpenVPN:/tmp
# scp ~/easyrsa/pki/ca.crt user@ip_OpenVPN:/tmp/RootCA.crt

Переходим на сервер SubCA и подписываем сертификат:

# cd ~/easyrsa/
# ./easyrsa import-req /tmp/dumasti.req dumasti
# ./easyrsa sign-req client dumasti

Будет запрошено подтверждение, надо ввести «yes».

# scp ~/easyrsa/pki/issued/dumasti.crt user@ip_OpenVPN:/tmp
# scp ~/easyrsa/pki/ca.crt user@ip_OpenVPN:/tmp/SubCA.crt

Возвращаемся на сервер OpenVPN и переносим подписанные сертификаты в нужные директории:

# cd /tmp

Для того, чтобы сервер OpenVPN принял клиентские ключи, мы должны объединить в один файл открытые ключи клиента и подчиненного/подписывающего ЦС:

# cat dumasti.crt SubCA.crt > ~/client-configs/keys/dumasti.crt
# cp /tmp/RootCA.crt ~/client-configs/keys/ca.crt
# sudo mv /tmp/RootCA.crt /etc/openvpn/vpnsrv1/
# sudo mv /tmp/vpnsrv1.crt /etc/openvpn/vpnsrv1/

Теперь у нас есть все необходимые сертификаты в нужных местах. Осталось создать конфигурацию сервера OpenVPN и клиента (у каждого могут быть свои убеждения и взгляды в данном вопросе, но для примера тут будет следующая конфигурация).

Можно использовать шаблон конфигурации сервера и клиента и отредактировать под себя:

# sudo cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz /etc/openvpn/
# sudo gzip -d /etc/openvpn/server.conf.gz
# cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ~/client-configs/base.conf

Но ниже я приведу содержимое уже готовых файлов конфигурации (символы; и # комментируют строку):

# sudo cat /etc/openvpn/vpnsrv1.conf

port 1194
proto udp
dev tun
ca vpnsrv1/RootCA.crt
cert vpnsrv1/vpnsrv1.crt
key vpnsrv1/vpnsrv1.key
dh vpnsrv1/dh.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
;push "route 192.168.10.0 255.255.255.0"
;push "route 192.168.20.0 255.255.255.0"
;client-config-dir ccd
;client-config-dir ccd
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"
client-to-client
;duplicate-cn
keepalive 10 120
tls-auth vpnsrv1/ta.key 0
key-direction 0
cipher AES-256-CBC
auth SHA256
max-clients 100
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
log-append /var/log/openvpn/openvpn.log
verb 3
;mute 20
explicit-exit-notify 1

# cat ~/client-configs/base.conf

client
dev tun
proto udp
remote your_server_ip 1194
;remote my-server-2 1194
;remote-random
resolv-retry infinite
nobind
user nobody
group nogroup
persist-key
persist-tun
remote-cert-tls server
;tls-auth ta.key 1
cipher AES-256-CBC
auth SHA256
key-direction 1
verb 3
;mute 20
# script-security 2
# up /etc/openvpn/update-resolv-conf
# down /etc/openvpn/update-resolv-conf

Так же нам надо настроит файервол и пересылку пакетов. Можно настроить iptables, но тут мы рассмотрим ufw.

Для начала узнаем имя нашего интерфейса:

# ip addr 

Откроем следующие порты (у меня ssh на 22 порту, а openvpn на 1194, если у вас другие, то действуйте соответственно):

# sudo ufw allow 1194
# sudo ufw allow 22

Далее откройте файл конфигурации ufw и вставьте туда следующее перед началом цепочки filter (замените мои значения на свои):

# sudo vim /etc/ufw/before.rules

# START OPENVPN RULES

# NAT table rules

*nat

:POSTROUTING ACCEPT [0:0]

# Allow traffic from OpenVPN client to eth0 (change to the interface you discovered!)

-A POSTROUTING -s 10.8.0.0/8 -o ens192 -j MASQUERADE

COMMIT

# END OPENVPN RULES

Перед этим:

# Don't delete these required lines, otherwise there will be errors
*filter

Нужно разрешить UFW пересылку пакетов по умолчанию. Находим нужную строку и меняем значение «DROP» на «ACCEPT»:

# sudo vim /etc/default/ufw

DEFAULT_FORWARD_POLICY="ACCEPT"

Настраиваем пересылку пакетов. Находим строку #net.ipv4.ip_forward=0 или #net.ipv4.ip_forward=1, удалим #, если стоит значение 0, то меняем его на 1:

# sudo vim /etc/sysctl.conf

net.ipv4.ip_forward=1

# sudo sysctl -p
# sudo ufw enable

Далее запускаем наш VPN:

# sudo systemctl start openvpn@vpnsrv1

Проверяем запуск:

# ip addr

Должен появиться новый сетевой интерфейс tun0 с ip 10.8.0.1

# sudo systemctl status openvpn@vpnsrv1

Если надо, чтобы VPN стартовал самостоятельно после перезагрузки добавляем службу в автозапуск:

# sudo systemctl enable openvpn@vpnsrv1

Далее создаем конфигурацию клиента. Ранее мы поместили все ключи и сертификаты в директорию ~/client-configs/keys/.

Создадим скрипт, который соберет конфигурацию, ключи и сертификаты в один файл user.ovpn:

# cd ~/client-configs/
# vim configs-maker.sh

#!/bin/bash
# First argument: Client identifier
KEY_DIR=/home/dumasti/client-configs/keys
OUTPUT_DIR=/home/dumasti/client-configs/files
BASE_CONFIG=/home/dumasti/client-configs/base.conf
cat ${BASE_CONFIG} 	<(echo -e '<ca>') 	${KEY_DIR}/ca.crt 	<(echo -e '</ca>\n<cert>') 	${KEY_DIR}/${1}.crt 	<(echo -e '</cert>\n<key>') 	${KEY_DIR}/${1}.key 	<(echo -e '</key>\n<tls-auth>') 	${KEY_DIR}/ta.key 	<(echo -e '</tls-auth>') 	> ${OUTPUT_DIR}/${1}.ovpn

Данный скрипт будет брать файлы с именем, которое вы ему передадите во время запуска и сконфигурирует один файл в директорию files.

Сделаем файл исполняемым:

# chmod +x configs-maker.sh

Запустим его:

# sudo ./configs-maker.sh dumasti

Теперь переносим клиентскую конфигурацию на свой компьютер из директории /home/dumasti/client-configs/files/

Запускаем VPN.

Из соображений безопасности сервера, на которых стоят ЦС, должны быть выключены и включаются только для подписания сертификатов.

Не обойдем вниманием и отзыв сертификатов. Для того, чтобы отозвать сертификат мы идем на сервер ЦС на котором был подписан сертификат и выполняем следующее (Для примера мы отзовем пользовательский сертификат (dumasti), который мы подписывали на сервере SubCA). Идем на сервер SubCA:

# cd ~/easyrsa/
# ./easyrsa revoke dumasti

Будет запрошено подтверждение отзыва, вводим «yes»

# ./easyrsa gen-crl

Был сформирован файл crl.pem. Его нам надо поместить на сервер OpenVPN и в конфигурации сервера добавить директиву и путь к файлу:

# scp ~/easyrsa/pki/crl.pem user@ip_OpenVPN:/tmp

Переходим на сервер OpenVPN:

# sudo mv /tmp/crl.pem /etc/openvpn/vpnsrv1/
# sudo vim /etc/openvpn/vpnsrv1.conf

Там где прописаны ключи и сертификаты добавляем следующую строку:

crl-verify vpnsrv1/crl.pem

Перезапускаем openvpn:

# sudo systemctl restart openvpn@vpnsrv1

Теперь клиент dumasti не сможет подключиться к VPN.

Благодарю за внимание!