[Перевод] PPP-over-HTTP/2: развлекаемся с dumbproxy и pppd
Краткое резюме
Автор статьи модифицировал прокси-сервер dumbproxy, добавив возможность запуска как подпроцесса и передачи данных через stdin/stdout. Цель — экспериментальная реализация передачи PPP через HTTP/2.
Для личных нужд я применяю несколько копий dumbproxy — простого, но весьма универсального прокси-сервера. Недавно я добавил в него новый режим работы: теперь можно запускать dumbproxy как подпроцесс и передавать данные через stdin/stdout, не дожидаясь соединения на порту. Это удобно для использования в качестве ProxyCommand для клиента OpenSSH.
Но главное — это подтолкнуло меня к идее, что я всего в одном шаге от реализации давно задуманного: передачи PPP через HTTP/2!
Dumbproxy уже обладает защитой соединения с помощью TLS, гибкой аутентификацией, опциональной защитой от активного зондирования и хорошей устойчивостью к фильтрации протокола. Было бы полезно интегрировать эти преимущества с известными протоколами частных сетей. Мне захотелось поэкспериментировать с PPP и отдать дань одному из самых старых фундаментальных туннельных протоколов. Ведь так интересно соединить сетевой протокол эпохи dial-up с современным HTTP/2!
### Исходная конфигурация
Я буду использовать простейшую конфигурацию dumbproxy на своём сервере, которая описана здесь и имеет несколько дополнений:
* кэш сертификатов хранится в общем Redis, чтобы сделать экземпляры серверов полностью статичными;
* несколько доменов фильтруются скриптом на JavaScript;
* домен .onion перенаправляется в демон Tor.
В целом, это обычный форвард-прокси с автоматическими сертификатами от LetsEncrypt и локальной базой паролей в файле.
Кстати, есть готовая спецификация cloud-init для быстрой настройки сервера при его создании в облаке.
### Настройка сервера
Рассмотрим скрипт перенаправления на JavaScript (опция -js-proxy-router). У меня он выглядит так:
```
/etc/dumbproxy-route.js:
function getProxy(req, dst, username) {
if (dst.originalHost.replace(/\.$/, "").toLowerCase().endsWith(".onion")) {
return "socks5://127.0.0.1:9050"
}
return ""
}
```
Здесь уже есть одно правило, не относящееся к текущей задаче. Добавим новое, чтобы определённый адрес перенаправлять в подпроцесс pppd с нужным файлом опций:
```
/etc/dumbproxy-route.js:
function getProxy(req, dst, username) {
if (dst.originalHost.replace(/\.$/, "").toLowerCase().endsWith(".onion")) {
return "socks5://127.0.0.1:9050"
}
if (dst.originalHost.toLowerCase() == "pppd") {
return "cmd://?cmd=pppd&arg=file&arg=/etc/ppp/options.vpn"
}
return ""
}
```
Установите pppd — он есть в пакете ppp практически во всех дистрибутивах Linux:
```
apt install ppp
```
Параметры pppd будут такими:
```
/etc/ppp/options.vpn:
nodetach
notty
noauth
172.22.255.1:172.22.255.2
ms-dns 1.1.1.1
ms-dns 8.8.8.8
```
Этого достаточно для установления туннеля. Однако, чтобы трафик действительно пересылался, нужно кое-что ещё.
Включите пересылку IP-пакетов:
```
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf && sysctl -p
```
Добавьте правило трансляции исходящего адреса в iptables:
```
iptables -t nat -I POSTROUTING -o $(ip route show default | head -1 | grep -Po '(?<=dev\s)\s*\S+') -j MASQUERADE
iptables -t mangle -I FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
```