Плащ-невидимка: обфусцируем VPN

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

Хорошая новость в том, что у меня накопилось некоторое количество материалов для перевода, плюс к тому - судорожные телодвижения наших законодателей, бьющихся в агонии, продуцируют всё более драконовские манускрипты, которые скоро запретят, кажется, вообще весь интернет.

Ну или как минимум - любые VPN-туннели, кроме одобренных дуболомами

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

Что с этим можно поделать? Сегодняшнее стоп-слово - обфускация!

Обфускацию, слегка упрощая и переводя на церковнославянский, можно назвать маскировкой. В данном случае - маскировкой самого факта использования VPN. Благодаря повсеместному (спасибо, Let`s Encrypt!) распространению HTTPS, шифрованный трафик в интернете сам по себе аномалией и объектом пристального внимания быть перестал. Да, пока что (до внедрения ECH) в процессе HTTPS-хэндшейка товарищ майор всё ещё может видеть, к какому доменному имени происходит подключение - и как-то эту информацию использовать, но в нашем случае этот момент учтён, и более того - поставлен на службу.

Существует много разных способов обфускации трафика, но пока рассмотрим только один - Cloak

С точки зрения внешнего наблюдателя, передача данных с помощью Cloak не отличается от захода на обычный сайт. Внутри же может быть что угодно, как VPN-туннель, так и потоковое видео, торренты или нечто иное - главное, чтобы на обоих концах был установлен обфускатор с подходящими параметрами.

На более низком уровне, у нас происходит классическая организация безопасного канала через небезопасную среду передачи. Для этого на сервере генерируется пара эллиптических DH-ключей. Кроме того, у каждого клиента существует уникальный идентификатор (UID), отправляемый им в зашифрованном публичным ключом сервера виде при подключении - он играет роль Pre-shared Secret (PSK).

Защита от "дуболома посередине" реализуется следующими способами:

Говоря иначе - мы имеем нечто, выглядящее для тащмайора как утка, двигающееся как утка, и даже если это нечто потыкать палочкой - крякающее совсем как утка. А унутре у неё заяц, или яйцо, или игла - что пожелаете. А раз это нечто не отличить от вполне легитимной утки - то и блокировать это нечто незачем.

Перейдём к практической части

Для организации обфусцированного VPN-туннеля нам понадобится следующее:

NB! Я предполагаю, что в этот момент на VPS уже корректно настроен OpenVPN-сервер на UDP-порту 1984. Проделать это можно, воспользовавшись одним из гайдов в интернете - например, вот этим.

Cloak написан на Go, поэтому для начала нам нужно добавить в систему соответствующий компилятор: https://go.dev/doc/install. Затем клонируем репозиторий и собираем бинарники (также их можно скачать непосредственно с Гитхаба):

git clone https://github.com/cbeuw/Cloak
cd Cloak
go get ./...
make

После данных манипуляций мы получаем два исполняемых файла в каталоге build - ck-server и ck-client. Первый после заполнения конфига нужно будет запустить рядом с VPN-сервером, второй - на клиенте. Последовательность действий следующая:

  1. Генерируем ключи шифрования с помощью команды ck-server -key, запоминаем их на будущее.
  2. Генерируем UID клиента (можно нескольких) с помощью команды ck-server -uid, тоже запоминаем.
  3. Берём из подкаталога example_config рыбу серверного конфига, копируем, например, в /etc/cloak и вносим в этот файл изменения, приводя его примерно к следующему виду:
    {{
      "ProxyBook": {
        "openvpn": [
          "udp",
          "127.0.0.1:1984"
        ]
      },
      "BindAddr": [
        ":443",
        ":80"
      ],
      "BypassUID": [
        "MY5RmiOSRKRC1ajegifHSQ==" # Сюда копируем UID клиентов из шага 2
      ],
      "RedirAddr": "webbtelescope.org", # Выбираем один или несколько безобидных доменов, которые будут фигурировать в TLS-хэндшейке
      "PrivateKey": "IOAa0Rs9erpILNgPNhff/BQtkOB+Q6E4+b8QuXfH/Wo=", # Сюда копируем приватный ключ сервера из шага 1
      "AdminUID": "",
      "DatabasePath": "/tmp/userinfo.db"
    }
  4. Вносим изменения в конфиг OpenVPN-сервера, оставляя его слушать только локально, а также добавляя отправку клиентам маршрута до Cloak-сервера (это внешний адрес нашей VPS):
    local 127.0.0.1
    push "redirect-gateway def1"
    push "route 1.2.3.4 255.255.255.255 net_gateway"
  5. В конфиге OpenVPN-клиента меняем адрес сервера на локальный:
    remote 127.0.0.1 1984 udp4
  6. Запускаем OpenVPN-сервер и Cloak-сервер (ck-server -c <путь к файлу с конфигом>)
  7. Заполняем конфиг для Cloak-клиента:
    {
      "Transport": "direct",
      "ProxyMethod": "openvpn",
      "EncryptionMethod": "aes-gcm",
      "UID": "MY5RmiOSRKRC1ajegifHSQ==", # UID из шага 2
      "PublicKey": "Q7ccuTK+Nz/+Dkzu9AfBB/AwRd1gJh9ph0EWzcXeKCw=", # Публичный ключ сервера из шага 1
      "ServerName": "webbtelescope.org", # Список доменов должен быть идентичен на сервере и клиентах
      "NumConn": 4,
      "BrowserSig": "chrome",
      "StreamTimeout": 300
    }
  8. Запускаем Cloak-клиент (ck-client -u -c <путь к файлу с конфигом>) и OpenVPN-клиент. Всё!

Можно попробовать подключиться по HTTP или HTTPS к внешнему адресу нашей VPS - при выключенном Cloak-клиенте в браузере произойдёт редирект на один из указанных в конфиге доменов.

Таким образом, у нас не торчит наружу порт VPN-сервера, а коннекты неотличимы от обычного веб-сёрфинга. Дуболомы в очередной раз посрамлены мощью математики.