Проект реализует L3-туннель поверх обычного SSH с использованием ssh -w / OpenSSH TUN. Сервер поднимает пул TUN-интерфейсов, клиенты случайно выбирают свободный tunN, подключаются по SSH и направляют трафик через зашифрованный SSH-канал.
Основная идея MVP: использовать стандартный OpenSSH вместо отдельного VPN-сервера. На сервере достаточно отдельного sshd-инстанса с PermitTunnel point-to-point, подготовленного пула tun100..tun200 и NAT. На клиентах используются Linux shell-клиент или Windows Go/Wintun-клиент.
User workstation
|
| local TUN adapter
|
Linux: sshtun_pool_client.sh
Windows: sshtun_pool_client.exe + Wintun
|
| SSH TCP, tun@openssh.com / ssh -w N:N
|
sshtun-pool-sshd на сервере
|
| tunN -> NAT -> WAN
v
Internet / routed networks
По умолчанию используется пул tun100..tun200 и адресация внутри 10.250.0.0/16:
tun100 -> server 10.250.0.1, client 10.250.0.2
tun101 -> server 10.250.0.5, client 10.250.0.6
tun200 -> server 10.250.1.145, client 10.250.1.146
Адреса выделяются парами по схеме /30-блоков. Клиенту не нужно заранее знать свой IP: он вычисляется из номера выбранного tunN.
.
├── install_server.sh
├── sshtun_pool_client.sh
└── sshtun_pool_windows_client
├── README.md
├── build.ps1
├── go.mod
├── go.sum
├── cmd/sshtun_pool_client/main.go
└── internal
├── admin
├── ipcalc
├── packetpump
├── routes
├── sshtransport
└── wintun
Серверный установщик. Он:
- устанавливает зависимости;
- создаёт системного пользователя
sshvpn; - создаёт отдельную конфигурацию SSHD в
/opt/sshtun_pool/sshd_config; - создаёт отдельный systemd-сервис
sshtun-pool-sshd.service; - создаёт service
sshtun-pool-network.serviceдля подготовки TUN-пула и NAT; - генерирует отдельный host key для SSH-TUN сервиса;
- включает
net.ipv4.ip_forward=1; - создаёт
tun100..tun200; - добавляет минимальные правила NAT/forwarding через WAN-интерфейс.
Важно: это отдельный SSHD-инстанс на отдельном порту, а не изменение основного SSH на 22/tcp.
Linux-клиент. Поддерживает:
- full-tunnel режим;
- split-tunnel режим;
- route/include/exclude CIDR;
- списки маршрутов из файлов;
- DNS через
resolvectl; - IPv6 blackhole для защиты от IPv6 leak;
- lock от параллельных стартов/остановок;
- cleanup старых управляемых
tunN; - работу с passphrase-protected ключами через
ssh-agentили--ask-passphrase; status,doctor,cleanup.
Windows-клиент на Go. Использует Wintun и совместимую с Linux-клиентом CLI-схему. Основной бинарь: sshtun_pool_client.exe.
Поддерживает:
- background worker после
start; - состояние в
C:\ProgramData\sshtun_pool_client\state.json; - runtime-лог в
C:\ProgramData\sshtun_pool_client\sshtun.log; - full-tunnel и split-tunnel;
- include/exclude CIDR и route-файлы;
- encrypted private keys;
status,doctor,cleanup;- pinning SSH host key через
--host-key-sha256.
Поддерживаемые системы: Linux с systemd, OpenSSH server, iproute2, iptables.
Нужны:
- root-доступ;
/dev/net/tun;- доступный TCP-порт, по умолчанию
65523; - разрешённый forwarding/NAT;
- внешний IPv4-адрес или домен.
Нужны:
- root-доступ для создания TUN и маршрутов;
ssh;iproute2;flock;getent;- желательно
resolvectlдля DNS.
Нужны:
- Windows с правами администратора;
sshtun_pool_client.exe;wintun.dllрядом с exe;- приватный SSH-ключ пользователя.
Для сборки Windows-клиента нужен Go 1.22+ и PowerShell.
На сервере:
sudo bash install_server.shПо умолчанию будут использованы параметры:
user: sshvpn
port: 65523
tun pool: tun100..tun200
network: 10.250.0.0/16
mtu: 1400
base dir: /opt/sshtun_pool
Проверка сервисов:
systemctl status sshtun-pool-sshd.service --no-pager
systemctl status sshtun-pool-network.service --no-pagerПроверка listening port:
ss -lntp | grep 65523Проверка TUN-пула:
ip addr show tun100
ip addr show tun101На админской машине или на клиенте:
ssh-keygen -t ed25519 -f ./id_ed25519 -N "" -C "user-device"На сервер нужно добавить публичный ключ в:
/opt/sshtun_pool/authorized_keys
Рекомендуемая строка ключа:
no-pty,no-agent-forwarding,no-X11-forwarding,no-port-forwarding,no-user-rc ssh-ed25519 AAAAC3... user-device
Для random pool mode не добавляйте tunnel="N" в authorized_keys. Иначе ключ будет прибит к конкретному TUN-номеру и несколько устройств с одним ключом начнут конфликтовать.
После изменения ключей перезапуск сервиса обычно не нужен, но права должны быть корректными:
chown root:sshvpn /opt/sshtun_pool/authorized_keys
chmod 640 /opt/sshtun_pool/authorized_keysFull-tunnel режим:
sudo bash sshtun_pool_client.sh start --host SERVER_IP --key ./id_ed25519Full-tunnel, но локальные/private сети идут напрямую:
sudo bash sshtun_pool_client.sh start \
--host SERVER_IP \
--key ./id_ed25519 \
--exclude 10.0.0.0/8 \
--exclude 172.16.0.0/12 \
--exclude 192.168.0.0/16Split-tunnel режим:
sudo bash sshtun_pool_client.sh start \
--host SERVER_IP \
--key ./id_ed25519 \
--no-full-tunnel \
--route 10.10.0.0/16 \
--route 172.20.0.0/16Маршруты из файла:
sudo bash sshtun_pool_client.sh start \
--host SERVER_IP \
--key ./id_ed25519 \
--exclude-file ./ru-cidrs.txtОстановка:
sudo bash sshtun_pool_client.sh stopДиагностика:
sudo bash sshtun_pool_client.sh status
sudo bash sshtun_pool_client.sh doctor
sudo bash sshtun_pool_client.sh cleanupВ каталоге sshtun_pool_windows_client:
Set-ExecutionPolicy -Scope Process Bypass -Force
.\build.ps1Скрипт скачает wintun.dll, подтянет Go-модули и соберёт:
dist\sshtun_pool_client.exe
dist\wintun.dll
Сборка под другую архитектуру:
.\build.ps1 -Arch amd64
.\build.ps1 -Arch arm64
.\build.ps1 -Arch 386PowerShell нужно запустить от имени администратора.
Full-tunnel режим:
.\dist\sshtun_pool_client.exe start --host SERVER_IP --key .\id_ed25519Full-tunnel с исключением private-сетей:
.\dist\sshtun_pool_client.exe start `
--host SERVER_IP `
--key .\id_ed25519 `
--exclude-privateSplit-tunnel режим:
.\dist\sshtun_pool_client.exe start `
--host SERVER_IP `
--key .\id_ed25519 `
--no-full-tunnel `
--route 10.10.0.0/16 `
--route 172.20.0.0/16Маршруты из файла:
.\dist\sshtun_pool_client.exe start --host SERVER_IP --key .\id_ed25519 --exclude-file .\ru-cidrs.txtОстановка и диагностика:
.\dist\sshtun_pool_client.exe status
.\dist\sshtun_pool_client.exe doctor
.\dist\sshtun_pool_client.exe stop
.\dist\sshtun_pool_client.exe cleanupВесь IPv4-трафик уходит через туннель. Для этого клиент добавляет два маршрута:
0.0.0.0/1
128.0.0.0/1
Такой подход не ломает исходный default route полностью и позволяет оставить отдельный bypass-route до реального IP сервера.
Основной интернет идёт через туннель, но отдельные сети уходят напрямую через исходный default gateway.
Типовой пример:
--exclude 10.0.0.0/8 --exclude 172.16.0.0/12 --exclude 192.168.0.0/16На Windows есть сокращение:
--exclude-privateЧерез туннель идут только явно указанные сети:
--no-full-tunnel --route 10.10.0.0/16 --route 172.20.0.0/16Это удобно для корпоративных сетей, внутренних ресурсов или отдельных подсетей.
Общие опции Linux/Windows:
--host HOST
--key FILE
--user USER
--port PORT
--tun N
--tun-start N
--tun-end N
--mtu N
--no-full-tunnel
--route CIDR
--route-file FILE
--include CIDR
--include-file FILE
--exclude CIDR
--exclude-file FILE
--route-private
--exclude-private
--no-dns
--dns "1.1.1.1 8.8.8.8"
--no-block-ipv6
--no-clean-orphans
--ask-passphrase
Linux-дополнительно:
--remote-setup
--no-remote-setup
--remote-wan-dev DEV
Windows-дополнительно:
--adapter-name "SSHTUN Pool"
--host-key-sha256 SHA256:...
--route-style gateway|onlink
--connect-timeout SECONDS
--keepalive-seconds SECONDS
--keepalive-misses N
Серверный установщик создаёт отдельный sshd-инстанс, отдельного пользователя и отдельный authorized_keys:
/opt/sshtun_pool/sshd_config
/opt/sshtun_pool/authorized_keys
/opt/sshtun_pool/ssh_host_ed25519_key
Основные ограничения в SSHD-конфигурации:
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
PermitRootLogin no
AllowUsers sshvpn
PermitTunnel point-to-point
AllowTcpForwarding no
GatewayPorts no
PermitTTY no
X11Forwarding no
AllowAgentForwarding no
AllowStreamLocalForwarding no
PermitUserEnvironment no
ForceCommand /bin/false
Пользователь sshvpn создаётся с nologin, то есть обычный shell ему не нужен. Для ssh -w достаточно TUN-запроса, shell-доступ пользователю не выдаётся.
Для MVP используется простая модель:
один пользователь = один SSH-ключ
Один ключ может использоваться на нескольких устройствах. Так как клиент выбирает случайный tunN из пула, несколько устройств с одним ключом не должны конфликтовать, пока есть свободные TUN-номера.
Для production желательно добавить отдельный слой учёта и ограничений:
- лимит одновременных подключений на ключ;
- отзыв ключа через удаление из
authorized_keys; - перевыпуск ключа через удаление старого и добавление нового;
- аудит подключений по SSHD-логам;
- версионирование и checksum файла
authorized_keysпри массовой синхронизации.
/run/sshtun/state
/run/sshtun/ssh.log
/run/sshtun/ssh.last.log
/run/sshtun/known_hosts
/run/sshtun/lock
Команды:
sudo bash sshtun_pool_client.sh status
sudo bash sshtun_pool_client.sh doctorC:\ProgramData\sshtun_pool_client\state.json
C:\ProgramData\sshtun_pool_client\sshtun.log
Команды:
.\dist\sshtun_pool_client.exe status
.\dist\sshtun_pool_client.exe doctorjournalctl -u sshtun-pool-sshd.service -n 200 --no-pager
journalctl -u sshtun-pool-network.service -n 200 --no-pager
systemctl status sshtun-pool-sshd.service --no-pager
systemctl status sshtun-pool-network.service --no-pagerLinux:
ip addr show tun100
ip route
curl -4 ifconfig.me
sudo bash sshtun_pool_client.sh statusWindows:
.\dist\sshtun_pool_client.exe status
curl.exe -4 ifconfig.me
route print -4
ipconfig /allНа сервере:
ip addr show tun100
iptables -t nat -S | grep 10.250
journalctl -u sshtun-pool-sshd.service -fПроверить:
- публичный ключ добавлен в
/opt/sshtun_pool/authorized_keys; - права на файл
authorized_keysкорректные; - клиент использует правильный приватный ключ;
- подключение идёт на правильный порт
65523, а не на основной SSH-порт; - для ключа с passphrase используется
ssh-agentили--ask-passphrase.
Проверить:
systemctl status sshtun-pool-sshd.service --no-pager
ss -lntp | grep 65523
iptables -SТакже убедиться, что порт открыт в firewall/security group провайдера.
Проверить на сервере:
ls -l /dev/net/tun
cat /opt/sshtun_pool/sshd_config | grep PermitTunnel
systemctl status sshtun-pool-network.service --no-pagerВ конфигурации SSHD должно быть:
PermitTunnel point-to-point
Проверить серверный NAT и forwarding:
sysctl net.ipv4.ip_forward
iptables -t nat -S | grep MASQUERADE
iptables -S FORWARD | grep 10.250
ip routeПроверить, что WAN-интерфейс определился правильно.
Linux-клиент использует resolvectl, если он установлен. Проверить:
resolvectl status
sudo bash sshtun_pool_client.sh doctorМожно отключить изменение DNS:
--no-dnsИли указать свои DNS:
--dns "1.1.1.1 8.8.8.8"По умолчанию Linux-клиент добавляет blackhole для IPv6 default route. Отключить это поведение можно так:
--no-block-ipv6Для production лучше отдельно решить, будет ли IPv6 полностью запрещён или поддержан через отдельную маршрутизацию.
Linux:
sudo bash sshtun_pool_client.sh cleanupWindows:
.\dist\sshtun_pool_client.exe cleanupCleanup удаляет только управляемые интерфейсы/адаптеры проекта, а не произвольные сетевые интерфейсы системы.
Серверный установщик поддерживает переопределение параметров через env:
LISTEN_PORT=65523 \
TUN_NUM_START=100 \
TUN_POOL_SIZE=101 \
TUN_MTU=1400 \
BASE_DIR=/opt/sshtun_pool \
sudo -E bash install_server.shОсновные переменные:
BASE_DIR
SERVICE_NAME
NETWORK_SERVICE_NAME
SYSTEM_USER
LISTEN_PORT
TUN_NUM_START
TUN_POOL_SIZE
TUN_PREFIX
TUN_MTU
TUN_NET_A
TUN_NET_B
TUN_POOL_CIDR
LIMIT_NOFILE
TASKS_MAX
Linux-клиент также можно настраивать через env, но обычно удобнее использовать CLI-флаги.
Минимальный рабочий набор:
install_server.sh
sshtun_pool_client.sh
sshtun_pool_windows_client/build.ps1
sshtun_pool_windows_client/go.mod
sshtun_pool_windows_client/go.sum
sshtun_pool_windows_client/cmd/sshtun_pool_client/main.go
sshtun_pool_windows_client/internal/**
Для сборки Windows-клиента нужны все Go-файлы внутри cmd/ и internal/, а также go.mod, go.sum, build.ps1. Удалять *_other.go не стоит: они нужны для корректной кроссплатформенной сборки и заглушек под неподдерживаемые платформы.
- Это MVP/прототип транспортного слоя, а не полный коммерческий клиент с GUI, автообновлением и подписанным installer.
- Нет центрального control-plane для выдачи/отзыва ключей.
- Нет встроенного лимита одновременных подключений на один ключ.
- Нет встроенной синхронизации
authorized_keysпо флоту серверов. - Нет встроенного мониторинга качества канала.
- IPv6 по умолчанию скорее блокируется, чем полноценно туннелируется.
- Windows-сборка требует Wintun DLL рядом с exe.
- Добавить генерацию пользовательского JSON-профиля: host, port, user, private key, routing mode, DNS, route/exclude files.
- Добавить централизованный revoke/reissue ключей.
- Добавить лимит одновременных подключений на ключ.
- Добавить безопасную синхронизацию
authorized_keysпо серверам. - Добавить checksum/version для файлов ключей.
- Добавить GUI/Tray-клиент поверх CLI-ядра.
- Подписать Windows/macOS binaries и installer.
- Добавить автообновление клиента.
- Вынести большие списки маршрутов, например country CIDR, в отдельный обновляемый файл/сервис.
- Добавить полноценный healthcheck: SSH connect, tunnel up, DNS, public IP, route test, packet pump stats.
Сервер:
sudo bash install_server.sh
systemctl status sshtun-pool-sshd.service --no-pagerКлюч:
ssh-keygen -t ed25519 -f ./id_ed25519 -N "" -C "user-device"
cat ./id_ed25519.pub >> /opt/sshtun_pool/authorized_keysLinux connect:
sudo bash sshtun_pool_client.sh start --host SERVER_IP --key ./id_ed25519
sudo bash sshtun_pool_client.sh stopWindows build:
cd sshtun_pool_windows_client
Set-ExecutionPolicy -Scope Process Bypass -Force
.\build.ps1Windows connect:
.\dist\sshtun_pool_client.exe start --host SERVER_IP --key .\id_ed25519
.\dist\sshtun_pool_client.exe stop