December 11, 2018

Немного о "безопасных" VPN

Наступило время, когда VPN уже не является каким-то экзотическим инструментом бородатых сисадминов. Задачи у пользователей разные, но факт в том, что VPN стал нужен вообще всем.

Проблема текущих VPN решений в том, что их тяжело правильно настроить, дорого обслуживать, а так же в них полно legacy кода сомнительного качества.

Несколько лет назад канадский специалист по информационной безопасности Jason A. Donenfeld решил, что хватит это терпеть, и начал работу над WireGuard. Сейчас WireGuard готовится к включению в состав ядра Linux, он даже получил похвалы от Линуса Торвальдса и в американском сенате.

Заявленные преимущества WireGuard над другими VPN решениями:

  • Простой в использовании.
  • Использует современную криптографию: Noise protocol framework, Curve25519, ChaCha20, Poly1305, BLAKE2, SipHash24, HKDF и т.д.
  • Компактный читаемый код, проще исследовать на уязвимости.
  • Высокая производительность.
  • Четкая и проработанная спецификация.

Неужели найдена серебрянная пуля? OpenVPN и IPSec пора закапывать? Я решил с этим разобраться, а заодно сделал скрипт для автоматической установки личного VPN сервера.

Принципы работы

Принципы работы можно описать примерно так:

  • Создается WireGuard интерфейс, ему назначается приватный ключ и IP адрес. Загружаются настройки других пиров: их публичные ключи, IP адреса и т.д.
  • Все IP пакеты, приходящие на WireGuard интерфейс инкапсулируются в UDP и безопасно доставляются другим пирам.
  • Клиенты задают публичный IP адрес сервера в настройках. Сервер автоматически узнает внешние адреса клиентов, когда от них приходят корректно аутентифицированные данные.
  • Сервер может менять публичный IP адрес не прерывая работы. При этом он отошлет оповещение подключенным клиентам и они обновят свою конфигурацию на лету.
  • Используется концепт маршрутизации Cryptokey Routing. WireGuard принимает и отправляет пакеты на основании публичного ключа пира. Когда сервер расшифровывает корректно аутентифицированный пакет, проверяется его src поле. Если оно соответствует с конфигурацией allowed-ips аутентифицированного пира, то пакет принимается интерфейсом WireGuard. При отправке исходящего пакета происходит соответственная процедура: берется dst поле пакета и на основании его выбирается соответсвующий пир, пакет подписывается своим ключом, шифруется ключом пира и отправляется на remote endpoint.

Вся основная логика WireGuard занимает менее 4 тысяч строк кода, тогда как OpenVPN и IPSec имеют сотни тысяч строк. Для поддержки современных криптоалгоритмов предлагается включить в состав ядра Linux новый криптографический API Zinc. В данный момент идет обсуждение, насколько это удачная идея.

Производительность

Максимальное преимущество в производительности (по сравнению с OpenVPN и IPSec) будет заметно на Linux системах, так как там WireGuard реализован в виде модуля ядра. Кроме этого поддерживаются macOS, Android, iOS, FreeBSD и OpenBSD, но в них WireGuard выполняется в userspace со всеми вытекающими последствиями для производительности. Поддержку Windows обещают добавить в ближайшем будущем.

Результаты бенчмарков с официального сайта:

Мой опыт использования

Я не эксперт по настройке VPN. Однажды настраивал OpenVPN ручками и это было очень муторно, а IPSec даже и не пытался. Слишком много решений нужно принимать, очень легко выстрелить себе в ногу. Поэтому я всегда пользовался готовыми скриптами для настройки сервера.

Так вот, WireGuard, с моей точки зрения, вообще идеален для пользователя. Все низкоуровневые решения приняты в спецификации, поэтому процесс подготовки типичной VPN инфраструктуры занимает всего несколько минут. Нафакапить в конфигурации практически невозможно.

Процесс установки детально описан на официальном сайте, отдельно хочется отметить отличную поддержку OpenWRT.

Генерируются ключи шифрования утилитой wg:

SERVER_PRIVKEY=$( wg genkey )
SERVER_PUBKEY=$( echo $SERVER_PRIVKEY | wg pubkey )
CLIENT_PRIVKEY=$( wg genkey )
CLIENT_PUBKEY=$( echo $CLIENT_PRIVKEY | wg pubkey )

Далее, нужно создать серверный конфиг /etc/wireguard/wg0.conf со следующим содержанием:

[Interface]
Address = 10.9.0.1/24
PrivateKey = $SERVER_PRIVKEY
[Peer]
PublicKey = $CLIENT_PUBKEY
AllowedIPs = 10.9.0.2/32

и поднять туннель скриптом wg-quick:

sudo wg-quick up /etc/wireguard/wg0.conf

В системах с systemd вместо этого можно использовать sudo systemctl start [email protected].

На клиентской машине, создать конфиг /etc/wireguard/wg0.conf:

[Interface]
PrivateKey = $CLIENT_PRIVKEY
Address = 10.9.0.2/24
[Peer]
PublicKey = $SERVER_PUBKEY
AllowedIPs = 0.0.0.0/0
Endpoint = 1.2.3.4:51820 # Внешний IP сервера
PersistentKeepalive = 25 

И точно так же поднять туннель:

sudo wg-quick up /etc/wireguard/wg0.conf

Осталось настроить NAT на сервере, чтобы клиенты могли выходить в Интерет, и все готово!

Такую простоту использования и компактность кодовой базы удалось достичь за счет отказа от функционала дистрибьюции ключей. Здесь нет сложной системы сертификатов и всего этого корпоративного ужаса, короткие ключи шифрования распространяются примерно как SSH ключи. Но в связи с этим возникает проблема: WireGuard будет не так просто внедрять в некоторых уже существующих сетях.

Из недостатков стоит отметить, что WireGuard не заработает через HTTP proxy, поскольку в качестве транспорта есть только протокол UDP. Возникает вопрос, возможно ли будет обфусцировать протокол? Конечно, это не прямая задача VPN, но для OpenVPN, например, существуют способы маскировки под HTTPS, что помогает жителям тоталитарных стран полноценно пользоваться Интернетом.

Выводы

Подводя итог, это очень интересный и перспективный проект, можно уже сейчас использовать его на личных серверах. Какой профит? Высокая производительность на Linux системах, простота настройки и поддержки, компактная и читабельная кодовая база. Однако, бросаться переводить комплексную инфраструктуру на WireGuard еще рано, стоит подождать включение в состав ядра Linux.

Для экономии своего (и вашего) времени я разработал автоматический установщик WireGuard. С его помощью можно поднять личный VPN для себя и своих знакомых даже ничего в этом не понимая.