- Сервер на LinuxVPS с публичным IP, 1–2 ядра, ~512 МБ RAM; готовый .deb — amd64
- root-доступнужны сетевые права: TUN, NAT, привязка к порту
Установка и настройка Qeli
Полное руководство: от сборки сервера до сценариев использования. Все команды и конфиги — рабочие, для Linux-сервера с публичным IP.
Установка одной командой
На чистом сервере Debian 12+ / Ubuntu 22.04+ выполните от root — скрипт развернёт reality-tls сервер на :443 целиком: поставит зависимости и последний .deb из релизов, запишет конфиг с full-tunnel NAT, сгенерирует ключ сервера, заведёт пять пользователей с готовыми ссылками qeli:// и запустит службу.
# 1. скачать скрипт
$ curl -fsSLO https://raw.githubusercontent.com/litvinovtd/qeli/main/install-qeli-server.sh
# 2. прочитать его — и только потом запускать от root
$ less install-qeli-server.sh
$ bash install-qeli-server.sh
Чтобы задать адрес подключения явно (IP или домен), передайте его аргументом: bash install-qeli-server.sh vpn.example.com — иначе публичный IP определится сам. Профиль и порт задаются заранее переменными: QELI_PROFILE=reality-tls|fake-tls|udp-quic и QELI_PORT=<1-65535>. Готовые строки подключения окажутся в /etc/qeli/client-links/: вставьте или отсканируйте любую из них в приложении. Нужен полный контроль или сборка из исходников — пошаговая установка ниже.
Почему в два шага, а не curl … | bash: скрипт выполняется от root, и его стоит прочитать до запуска. Вдобавок curl -fsSL на HTTP-ошибке не печатает ничего, а bash с пустым stdin выходит с кодом 0 — сорвавшаяся загрузка выглядит как успешная установка.
Что делает скрипт с системой: включает форвардинг и MSS clamp, правит sysctl (BBR/fq, параметры TCP и PMTU), поднимает веб-панель на 0.0.0.0:8080 с self-signed сертификатом, а пароль панели показывает один раз. Скачивается он из ветки main (версия не зафиксирована) — для воспроизводимости берите его с нужного тега.
Запуск в Docker
Один мультиарх-образ (amd64 / arm64 / armv7) совмещает обе роли — server и client — со всеми зависимостями внутри. Работает на любом Linux-хосте и в контейнерных рантаймах роутеров (MikroTik RouterOS v7, OpenWrt). Готовый образ публикуется в GHCR (тег :latest = новейший релиз) — собирать из исходников не нужно. Запуск — через Docker Compose:
# готовый мульти-arch образ из GHCR (собирать не нужно):
$ docker pull ghcr.io/litvinovtd/qeli:latest
$ docker tag ghcr.io/litvinovtd/qeli:latest qeli:latest # имя, на которое ссылается compose
# либо собрать самому: docker buildx build -f release/docker/Dockerfile -t qeli:latest --load .
# скачать compose-файл (или склонировать репозиторий):
$ curl -fsSLO https://raw.githubusercontent.com/litvinovtd/qeli/main/release/docker/docker-compose.yml
# запуск сервера:
$ docker compose -f docker-compose.yml up -d qeli-server
Контейнеру нужны /dev/net/tun и capabilities NET_ADMIN, NET_RAW, NET_BIND_SERVICE (без --privileged); для серверного NAT — net.ipv4.ip_forward=1. Том /etc/qeli обязательно сохраняйте — там ключ сервера и пользователи. Тот же образ запускается и как клиент-шлюз (client); для клиента в контейнере задайте dns = off. Полное руководство (Linux и MikroTik) — в release/docker/README.md.
Что понадобится
- Rustдля сборки из исходников — либо готовый бинарь / .deb
- Клиентпод вашу платформу: Linux, Windows, macOS или Android
Сборка и установка
Соберите единый бинарь сервера-и-клиента из исходников и положите его в /usr/bin. Один и тот же бинарь работает и как сервер, и как клиент, и как утилита управления.
$ git clone https://github.com/litvinovtd/qeli.git
# Cargo-проект лежит в подкаталоге qeli/ монорепозитория
$ cd qeli/qeli && cargo build --release
$ sudo install -Dm755 target/release/qeli /usr/bin/qeli
$ sudo install -Dm644 config/server.conf /etc/qeli/server.conf
# служба, пользователь и каталоги — при установке из .deb это делает пакет,
# при ручной сборке создайте их сами:
$ sudo install -Dm644 debian/qeli.service /etc/systemd/system/qeli.service
$ sudo useradd --system --no-create-home --shell /usr/sbin/nologin qeli
$ sudo install -d -o qeli -g qeli /etc/qeli /var/log/qeli
$ sudo install -m640 -o qeli -g qeli /dev/null /etc/qeli/users.conf
$ sudo systemctl daemon-reload
Конфигурация сервера
Откройте /etc/qeli/server.conf — единый текстовый формат (flat-INI). Минимальный профиль reality-tls на 443 с выходом в интернет:
[auth]
# Рекомендуем поставить true — сервер пустит только клиентов, заранее закрепивших
# (pinned) его ключ: защита от подменного сервера (анти-MITM) и ключ не виден
# сетевым сканерам. false = TOFU (доверие ключу при первом подключении).
require_client_key_proof = false
[profile:main]
bind.address = 0.0.0.0
bind.port = 443
bind.transport = tcp
# базовый режим; reality-tls включается ниже
obf.mode = fake-tls
tun.address = 10.0.0.1
tun.netmask = 255.255.255.0
pool.cidr = 10.0.0.0/24
# выпуск клиентов в интернет
routing.nat.enabled = true
# резолвер в туннеле
dns.enabled = true
# reality-tls: туннель внутри настоящего TLS 1.3
# server_name должен совпадать с target — именно он попадёт в ссылку qeli://
obf.tls.server_name = www.microsoft.com
obf.tls.reality_proxy.enabled = true
# чей сертификат заимствуем
obf.tls.reality_proxy.target = www.microsoft.com
# токен клиента (reality_sid)
obf.tls.reality_proxy.short_ids = 7e78a17ad41f1004
# настоящий TLS (handrolled по умолч.)
obf.tls.reality_proxy.real_tls = true
Глобальные секции — [auth], [web], [logging]; каждый интерфейс — это [profile:имя]. Можно поднять несколько профилей сразу (TCP и UDP на разных портах). Комментарии — только на отдельной строке.
obf.mode задаёт базовый режим: fake-tls (по умолчанию), obfs (нужен obf.obfs_key) или plain. Флагманский reality-tls включается поверх fake-tls блоком obf.tls.reality_proxy.* (real_tls = true — туннель внутри настоящего TLS 1.3). Для UDP-профиля: obf.quic.enabled = true и bind.transport = udp.
Запуск как служба
Через systemd (рекомендуется): служба qeli работает под отдельным пользователем с минимальными правами (только сетевые capabilities), сама перезапускается при сбое.
$ sudo systemctl enable --now qeli
$ journalctl -u qeli -f # смотреть логи
# либо вручную для проверки (foreground):
$ sudo qeli server --config /etc/qeli/server.conf
Пример вывода при старте — одна служба поднимает несколько режимов сразу:
$ qeli server --config /etc/qeli/server.conf
[info] qeli 0.7.13 · запуск 5 профилей
[info] Profile 'reality' tcp/443 reality-tls · основной
[ ok ] REALITY real-TLS termination enabled
[info] Profile 'tls' tcp/8443 fake-tls · запасной
[info] Profile 'obfs' tcp/8444 obfs + WebSocket
[info] Profile 'plain' tcp/8445 raw · доверенные сети
[info] Profile 'udp' udp/4443 udp-quic · QUIC v1 headers
[ ok ] web admin → https://0.0.0.0:8080
При старте сервер печатает поднятые профили и адрес веб-панели. HUP (или systemctl reload qeli) перечитывает пользователей и часть runtime-настроек без разрыва сессий. Изменения профилей, портов, TUN и транспорта требуют systemctl restart qeli.
Ключ сервера и пиннинг
Покажите публичный ключ каждого профиля — клиенты «пиннят» его, чтобы исключить подключение к подменному серверу (анти-MITM):
$ qeli show-identity --config /etc/qeli/server.conf
main a3f5…d290 # → в поле key у клиента
Скопируйте ключ в поле key клиента. Чтобы отклонять непиненных клиентов и скрыть ключ от сканеров — поставьте require_client_key_proof = true. Сменить ключ: qeli rotate-identity <профиль> (после этого клиенты обновляют key).
Заведение пользователей
Команда add-client хэширует пароль (Argon2id) и дописывает пользователя в файл. С флагами --link --host она сразу печатает ссылку qeli:// — её можно показать QR-кодом и импортировать в приложение одним нажатием.
$ qeli add-client alice --link --host vpn.example.com:443
password: x7Qp…m2K # сгенерирован, печатается один раз
qeli://alice:x7Qp…m2K@vpn.example.com:443?proto=tcp&mode=reality-tls&key=a3f5…d290&sni=www.microsoft.com&rsid=7e78a17ad41f1004
# с ограничениями:
$ qeli add-client bob --password 's3cret' --profiles main --max-sessions 2
# применить новых пользователей (без разрыва активных сессий)
$ sudo systemctl reload qeli
Файл пользователей (auth.users_file, по умолчанию /etc/qeli/users.conf) можно править и вручную — по секции на пользователя плюс шаблоны [group:*]:
[user:alice]
# добавляет qeli add-client alice
password_hash = $argon2id$…
enabled = true
# одновременных устройств (0 = без лимита)
max_sessions = 2
# фиксированный IP в туннеле (из pool.cidr)
static_ip = 10.0.0.50
# доступ только к этим профилям
profiles = main
# куда можно ходить (ACL); пусто = без ограничений
allowed_networks = 10.0.0.0/24
# лимит скорости, Мбит/с (0 = без лимита)
bandwidth.limit_mbps = 50
# наследовать лимиты из [group:premium]
group = premium
# доп. маршрут этому пользователю
route = 10.20.0.0/16 gateway=10.0.0.1
[group:premium]
bandwidth_limit_mbps = 100
max_sessions = 3
Пароль можно задать (--password) или сгенерировать автоматически — он печатается один раз и не восстанавливается (хранится только хеш). Ограничения: --profiles (доступ к профилям), --static-ip (фиксированный IP в туннеле), --max-sessions. Управлять пользователями удобнее через веб-панель.
Подключение клиента
Приложение (телефон / десктоп): импортируйте ссылку qeli:// или отсканируйте QR — сервер, режим и закреплённый ключ подтянутся сами.
CLI-клиент (Linux): заполните client.conf и запустите:
[qeli]
server = vpn.example.com:443
proto = tcp
mode = reality-tls
user = alice
pass = ••••••••
# ключ из show-identity
key = a3f5…d290
# short_id из профиля сервера
reality_sid = 7e78a17ad41f1004
# как у reality-цели
sni = www.microsoft.com
$ sudo qeli client --config /etc/qeli/client.conf
Кроме базовых полей, клиент понимает локальные опции (задаются только в файле, в ссылку qeli:// не входят) — авто-MTU, режим туннеля, kill-switch и совместимые с OpenVPN persist_tun / local / metric (а в клиентах Windows/macOS — ещё и route_file):
# локальные опции (в ссылку qeli:// не входят).
# ВАЖНО: комментарий — только отдельной строкой. Всё, что стоит после «=»,
# попадает в значение целиком, вместе с «# ...».
# авто-подбор MTU (важно на LTE/CGNAT); >0 = вручную
mtu = 0
# полный туннель; false = split-tunnel
gateway = true
# заворачивать и приватные подсети сервера
route_local = false
# эти подсети — мимо туннеля
exclude = 203.0.113.0/24
# блокировать выход, пока туннель не поднят (Linux)
kill_switch = true
# держать адаптер и маршруты между реконнектами (Win/macOS)
persist_tun = true
# авто-подключение при старте панели/супервизора
autostart = true
Поля proto, mode и (для obfs — obfs_key; для reality-tls — key, reality_sid, sni) должны совпадать с профилем сервера — проще всего импортировать ссылку qeli://, она подставит их сама. Маршруты, DNS и MTU сервер выдаёт при подключении; локальные опции выше — по желанию. Полный аннотированный список ключей — в config/client.conf; есть и алиасы режима mode = udp-quic / udp-obfs.
Несколько профилей и режимов
Один сервер поднимает несколько профилей сразу — по профилю на транспортный режим и порт; пользователи общие и работают на любом. У каждого профиля свой порт, своя TUN-подсеть и свой пул адресов. Пример (из config/server-multiprofile.conf):
[profile:reality-tls] # настоящий TLS 1.3 — TCP :443
bind.port = 443
bind.transport = tcp
obf.mode = fake-tls
obf.tls.reality_proxy.enabled = true
obf.tls.reality_proxy.real_tls = true
obf.tls.reality_proxy.target = www.microsoft.com
obf.tls.reality_proxy.short_ids = 0123456789abcdef
tun.address = 10.9.0.1
pool.cidr = 10.9.0.0/24
[profile:fake-tls] # TLS-подобный хендшейк — TCP :8443
bind.port = 8443
bind.transport = tcp
obf.mode = fake-tls
tun.address = 10.9.1.1
pool.cidr = 10.9.1.0/24
[profile:obfs] # ChaCha20-обфускация — TCP :8444
bind.port = 8444
bind.transport = tcp
obf.mode = obfs
obf.obfs_key = смените-меня
tun.address = 10.9.2.1
pool.cidr = 10.9.2.0/24
[profile:udp-quic] # fake-tls + QUIC поверх UDP — UDP :8449
bind.port = 8449
bind.transport = udp
obf.mode = fake-tls
obf.quic.enabled = true
tun.address = 10.9.7.1
pool.cidr = 10.9.7.0/24
У каждого профиля — свой порт, TUN (tun.address / tun.name) и пул (pool.cidr); пользователи общие. Готовый шаблон со всеми 6 режимами (reality-tls, reality, fake-tls, obfs, plain, quic) — в config/server-multiprofile.conf. Ограничить пользователя одним профилем — ключом profiles в его секции.
Выход в интернет через сервер
Весь трафик устройства идёт через сервер и выходит в интернет с его IP — классический VPN: приватность в публичных сетях, единый внешний адрес. На сервере нужен NAT, а на клиенте — gateway = true: по умолчанию клиент работает в режиме split-tunnel.
# MASQUERADE клиентов в интернет
routing.nat.enabled = true
# резолвер в туннеле
dns.enabled = true
# полный туннель включается НА КЛИЕНТЕ: по умолчанию gateway = false (split-tunnel)
gateway = true
# опционально: не выпускать трафик мимо туннеля, пока он не поднят (Linux)
kill_switch = true
Доступ к сети за сервером
Дотянуться из любой точки до локальной сети за сервером — NAS, камеры, принтеры, внутренние сервисы. Сервер раздаёт маршрут к подсети повторяемым ключом route, и клиент применяет его к туннелю автоматически.
# форвардинг в приватные сети
routing.forward_private = true
# подсеть за сервером
route = 192.168.1.0/24 gateway=10.0.0.1
Маршрут можно выдать и индивидуально — тем же ключом route в секции [user:имя] (переопределяет глобальные). Так, например, подрядчик получит доступ только к одной подсети.
Объединение сетей (site-to-site)
Соедините целые сети нескольких площадок так, чтобы их узлы видели друг друга — связать офисы, филиалы, домашнюю и рабочую сеть. На каждой площадке ставится шлюз с qeli client и фиксированным IP в туннеле; сервер раздаёт маршруты к подсетям площадок, и трафик ходит между сетями через туннель на уровне L3.
[user:branch-a]
# LAN ЗА этим клиентом — аналог iroute в OpenVPN (ключевая строка)
client_subnet = 192.168.50.0/24
# фиксированный IP в туннеле — удобно, но не обязательно
static_ip = 10.0.0.10
# site-to-site идёт БЕЗ NAT — реальные адреса сохраняются
routing.nat.enabled = false
# транзит туннель ↔ сети, без MASQUERADE
routing.forward_private = true
# обратный маршрут к сети за сервером — пушится клиентам
route = 10.20.0.0/24 gateway=10.0.0.1
[qeli]
# ip_forward + FORWARD ACCEPT + MSS-clamp, но БЕЗ MASQUERADE —
# реальные адреса LAN сохраняются
forward = true
Ключевая строка — client_subnet у пользователя: она регистрирует подсеть за клиентом как входящий маршрут. Без неё сервер дропнет пакеты на любой адрес, кроме пулового IP клиента. На шлюзе площадки — forward = true (форвардинг без NAT). Доступно с 0.7.11; на Android не поддерживается. Адресацию площадок держите непересекающейся.
Раздельный туннель (split-tunnel)
В туннель идёт только то, что нужно — например, рабочие подсети, — а остальной трафик клиента идёт напрямую. Режим выбирает клиент: gateway = false (это значение по умолчанию), а сервер лишь раздаёт нужные маршруты. Отдельные подсети можно, наоборот, увести мимо туннеля ключом exclude.
# без полного выхода в интернет
routing.nat.enabled = false
# только рабочие подсети
route = 10.50.0.0/16 gateway=10.0.0.1
# split-tunnel — значение по умолчанию: в туннель идут только выданные маршруты
gateway = false
# при желании увести отдельные подсети мимо туннеля
exclude = 203.0.113.0/24
Команды управления
Работают на лету через управляющий сокет, без перезапуска сервера. Исключения — rotate-identity и set-web-password: они правят конфиг на диске, и изменения вступают в силу после systemctl restart qeli.
qeli list-clientsкто сейчас подключёнqeli kick <user>отключить пользователяqeli disable-user <user>заблокировать (отключить + запретить реконнект)qeli enable-user <user>снова разрешить входqeli set-bandwidth <user> <mbps>лимит скорости (0 = без лимита)qeli show-routes <user>маршруты пользователяqeli rotate-identity <profile>сменить ключ сервера у профиляqeli set-web-passwordзадать пароль входа в веб-панельУправление через браузер
Включите секцию [web] — и получите панель управления: все настройки профилей, пользователи, группы, маршруты и ротация ключей сервера правятся прямо из браузера, без SSH и редактирования файлов. Вход защищён (Argon2id + same-origin CSRF). Для публикации наружу есть встроенный HTTPS (self-signed или ваш сертификат), список разрешённых IP (allowed_ips), security-заголовки и режим fail-closed — отдельный reverse-proxy не обязателен. А если он нужен, панель отдаётся по префиксу (base_path, напр. /qeli/); тумблер CSRF (csrf) и своя защита входа от перебора (отдельно от VPN) — тоже в конфиге. Интерфейс на русском и английском, работает без внешнего CDN.
[web]
enabled = true
# 0.0.0.0 = наружу (тогда обязателен tls + пароль)
bind = 127.0.0.1
port = 8080
username = admin
# проще: qeli set-web-password
password_hash = $argon2id$…
# — публикация наружу —
# встроенный HTTPS (self-signed или ваш сертификат)
tls = true
# tls_cert = /etc/letsencrypt/live/host/fullchain.pem
# tls_key = /etc/letsencrypt/live/host/privkey.pem
# белый список источников (впишите свой IP!)
allowed_ips = 203.0.113.4
# хост по умолчанию для ссылок и QR
public_host = vpn.example.com
# — за reverse-proxy —
# префикс пути (пусто = корень)
base_path = /qeli
# домены для CSRF за прокси
allowed_origins = panel.example.com
# чьим X-Forwarded-For доверять
trusted_proxies = 10.0.0.0/8
# — прочее —
# CSRF-защита (выключать только на loopback)
csrf = true
# время жизни сессии, сек (макс. 30 дней)
session_ttl_secs = 86400
# уведомлять о новых версиях (opt-in)
update_check = false
# защита входа в панель, отдельно от VPN
brute_force.max_attempts = 5