Настраиваем корпоративный VPN

2023.02.25
Развернем VPN на Ubuntu при помощи OpenVPN и Easy-RSA, поместим туда наши VPS и других клиентов, закроем внутренюю сеть от Интернета при помощи iptables

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

Каждое ПО предоставляет свои средства настройки шифрования данных, но есть другой путь - объединить все VPS в единую приватную сеть (VPN) и закрыть доступ из сети Интернет. Таким образом весь трафик во внутренней сети будет шифроваться, а в эту сеть можно попасть только будучи клиентом VPN, но чтобы им стать нужно получить соотвествующий сертификат.

Теория

Центр Сертификации (ЦС)/Certification Authority (CA) - выдает подписанные сертификаты для сервера и клиентов сети VPN.

Сервер OpenVPN - ПО организующее безопасный туннель внутри небезопасной сети Интернет.

Клиент OpenVPN - узел в сети VPN, использующий безопасный туннель для взаимодействия с другими клиентами.

Сервер и клиенты OpenVPN - генерирует файл запрос на сертификат (.req), и вместе с ним генерирует приватный ключ (.key):

Файлы:

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

Центр Сертификации и сервер OpenVPN

Некоторые руководства по настройке VPN рекомендуют размещать ЦС и сервер OpenVPN на разных серверах. Это другое руководство, здесь мы будем использовать один и тот же сервер.

Нам нужно следующее ПО:

$ sudo apt install openvpn easy-rsa

Необходимо завести отдельного пользователя для работы с ЦС и дать ему sudo:

$ sudo adduser username
$ sudo usermod -aG sudo username

Переключаемся на нашего нового пользовтаеля:

$ sudo su byurrer

Создаем директорию, в которой будем вести работу с ЦС, создаем симлинки на easy-rsa и ограничиваем права доступа:

$ mkdir ~/easy-rsa
$ ln -s /usr/share/easy-rsa/* ~/easy-rsa/
$ chmod 700 ~/easy-rsa

Задаем параметры криптографии (эллиптические кривые, Elliptic Curve Cryptography, ECC), создаем файл vars:

$ nano vars

И записываем следующее:

set_var EASYRSA_ALGO "ec"
set_var EASYRSA_DIGEST "sha512"

Другие возможные параметры можно посмотреть в var.example.

Сроки по умолчанию:

Инициализируем PKI и создаем ЦС:

$ ./easyrsa init-pki

# будет предложено ввести пароль, рекомендуется
# будет сгенерирован ca.crt
$ ./easyrsa build-ca

Сгенерируем запрос создания ключа доступа для сервера OpenVPN (без пароля) и подписываем:

$ ./easyrsa gen-req server nopass
$ ./easyrsa sign-req server server

Нужно будет подтвердить что ключ из доверенного источника, а также ввести пароль если закрытый ключ ЦС зашифрован паролем.

Создаем общий секретный ключ tls_crypt для дополнительной безопасности:

$ openvpn --genkey --secret ta.key

Первоначальные работы с ЦС законены, теперь мы имеем:

Перемещаем все это в директорию /etc/openvpn/server:

$ sudo cp pki/issued/server.crt /etc/openvpn/server
$ sudo cp pki/private/server.key /etc/openvpn/server
$ sudo cp pki/ca.crt /etc/openvpn/server
$ sudo cp ta.key /etc/openvpn/server

Конфиг сервера OpenVPN

Берем пример конфига для сервера OpenVPN:

$ sudo cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz /etc/openvpn/server/
$ sudo gunzip /etc/openvpn/server/server.conf.gz
$ sudo nano /etc/openvpn/server/server.conf

Теперь поочередено вносим в него правки.

Включаем HMAC firewall:

tls-crypt ta.key

Устанавливаем более надежный уровень шифрования:

cipher AES-256-GCM
auth SHA256

Судя по всему при использовании эллиптических кривых, ключ Диффи — Хеллмана не нужен, убираем:

dh none

Разрешаем клиентам соединяться друг с другом:

client-to-client

В нашей VPN каждый клиент должен иметь статический IP адрес, который мы будем задавать самостоятельно, для этого будем использовать CCD (Client Config Dir):

# указание диретории конфигов подключаемых клиентов и шлюз
client-config-dir ccd
route 10.8.0.0 255.255.255.0

И запуск OpenVPN без привелегий:

user nobody
group nogroup

IP forwarding

Теперь нужно включить IP forwarding чтобы сервер VPN мог выполнять свои функции - перенаправлять трафик через сеть VPN:

$ sudo sysctl -w net.ipv4.ip_forward=1

Запуск сервера OpenVPN

$ sudo systemctl -f enable openvpn-server@server.service
$ sudo systemctl start openvpn-server@server.service
$ sudo systemctl status openvpn-server@server.service

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

sudo journalctl -xe

Инфраструктура для клиентских конфигураций

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

Создаем директорию для хранения клиентских собранных конфигураций для подключения к нашей VPN:

$ mkdir -p ~/client-configs/files
$ chmod -R 700 ~/client-configs

Копируем пример конфига клиента OpenVPN:

$ cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ~/client-configs/base.conf
$ nano ~/client-configs/base.conf

В этом базовом конфиге нужно:

# указать публичный ip адрес сервера OpenVPN
remote your_server_ip 1194

# запуск без привелегий
user nobody
group nogroup

# ta.key встроим в сам конфиг, поэтому здесь отключим
;tls-auth ta.key 1

# более надежное шифрование
cipher AES-256-GCM
auth SHA256

# направление ключа
key-direction 1

Создадим скрипт сборки конфига клиента .ovpn:

$ nano make_config.sh

Вставим следущий код:

#!/bin/bash

CA_DIR=~/easy-rsa/pki
KEY_DIR=~/easy-rsa/pki/issued
PRIVATE_DIR=~/easy-rsa/pki/private
PRIVATE_OVPN_DIR=/etc/openvpn/server
OUTPUT_DIR=~/client-configs/files
BASE_CONFIG=~/client-configs/base.conf

cat ${BASE_CONFIG} \
    <(echo -e '<ca>') \
    ${CA_DIR}/ca.crt \
    <(echo -e '</ca>\n<cert>') \
    ${KEY_DIR}/${1}.crt \
    <(echo -e '</cert>\n<key>') \
    ${PRIVATE_DIR}/${1}.key \
    <(echo -e '</key>\n<tls-crypt>') \
    ${PRIVATE_OVPN_DIR}/ta.key \
    <(echo -e '</tls-crypt>') \
    > ${OUTPUT_DIR}/${1}.ovpn

Ограничим доступ к этому скрипту:

$ chmod 700 ~/client-configs/make_config.sh

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

$ ./easyrsa gen-req client_name nopass
$ ./easyrsa sign-req client client_name

Инфомрацию о сертификате можно получить так:

$ ./easyrsa client_name

Укажем желаемый статический IP клиента (10.8.0.2) и адрес шлюза/сервера OpenVPN (10.8.0.1) конфиг в CCD:

$ sudo echo "ifconfig-push 10.8.0.2 10.8.0.1" > /etc/openvpn/server/ccd/client_name

Теперь соберем клиентский конфиг .ovpn:

./make_config.sh client_name

Если наш клиент это другой сервер, тогда размещаем конфиг полученный на предыдущем шаге по пути /etc/openvpn/client/client_name.conf а запускаем демон OpenVPN:

$ sudo systemctl -f enable openvpn-client@client_name.service
$ sudo systemctl start openvpn-client@client_name.service

Смотрим статус, если демон запущен значит узел подключился к VPN:

$ sudo systemctl start openvpn-client@client_name.service

Проверим список доступных интерфейсов, должен появиться tun:

$ ip -br a
byurrer@byurrer-S15C:~$ ip -br a
lo               UNKNOWN        127.0.0.1/8 ::1/128 
enp3s0           DOWN           
tun0             UNKNOWN        10.8.0.130 peer 10.8.0.1/32 fe80::5ac:b84d:b2a6:6cd3/64

Если клиент на Ubuntu Desktop, тогда можно все сделать через удобный графический интерфейс. Заходим в Настроки - Сеть нажимаем + возле VPN, затем Импортировать из файла:

Импорт настроек VPN из файла в графическом интерфейсе Ubuntu Desktop

Чтобы ходить в Интернет не через VPN, а через внешнюю сеть, надо зайти в настройки этого VPN на вкладку IPv4 и включить Использовать это подключение только для ресурсов в этой сети:

Использовать это подключение только для ресурсов в этой сети

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

Нам нужно чтобы из внешнего мира нельзя было достучаться до наших VPS, но только через VPN. Иными словами только клиент VPN должен иметь возможность соединяться с другими клиентами сети. Это можно сделать при помощи фаервола, например iptables.

Изолируя VPS от внешнего мира при помощи VPN мы можем расслабитсья и не думать про TLS внутри сети.

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

$ ip -br a
lo               UNKNOWN        127.0.0.1/8 ::1/128 
ens3             UP             185.105.90.67/24 2a09:5302:ffff::da2/48 fe80::5054:ff:fe10:233f/64 

Здесь:

Если на VPS есть Docker, тогда могут быть интерфейсы с префиксами br и veth, а также bridge интерфейс docker0.

Еще полезно знать список открытых портов, чтобы после настройки фаервола проверить их доступность извне.

Для начала, будем думать что нам запрещено все, а теперь открываем то, что нам нужно.

Входящий трафик на хост

Документация подсказывает что нужно разрешить весь входящий udp трафик на 1194 порту:

$ sudo iptables -A INPUT -p udp --dport 1194 -j ACCEPT

И разрешить входящий трафик из tun интерфейсов (это и есть трафик VPN адресованный локальной машине):

$ sudo iptables -A INPUT -i tun+ -j ACCEPT

Теперь нужно разрешить доступ к локальному резолверу, иначе будут задержки при подключении к ssh:

$ sudo iptables -A INPUT -p udp --dport 53 -j ACCEPT

Разрешим весь исходящий трафик от localhost (если этого не сделать, то иногда, исходящий трафик с localhost будет отклонятся и могут быть проблемы с nginx при проксировании на localhost, странно, но вот так):

$ sudo iptables -A INPUT -i lo -j ACCEPT

Если у нас на VPS есть Docker, тогда надо разрешить исходящие пакеты с br+ интерфейсов:

$ sudo iptables -A INPUT -i br+ -j ACCEPT

Как мы помним из предыдущей статьи про iptables, выше мы разрешили только установить соединение, но не продолжить, поэтому разрешаем продолжать то, что уже начато:

$ sudo iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

Теперь установим политику по умолчанию DROP - отбрасывать все что явно не разрешено:

$ sudo iptables -P INPUT DROP

Перенаправляемый трафик

Трафик внутри нашего кластера VPS будет ходить не только по tun интерфейсам, могут быть запросы с tun на другие интерфейсы, поэтому нужно разрешить весь перенаправляемый трафик:

$ sudo iptables -A FORWARD -i tun+ -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT

Если на VPS есть Docker, и его сетевые интерфейсы не должны быть доступны снаружи (даже при пробросе портов), но сами контейнеры должны уметь ходить в интернет, тогда:

# запрещаем новые входящие соединения из интерфейса, который смотрит наружу
$ sudo iptables -A FORWARD -i ens+ -o br+ -m state --state NEW -j DROP

# разрешаем все соединения, которые установленные и относящиеся к установленным соединениям
# таковыми будут те, в которых контейнер отправлет запрос в Интернет и получает ответ
$ sudo iptables -A FORWARD -i ens+ -o br+ -m state --state RELATED,ESTABLISHED -j ACCEPT

Теперь установим политику по умолчанию DROP - отбрасывать все что явно не разрешено:

$ sudo iptables -P FORWARD DROP

Литература

В некоторой части эта статья основана на других статьях из интернетов, например очень помогла статья на digitalocean и эта на habr, иногда смотрел на эту статью, а с ccd помогла эта статья. Вначале изучаения читал эту статью, возможно что-то из нее тоже перенял.

В телеграм канале DevOps от первого лица можно оставить комментарий или почитать интересные истории из практики DevOps