1. Введение
Виртуальные частные сети (VPN) давно вошли в повседневную жизнь. Множество технологий и реализаций сервиса приватных сетей (как для частного применения, так и для использования внутри периметра организаций), обеспечивающих определённый уровень безопасности, доступны для использования широкому кругу ИТ-специалистов.
Не смотря на обилие технологий, предлагаю остановиться на старом добром OpenVPN (в связке с EASY-RSA). Решение от Джеймса Йонана отличается гибкостью, функциональностью, надёжностью и непрерывностью разработки на протяжении приличного временного периода. Так сказать, мастодонт от мира VPN-решений.
Спойлер — ссылка на довольно функциональное решение (ничего особенного, чистый бэкэнд), написанное на bash некоторое время назад, ждёт вас в конце публикации (в виде github-репозитория под именем «openvpn_helper»), а здесь же уделю внимание общей структуре и некоторым аспектам использования набора скриптов и OpenVPN.
***
Список необходимых компонентов (используемая ОС — AlmaLinux 8):
1) OpenVPN 2.4.12;
2) EASY-RSA 3.0.8.
2. Описание общей структуры репозитория
2.1. Директория «ovpn-server»
Директория содержит файлы и поддиректории двух типов: статичные (входящие в репозитория непосредственно) и генерируемые автоматически.
Статичные:
«main.sh» - файл, содержащий основную кодовую базу репозитория.
«vpn.env» - основной файл конфигурации.
«full_setup_vpn_server.sh» - скрипт полной установки VPN-сервера. После запуска исполняет следующие действия:
1) инициация структуры файлов/директорий для управления ключами easyrsa («./easyrsa init-pki»);
2) создание корневого сертификата («./easyrsa build-ca»);
3) генерация ключа Диффи-Хелмана («./easyrsa gen-dh»);
4) создание запроса на выпуск сертификата VPN-сервера («./easyrsa gen-req vpn-server»);
5) импорт и подписание сертификата VPN-сервера («./easyrsa sign-req server vpn-server»);
6) генерация списка отозванных сертификатов («./easyrsa gen-crl»);
7) генерация секретного ключа ta.key («/usr/sbin/openvpn --genkey --secret "$PKI_DIR/ta.key"»), используемого как на стороне сервера, так и на стороне клиента. Необходим для обеспечения дополнительного уровня безопасности (защищает от сканирования прослушиваемых VPN-сервером портов, переполнения буфера SSL/TLS, DoS-атак и flood-атак, а также вводит дополнительный механизм аутентификации между сервером и клиентом);
8) генерация файла конфигурации для VPN-сервера, вспомогательных скриптов и файлов со справочной информацией;
9) копирование всех необходимых сертификатов и файлов конфигурации, необходимы для работы VPN-сервера, в директорию «/etc/openvpn/server».«create_cli_cert_and_conf.sh» - скрипт генерации клиентского набора сертификатов и файла конфигурации.
«revoke_client_cert.sh» - скрипт отзыва клиентских сертификатов.
«gen_crl_and_restart_ovpn.sh» - скрипт генерации CRL-сертификата (Certificate Revocation List или список отозванных сертификатов), содержащего сведения о всех отозванных клиентских сертификатах (отозванных посредством «revoke_client_cert.sh»). Скрипт необходимо запускать каждый раз после отзыва клиентского сертификата.
«get_client_cert_expire_info.sh» - скрипт проверки клиентских сертификатов на предмет устаревания.
«get_server_cert_expire_info.sh» - скрипт проверки серверных сертификатов на предмет устаревания.
Файлы и директории, формируемые на этапе полной установки VPN-сервера (full_setup_vpn_server.sh):
директория «pki_root/pki» - содержит развёрнутую структуру файлов/директорий для управления ключами easyrsa. Краткое описание файлов/директорий структуры:
1) certs_by_serial — директория хранения выпущенных сертификатов (в рамках данной структуры pki), ранжированных по серийным номерам;
2) issued — директория хранения выпущенных сертификатов, ранжированных по имени (CN);
3) private — директория хранения закрытых ключей (в т.ч. и закрытого ключа корневого сертификата) к соответствующим сертификатам;
4) renewed — директория хранения обновлённых сертификатов. В рамках «openvpn_helper» не используется.
5) reqs — директория хранения запросов на выпуск сертификатов.
6) revoked — директория хранения отозванных запросов/сертификатов/закрытых ключей;
7) ca.crt — корневой сертификат;
8) crl.pem — список отозванных сертификатов;
9) dh.pem — ключ Диффи-Хеллмана;
10) index.txt — актуальная база данных всех сертификатов в рамках данной структуры управления ключами/сертификатами;
11) index.txt.attr — содержит параметр «unique_subject = yes», запрещающий создание сертификатов с одинаковыми именами до процедуры отзыва;
12) index.txt.attr.old — предыдущее значение параметра index.txt.attr;
13) index.txt.old — предыдущее содержание базы данных сертификатов index.txt. Изменяется, например, после отзыва клиентского сертификата);
14) openssl-easyrsa.cnf — стандартные для данной структуры параметры с указанием имён переменных, которые возможно переопределить через переменные окружения;
15) safessl-easyrsa.cnf — содержание аналогично файлу openssl-easyrsa.cnf, но с указанием конкретных значений;
16) serial — используется для промежуточного хранения серийного номера генерируемого сертификата до момента его подписания. После подписания серийный номер перемещается в файл “serial.old”;
17) serial.old — содержит серийный номер последнего сгенерированного (и подписанного) сертификата;
18) ta.key — секретный ключ для реализации tls-аутентификации.
«RESTART_OVPN-$VPN_SERVER_NAME_S.sh» (где VPN_SERVER_NAME_S - параметр из vpn.env) - скрипт перезапуска экземпляра OpenVPN, основанного на конфигах и сертификатах, сгенерированных посредством «full_setup_vpn_server.sh» (на основе файла конфигурации «vpn.env», расположенного в текущей директории).
«SET-STATIC-IP-FOR-CLIENTS.txt» - заполняется в формате «username,ip-address», если требуется, чтобы клиентам VPN-сервера выдавались статичных ip-адреса. После внесения изменений необходимо запустить скрипт «RESTART»
«STOP_OVPN-$VPN_SERVER_NAME_S.sh» (где VPN_SERVER_NAME_S — параметр из vpn.env) — полный останов VPN-сервера.
«README_MAIN.txt» - краткая инструкция об использовании VPN-сервера.
«README-user-management.txt» - памятка об управлении пользователями. Возможность коннекта к VPN (помимо сертификатов) ограничивается локальными УЗ, созданными на хосте и состоящими в определённой группе пользователей.
«readme-firewalld-rules.txt» - памятка о настройке файервола (firewalld).
«readme-selinux-rules.txt» - памятка о конфигурации selinux.
«SAVED_CERT_PASSWORDS.txt» - сохранённые пароли (от корневого сертификата и сертификата VPN-сервера).
Файлы и директории, формируемые на этапе генерации клиентских сертификатов (create_cli_cert_and_conf.sh):
директория «cli_certs» - содержит поддиректории вида «CLIENT_CERT_NAME-files», где расположен набор клиентских конфигов и сертификатов, а также архив для передачи пользователю.
2.2. Директория «run_if_dnsmasq_is_not_installed»
Скрипт «run.sh». Простой скрипт, осуществляющий установку сервиса dnsmasq.
Файл конфигурации «dnsmasq.conf». Содержит пример конфигурации сервиса без вышестоящих dns-серверов.
Для использования DNS-сервера, установленного на хосте с OpenVPN, необходимо подкорректировать правила firewalld. Рекомендации по настройке смотреть в файле «readme-firewalld-rules.txt» (после развёртывания VPN-сервера).
2.3. Директория «run_if_openvpn_is_not_installed»
Скрипт «run.sh» устанавливает необходимый для работы VPN набор софта: dnf-репозиторий «epel-release», openvpn, easy-rsa.
2.4. Директория «run_if_selinux_enabled»
Директория содержит 2 файла:
selinux-модуль «ovpn_mod0.te»;
скрипт «run.sh», устанавливающий пакет «selinux-policy» и применяющий selinux-модуль «ovpn_mod0».
2.5. Директория «run_if_zip_is_not_installed»
Скрипт «run.sh» устанавливает zip-архиватор, используемый при создании архивов для передачи конечному пользователю.
2.6. Файл-инструкция «if_need_another_vpnserver_on_this_host.txt»
Документ, кратко описывающий алгоритм развёртывания второго (и последующих) VPN-сервисов на одном и том же хосте.
Алгоритм таков (для примера):
1) создаём директорию, например, «ovpn-server0»;
2) переносим в неё содержание директории «ovpn-server» (исключения: cli_certs, pki_root);
3) редактируем в директории «ovpn-server0» файл «vpn.env», уделяя особое внимание параметрам: VPN_SERVER_NAME_S,
VPN_PORT_S,
VPN_NETWORK_S,
VPN_DHCP_IP_S,
VPN_INTERNAL_NETWORK_ROUTE_S,
VPN_NETWORK_MASQUERADE_SRC_S,
VPN_INTERNAL_NETWORK_MASQUERADE_DST_S,
VPN_INTERNAL_IP_DEV_NAME_S,
VPN_SERVER_INT_FIREWALLD_ZONE_S,
VPN_PORT_CL;
4) из директории «ovpn-server0» запускаем скрипт «full_setup_vpn_server.sh».
2.7. Скрипт «run_first.sh»
Разрешает форвардинг (перенаправление) трафика ipv4, что важно для осуществления доступа из VPN-сети в интранет.
3. Описание основного файла конфигурации vpn.env
Параметры, относящиеся только к генерации сертификатов и конфига (например, «/etc/openvpn/server/server.conf») VPN-сервера (full_setup_vpn_server.sh):
VPN_CONF_DIR_S — директория хранения файлов конфигурации OpenVPN. Дефолтное значение - «/etc/openvpn/server».
VPN_SERVER_NAME_S — имя VPN-сервера в контексте возможности запуска нескольких экземпляров VPN в зависимости от имени файла конфигурации. Желательно, чтобы этот параметр совпадал с частью (ovpn-XXX) имени директории, где расположен файл «vpn.env»).
IP_VPN_SERVER_S — ip-адрес интерфейса, смотрящего в Интернет.
VPN_PORT_S — порт интерфейса, смотрящего в Интернет. Должен быть уникален, если в рамках одного хоста настроено более одного VPN-сервера.
VPN_SERV_LOG_DIR_S — директория размещения логов VPN-сервера. Значение по умолчанию - /var/log/openvpn.
VPN_MAX_CLIENTS_S — максимальное количество клиентов VPN-сервера. В топологии «subnet» при использовании подсети для VPN с маской 24 (или 255.255.255.0) максимально возможное кол-во клиентов равно 83. Значение по умолчанию — 32.
VPN_NETWORK_S — описание VPN-сети в формате «SUBNET SUBNET-mask». Значение по умолчанию - «172.16.10.0 255.255.255.0». Должно быть уникальным в случае, если в рамках одного хоста настроено более одного VPN-сервера.
VPN_DHCP_IP_S — адрес встроенного DHCP. Значение по умолчанию — 172.16.10.1. Должен быть уникальным в случае, если в рамках одного хоста настроено более одного VPN-сервера.
VPN_INTERNAL_NETWORK_ROUTE_S — маршрут до интранета (параметр передаётся клиентам VPN-сервера при подключении), к которой необходим доступ из VPN-сети. Заполнять в соответствии с конкретными параметрами вашей сети.
VPN_SERVER_LOG_LEVEL_S — уровень логирования VPN-сервера. Значение по умолчанию — 0.
VPN_NETWORK_MASQUERADE_SRC_S — описание подсети VPN-сервера, необходимое при отправке директив фаерволу (firewalld). Должно быть уникальным в случае, если в рамках одного хоста настроено более одного VPN-сервера.
VPN_INTERNAL_NETWORK_MASQUERADE_DST_S — описание внутренней подсети, необходимое при отправке директив фаерволу (firewalld).
VPN_INTERNAL_IP_DEV_NAME_S — имя интерфейса, смотрящего во внутреннюю сеть.
VPN_TUN_DEV_NAME_S — имя интерфейса, используемого при старте VPN-сервера. Должен быть уникальным в случае, если в рамках одного хоста настроено более одного VPN-сервера.
VPN_SERVER_INT_FIREWALLD_ZONE_S — зона фаервола, к которой должны принадлежать интерфейсы:
а) VPN_TUN_DEV_NAME_S;
б) VPN_INTERNAL_IP_DEV_NAME_S;
в) интерфейс, являющийся дефолтовым гейтвэем для VPN-сервера.
Пункт (в) необходим только в том случае, если VPN-сервер предполагается использовать как шлюз по умолчанию для VPN-клиентов.
Параметры, относящиеся только к генерации клиентских сертификатов и конфига (create_cli_cert_and_conf.sh):
IP_VPN_SERVER_CL — белый (и статичный) ip-адрес (или доменное имя), к которому необходимо обратиться VPN-клиенту для доступа к интрасети.
VPN_PORT_CL — соответствующий порт для параметра IP_VPN_SERVER_CL.
VPN_CLI_LOG_DIR_LINUX_CL — директория хранения лог-файлов, если клиент VPN-сервера — это хост с ОС Linux.
VPN_CLI_LOG_LEVEL_CL — уровень логирования VPN-клиента.
Параметры EASY-RSA (используются как при генерации сертификатов как для VPN-сервера, так и для клиента):
EASY_RSA_DIR_C — место расположения исполняемого файла easyrsa. Дефолтовое значение - /usr/share/easy-rsa/3.
EASYRSA_CERT_EXPIRE_EV — время устаревания (в днях) выпущенных сертификатов (в т.ч. и корневого). Дефолтовое значение — 3654 дней (10 лет).
EASYRSA_CRL_DAYS_EV – время устаревания (в днях) списка отзыва сертификатов (CRL). Дефолтовое значение — 3654 дней (10 лет).
EASYRSA_KEY_SIZE_EV – размер ключа шифрования.
EASYRSA_REQ_COUNTRY_EV – страна.
EASYRSA_REQ_PROVINCE_EV – область.
EASYRSA_REQ_CITY_EV – город.
EASYRSA_REQ_ORG_EV – организация.
EASYRSA_REQ_OU_EV – подразделение организации.
EASYRSA_REQ_CN_EV – имя сервера.
EASYRSA_REQ_EMAIL_EV – email-адрес администратора.
4. Заключение
Не смотря на функциональность и гибкость (особенно при наличии оснастки, описанной, например, выше), у OpenVPN есть недостаток (присутствует и у аналогичных решений) — механизм списка отозванных сертификатов (который CRL).
Если удалить клиентский сертификат из директории «issued» (структура управления ключами easyrsa), то его отзыв (посредством команды revoke) при компрометации окажется недоступным, что создаёт вероятность использования такого сертификата злоумышленниками. И в этом случае единственным вариантом обезопасить интранет от посторонних глаз — перевыпуск корневого сертификата, что влечёт за собой необходимость перевыпуска остальных сертификатов.
Логично было бы генерировать не список отозванных сертификатов (CRL), а список актуальных сертификатов (например, Cert Actual List), но, видимо, разработчики OpenVPN (а также Easy-RSA), когда-то решили иначе, что создаёт необходимость очень тщательно следить за всеми выпущенными/отозванными сертификатами.