OpenVPN - это просто

Здесь описана простейшая схема с одним сервером и одним клиентом. Тем не менее используются открытые ключи, работа сервера с правами непривилегированного пользователя и перенос демона openvpn после запуска в песочницу. Копируем код целыми блоками в оболочку bash и выполняем.

2019-12-08

Читая сейчас Reference manual for OpenVPN 2.4, я постарался привести свои слегка устаревшие знания по настройке OpenVPN-сети к современным реалиям. В настоящей инструкции, вместо RSA-сертификатов используется криптография на эллиптических кривых. Для шифрования канала выбран протокол с добавочной информацией, позволяющей контролировать целостность пакетов. tls-auth заменён на tls-crypt. CBC -> GCM. SHA1 -> SHA512, dh -> ecdh-curve.

Введение

OpenVPN прекрасный продукт для подключения

Для использования OpenVPN сети в минимальной конфигурации необходимо всего три вещи:
1. Центр авторизации.
2. Один OpenVPN-сервер.
3. Один OpenVPN-клиент.

Здесь приведена простая конфигурация для выхода клиента в интернет через VPN-сервер. Стороннему наблюдателю будут видны зашифрованные udp-пакеты, которыми обменивается клиент со случайного порта выше 1024-го на порт UDP 9216 удалённого хоста. То есть враг будет знать сам факт использования VPN, но без соответствующих ключей и сертификатов не сможет наблюдать содержимое зашифрованного VPN-туннеля. Маскировка OpenVPN-трафика под HTTPS-протокол реализуется пакетом obfsproxy, но о нём я напишу в другой статье.

Центр Сертификации

Центр Сертификации звучит очень сложно. На самом деле никакого дополнительного софта компилировать и собирать не надо. Создадим все необходимые для работы OpenVPN-сети файлы с помощью скриптиков из пакета Easy-RSA. Будет достаточно разместить EasyRSA в отдельной папке в домашнем каталоге, или, например, на флэшке. В самом крайнем случае, если нет нужды в постоянной или эпизодической работе по созданию новых ключей и сертификатов, то можно всё проделать прямо на OpenVPN-сервере, не забыв после надёжно удалить каталог.

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

Развёртывание "Центра Сертификации"

В выделенном для CA каталоге клонируем текущий Easy-RSA и подготавливаем файл с переменными, изменяющими настройки генерации ключей по умолчанию:

apt-get install git
git clone https://github.com/OpenVPN/easy-rsa.git
cd easy-rsa/easyrsa3
cp vars.example vars

Редактируем файл vars для усиления криптографии создаваемых сертификатов (EASYRSA_ALGO ec, EASYRSA_CURVE secp384r1, EASYRSA_DIGEST "sha512") и увеличения срока действия выданных сертификатов до 1095 дней (EASYRSA_CERT_EXPIRE 1095):

export DAYSEXP=1095; \
sed -i -e 's/^#set_var EASYRSA_ALGO.*$/set_var EASYRSA_ALGO ec/' vars; \
sed -i -e 's/^#set_var EASYRSA_CURVE.*$/set_var EASYRSA_CURVE secp384r1/' vars; \
sed -i -e 's/^#set_var EASYRSA_DIGEST.*$/set_var EASYRSA_DIGEST "sha512"/' vars; \
sed -i -e "s/^#set_var EASYRSA_CERT_EXPIRE.*$/set_var EASYRSA_CERT_EXPIRE $DAYSEXP/" vars

Для создания Центра Сертификации выполняем несколько команд:

./easyrsa init-pki; \
./easyrsa --batch build-ca nopass

Будет удобно, если здесь же создадим разделяемый секрет для сервера и клиента:

echo '-----BEGIN OpenVPN Static key V1-----' > pki/ta.key; \
head -c 256 /dev/urandom | hexdump -e '16/1 "%02x" "\n"' >> pki/ta.key; \
echo '-----END OpenVPN Static key V1-----' >> pki/ta.key

Имя для нового сертификата выбирается произвольное. Совпадение с именем хоста не обязательно. Для серверного сертификата выберем имя srv1, а для клиентского сертификата cln1.

Создание файла конфигурации для OpenVPN-сервера

Для нашего первого OpenVPN-сервера генерируем беспарольный ключ и сертификат командой easyrsa build-server-full. Комплект сгенерированных файлов для соответствующего сервера копируем в отдельный каталог kits/srv1 и создаём единственный файл конфигурации srv1.conf со встроенными ключём, сертификатом и разделяемым секретом:

export DN=srv1; \
export DNEXPDATE=$DN"-"$(date -u +%Y%m%d -d "+$(grep '^set_var EASYRSA_CERT_EXPIRE' vars|awk '{print $3}') days")-$(date -u +%H%M); \
export DIR=kits/$DN; export FILECONF=$DIR/$DN.conf; \
./easyrsa build-server-full $DNEXPDATE nopass; \
mkdir -p kits/$DN; \
cp pki/private/$DNEXPDATE.key kits/$DN; \
cp pki/issued/$DNEXPDATE.crt kits/$DN; \
cp pki/ca.crt kits/$DN; \
cp pki/ta.key kits/$DN; \
echo -e ';local #.#.#.#\n;multihome' > $FILECONF; \
echo -e 'port 9216\nproto udp\ndev tun200' >> $FILECONF; \
echo -e '\nserver 192.168.200.0 255.255.255.0\ntopology subnet' >> $FILECONF; \
echo -e 'push "redirect-gateway def1 bypass-dhcp"' >> $FILECONF; \
echo -e 'push "dhcp-option DNS 1.1.1.1"' >> $FILECONF; \
echo -e '\ncipher AES-256-GCM\necdh-curve secp384r1\nkeepalive 10 60' >> $FILECONF; \
echo -e '\nmax-clients 10\nfloat\n;tun-mtu 1300\n;fragment 1300\n;mssfix' >> $FILECONF; \
echo -e '\nchroot jail\nuser nobody\ngroup nogroup\npersist-key\npersist-tun' >> $FILECONF; \
echo -e "\n;ifconfig-pool-persist $DN-ipp.txt\nstatus $DN-status.log" >> $FILECONF; \
echo -e "log $DN.log\n;verb 3\nmute 20" >> $FILECONF; \
echo -e '\ndh none' >> $FILECONF; \
echo -e "# Expired date $DNEXPDATE UTC" >> $FILECONF; \
echo '<cert>' >> $FILECONF; sed -n '/BEGIN/,$p' $DIR/$DNEXPDATE.crt  >> $FILECONF; echo '</cert>' >> $FILECONF; \
echo '<key>' >> $FILECONF; cat $DIR/$DNEXPDATE.key  >> $FILECONF; echo '</key>' >> $FILECONF; \
echo '<ca>' >> $FILECONF; cat pki/ca.crt >> $FILECONF; echo '</ca>' >> $FILECONF; \
echo '<tls-crypt>' >> $FILECONF; sed -n '/BEGIN/,$p' pki/ta.key >> $FILECONF; echo '</tls-crypt>' >> $FILECONF

Создание файла конфигурации для OpenVPN-клиента

Для генерации клиентских ключей предназначена команда easyrsa build-client-full. Для первого клиента генерируем беспарольный ключ и сертификат, с копированием набора файлов в каталог kits/cln1. Здесь же создаём файл конфигурации cln1.ovpn со встроенными ключём, сертификатом и разделяемым секретом для подключения к серверу. Будет удобно скопировать только этот файл в OpenVPN клиент.
1. Обязательно меняем переменную VPNHOST на актуальный ip-адрес или доменное имя, с портом VPN-сервера, не трогая кавычек.
2. Изменив значение переменной REQPASS на отличное от nopass, получим запрос ввода пароля для ключа.

export DN=cln1; \
export REQPASS=nopass; \
export VPNHOST="203.0.113.1 9216"; \
export DIR=kits/$DN; export FILECONF=$DIR/$DN.ovpn; \
export DNEXPDATE=$DN"-"$(date -u +%Y%m%d -d "+$(grep '^set_var EASYRSA_CERT_EXPIRE' vars|awk '{print $3}') days")-$(date -u +%H%M); \
./easyrsa build-client-full $DNEXPDATE $REQPASS; \
mkdir -p $DIR; \
cp pki/private/$DNEXPDATE.key $DIR; \
cp pki/issued/$DNEXPDATE.crt $DIR; \
cp pki/ca.crt $DIR; \
cp pki/ta.key $DIR; \
echo -e 'client\ndev tun\nproto udp' > $FILECONF; \
echo -e 'auth SHA512' >> $FILECONF; \
echo -e "remote $VPNHOST\nresolv-retry infinite" >> $FILECONF; \
echo -e 'nobind\npersist-key\npersist-tun' >> $FILECONF; \
echo -e 'mute-replay-warnings\nremote-cert-tls server' >> $FILECONF; \
echo -e 'verb 1\nmute 20' >> $FILECONF; \
echo -e "# Expired date $DNEXPDATE UTC" >> $FILECONF; \
echo '<cert>' >> $FILECONF; sed -n '/BEGIN/,$p' $DIR/$DNEXPDATE.crt  >> $FILECONF; echo '</cert>' >> $FILECONF; \
echo '<key>' >> $FILECONF; cat $DIR/$DNEXPDATE.key  >> $FILECONF; echo '</key>' >> $FILECONF; \
echo '<ca>' >> $FILECONF; cat pki/ca.crt >> $FILECONF; echo '</ca>' >> $FILECONF; \
echo '<tls-crypt>' >> $FILECONF; sed -n '/BEGIN/,$p' pki/ta.key >> $FILECONF; echo '</tls-crypt>' >> $FILECONF

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

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

После установки OpenVPN клиента для Windows, в каталог C:/Program Files/Openvpn/Config копируем файл kits/cln1/cln1.ovpn. На рабочем столе находим и запускаем GUI OpenVPN. В области уведомлений на панели задач рядом с часами, должен появиться значок OpenVPN. Правой мышкой на нём вызываем меню и выбираем "Подключение". Естественно, делаем это после настройки сервера. Проверил, что работает.

Настройка Android

Устанавливаем официальный клиент OpenVPN в Google Store. С текущими настройками OpenVPN-клиент работает. Проверял на Android 5.1, 7.1.

Настройка "Яблока"

Официальный клиент для Apple в Apple Store. Сейчас под рукой нет эппла. Я помню, что настройка клиента не вызвала затруднений. Подсовываем клиенту файл cln1.ovpn и всё работает. При случае проверю и напишу.

Настройка Gnome NetworkManager

Устанавливаем пакет NetworkManager-openvpn, например для Ubuntu командой sudo apt-get install NetworkManager-openvpn, или в Void Linux команда sudo xbps-install -Su NetworkManager-openvpn.

В настройках NetworkManager'а "VPN Connections -> Configure VPN -> + -> Import a saved VPN connection..."", импортируем файл cln1.ovpn. При импорте, встроенные в файл конфигурации ключи и сертификаты экспортируются в отдельные файлы .pem в каталоге ~/.cert. Проверил, что работает.

Создание OpenVPN-сервера

Предполагается, что в AWS Lightsail, DigitalOcean, Vultr, ... был арендован VPS за несколько долларов, и с Ubuntu 18.04 на борту. Демон SSH слушает 22-ой порт и имеет запрет на использование паролей. Файрвол не настроен. Произведен апгрейд системы:

$ sudo apt-get -y update && sudo apt-get -y dist-upgrade && sudo apt-get -y --purge autoremove

Конфигурирование демона OpenVPN

Переходим в контекст root'а, устанавливаем пакет openvpn и создаём поддерево каталогов:

sudo su
apt-get install openvpn
cd /etc/openvpnl \
mkdir -p /etc/openvpn/jail /etc/openvpn/jail/tmp

В каталог /etc/openvpn копируем тем или иным способом файл srv1.conf для сервера, подготовленный нами ранее в Центре Сертификации, и размещённый в папке kits/srv1. Например, с помощью rsync и при наличии на сервере ssh-демона на порте 22 с PermitRootLogin yes, делаем так, естественно изменив первые три переменные на актуальные значения:

export FILENAME=kits/srv1/srv1.conf; \
export SSHADDRESS=203.0.113.1; \
export SSHPORT=22; \
rsync -avP -e "ssh -p $SSHPORT" $FILENAME [email protected]$SSHADDRESS:/etc/openvpn/

На всякий случай устанавливаем необходимые права и атрубуты для скопированного набора файлов:

chown root:root /etc/openvpn/*.conf
chmod 600 /etc/openvpn/*.conf

Включаем и запускаем демон:

systemctl enable [email protected]
systemctl start [email protected]

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

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

sysctl -w net.ipv4.ip_forward=1

export GW=$(ip r |awk '/default/{print $5}')
export IP=$(ip a sh dev ens3 |sed -En 's/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p')
export SSHPORT=22
export VPNPORT=9216; export VPNPROTO=udp

iptables -t nat -F
iptables -t mangle -F
iptables -F
iptables -X
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -A INPUT -i lo -j ACCEPT

iptables -A INPUT -p tcp -m tcp --dport $SSHPORT -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -p $VPNPROTO -m $VPNPROTO --dport $VPNPORT -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -i tun+ -p icmp -m icmp --icmp-type echo-request -m conntrack --ctstate NEW -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate INVALID -j DROP
iptables -A FORWARD -i tun200 -o $GW -j ACCEPT
iptables -A FORWARD -j REJECT --reject-with icmp-net-unreachable
iptables -t nat -A POSTROUTING -s 192.168.200.0/24 -o $GW -j SNAT --to-source $IP

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

Проверяем правильность настроек с помощью ранее настроенного OpenVPN-клиента. Подключаемся к серверу и в любом браузере вводим URL https://www.google.com/search?q=ip. Гугл ответит адресом ip. Если адрес совпадает с адресом OpenVPN-сервера, то настройка сервера верна и переходим к закреплению текущей конфигурации сервера на постоянной основе.