Появился у меня небольшой набор VPS
, на которых уже стоит различное ПО для хранения данных. Для всего этого нужно настраивать защищенное соединение. Из прошлой статьи про настройку VPS мы узнали что на наши машины постоянно кто-то ломится, а это заставляет еще раз подумать о безопасности сетевого контура.
Каждое ПО предоставляет свои средства настройки шифрования данных, но есть другой путь - объединить все VPS
в единую приватную сеть (VPN
) и закрыть доступ из сети Интернет. Таким образом весь трафик во внутренней сети будет шифроваться, а в эту сеть можно попасть только будучи клиентом VPN
, но чтобы им стать нужно получить соотвествующий сертификат.
Центр Сертификации (
ЦС
)/Certification Authority (CA
) - выдает подписанные сертификаты для сервера и клиентов сетиVPN
.
Сервер OpenVPN - ПО организующее безопасный туннель внутри небезопасной сети Интернет.
Клиент OpenVPN - узел в сети
VPN
, использующий безопасный туннель для взаимодействия с другими клиентами.
Сервер и клиенты OpenVPN - генерирует файл запрос на сертификат (.req)
, и вместе с ним генерирует приватный ключ (.key
):
Файлы:
.crt
) - заверенны ЦС
, позволяют идентифицировать узел, используются для шифрования трафика.key
) - используются для расшифровки трафикаta.key
) - элемент механизма проверки целостности информации, гарантии того что информация в ненадежной среде не была измененаПриватные ключи никогда не должны покидать узлы, на которых генерируются. Обмениваться можно только запросами на сертфиикаты и сертификатами.
Некоторые руководства по настройке
VPN
рекомендуют размещатьЦС
и сервер OpenVPN на разных серверах. Это другое руководство, здесь мы будем использовать один и тот же сервер.
Нам нужно следующее ПО:
PKI
)VPN
$ 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
.
Сроки по умолчанию:
ЦС
- 10 летИнициализируем 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
Первоначальные работы с ЦС
законены, теперь мы имеем:
server.crt
- подписанный открытый ключ сервера OpenVPN
server.key
- закрытый ключ сервера OpenVPN
ca.crt
- открытый ключ ЦС
ta.key
- общий секретный ключ (HMAC
)Перемещаем все это в директорию /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:
$ 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
чтобы сервер VPN
мог выполнять свои функции - перенаправлять трафик через сеть VPN
:
$ sudo sysctl -w net.ipv4.ip_forward=1
$ 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
, а через внешнюю сеть, надо зайти в настройки этого 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
Здесь:
lo
- localhost
ens3
- доступ к внешней сетиЕсли на 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
помогла эта статья. Вначале изучаения читал эту статью, возможно что-то из нее тоже перенял.