add ProxyJump function

This commit is contained in:
redlineo committed 2026-07-23 12:38:13 +03:00
1 parent 56e20e5464
commit 7f0e0fdb23
48 files changed
+3875 -44

No files matched your search

+23 -4
View File
@@ -96,7 +96,16 @@ sudo ssh-access-tui
сохранить.
6. **Пользователи** → `n` → имя пользователя (латиницей, будущий Linux-логин)
и комментарий (например ФИО) → `enter`.
7. Отметить `space` нужные группы (можно несколько) → `enter`.
7. Отметить `space` нужные группы (можно несколько). Ниже списка групп на
этом же экране — чекбокс «ProxyJump вместо LocalForward»: по умолчанию
выключен (клиентский конфиг — один `Host jump` с `LocalForward` на
каждый сервис, всегда открытое соединение); включите его, если вместо
этого нужен конфиг с отдельным `Host <alias>` на каждый сервис
(`ProxyJump jump`, обычная команда `ssh <alias>` без постоянно
открытого туннеля и без локальных портов) — например, для пользователей
с привычными SSH-клиентами не-Windows или когда локальные порты
неудобны. Права доступа на сервере не зависят от этого выбора — это
только формат выдаваемого файла `config`. → `enter`.
Это уже создаёт системный Linux-аккаунт (без пароля, без интерактивного
шелла) и запись в состоянии. **Ключ ещё не выдан** — статус в списке
@@ -128,11 +137,21 @@ sudo ssh-access-tui
меняется немедленно, без переотправки архива и **без замены ключа**
(изменение групп никогда не ротирует ключ).
На этом же экране есть чекбокс «ProxyJump вместо LocalForward» (см.
сценарий 1, шаг 7) — можно переключить режим клиентского конфига и
существующему пользователю. В отличие от смены групп, переключение этого
чекбокса **не применяется автоматически**: инструмент не отслеживает смену
стиля конфига как «устаревший» (только смену состава сервисов), поэтому
после переключения нужно вручную прогнать «Обновить конфиги» (см. ниже),
иначе у пользователя на диске просто останется конфиг в старом формате —
доступ на сервере это никак не ограничивает.
Если хотите, чтобы у пользователя появился ещё и обновлённый клиентский
`config` с новыми адресами для новых сервисов (иначе он про них просто не
узнает, хотя доступ уже есть) — **Пользователи** → выбрать пользователя →
`c` («обновить конфиг»), либо (то же самое, но сразу для многих)
**Обновить конфиги** в главном меню. Это генерирует архив только с
узнает, хотя доступ уже есть) — или чтобы применить только что
переключённый режим ProxyJump/LocalForward — **Пользователи** → выбрать
пользователя → `c` («обновить конфиг»), либо (то же самое, но сразу для
многих) **Обновить конфиги** в главном меню. Это генерирует архив только с
`config` + `INSTRUCTIONS.txt` (без ключа — ключ у пользователя уже есть и
остаётся тем же), пользователю нужно только подменить файл `config`. Не
требует подтверждения — ключ и доступ этим действием не меняются, это не
+55 -15
View File
@@ -46,13 +46,20 @@ TUI работает поверх уже существующего SSH+`sudo`-
(`State.DeleteHost`, `State.RemoveServiceRefs`) — переименование/удаление
одного сервиса никогда не задевает остальные сервисы того же хоста.
Ключевой момент: пользователь **никогда не подключается по SSH к целевому
хосту напрямую** (ни через `ProxyJump`, ни как-то ещё) — он открывает одно
соединение к самому jump-хосту и локально пробрасывает (`LocalForward`)
порты к каждому нужному сервису через это соединение. Не важно, слушает ли
на целевом порту настоящий SSH (22) или произвольный TCP-сервис (7888) —
для форвардинга это не имеет значения, оба варианта — это просто
"локальный порт → host:port через jump".
Ключевой момент: и в `LocalForward`, и в `ProxyJump`-режиме клиентского
конфига (`model.User.ProxyJump`, см. ниже) канал до целевого хоста —
это всегда один и тот же серверный примитив, `direct-tcpip` через
jump-хост: пользователь никогда не получает интерактивный шелл на целевом
хосте через jump, только форвардинг порта до него. В `LocalForward`-режиме
он открывает одно соединение к самому jump-хосту и локально пробрасывает
порты к каждому нужному сервису через это соединение; в `ProxyJump`-режиме
клиентский `ssh` сам открывает по одному `direct-tcpip`-каналу через то же
соединение к jump-хосту на время каждой команды (`ssh <alias>`), без
постоянно висящего туннеля и без локальных портов — разница только в том,
как клиент организует один и тот же тип канала, а не в том, что jump-хост
разрешает. Не важно, слушает ли на целевом порту настоящий SSH (22) или
произвольный TCP-сервис (7888) — для форвардинга это не имеет значения,
оба варианта — это просто "порт → host:port через jump".
## Как на самом деле работает ограничение "только прыжок/форвардинг"
@@ -84,14 +91,41 @@ TUI работает поверх уже существующего SSH+`sudo`-
повреждены/потеряны, а не основной механизм.
2. **Клиент (только удобство).** Файл `~/.ssh/config`, который уезжает
пользователю в архиве, — это один блок `Host jump` с набором строк
`LocalForward <локальный_порт> <host>:<port>` (`internal/keymgr/clientconfig.go`).
Он ничего не запрещает: пользователь может отредактировать его или
вызвать `ssh` руками с любым хостом. Единственная причина, по которой это
не работает, — сервер отклонит `permitopen`, которого нет в списке.
Список форвардов в клиентском конфиге должен генерироваться из того же
источника правды (группы в `state.yaml`), что и `permitopen=`, — иначе
конфиг и реальные права разойдутся.
пользователю в архиве, зависит от `model.User.ProxyJump` (переключается
чекбоксом на экране групп пользователя, `internal/ui/users.go`) и
генерируется одним из двух рендереров
(`internal/keymgr/clientconfig.go`), но оба **одинаково не являются
границей безопасности**:
- `false` (по умолчанию, `LocalForward`) — один блок `Host jump` с
набором строк `LocalForward <локальный_порт> <host>:<port>`, одно
всегда открытое соединение сразу даёт локальные порты на все сервисы
(`RenderClientConfig`).
- `true` (`ProxyJump`) — отдельный блок `Host <alias>` на каждый
разрешённый сервис с `ProxyJump jump` вместо `HostName jump`, без
локальных портов и без постоянно открытого соединения — каждый вызов
`ssh <alias>` сам поднимает и закрывает канал по требованию
(`RenderClientConfigProxyJump`).
Ни один из двух вариантов ничего не запрещает: пользователь может
отредактировать любой из них или вызвать `ssh` руками с любым хостом.
Единственная причина, по которой это не работает, — сервер отклонит
`permitopen`, которого нет в списке, и это ограничение **идентично для
обоих режимов**: `BuildUserArchive`/`BuildConfigOnlyArchive` в обоих
случаях берут один и тот же `[]model.Forward` (`State.AllowedForwards`)
и не трогают `internal/sysadm/authorizedkeys.go` — переключение
`ProxyJump` меняет только то, как выглядит клиентский конфиг, а не какие
`permitopen=` попадают в `authorized_keys`. Список форвардов в
клиентском конфиге (в любом из двух форматов) должен генерироваться из
того же источника правды (группы в `state.yaml`), что и `permitopen=`, —
иначе конфиг и реальные права разойдутся.
Переключение режима у уже существующего пользователя не применяется
автоматически: `ForwardsHash`/`IsConfigStale` отслеживают только
изменения состава форвардов, а не смену стиля конфига (само
переключение `ProxyJump` без изменения групп не помечает конфиг как
устаревший) — администратор должен вручную прогнать «Обновить конфиги»
после переключения, если хочет, чтобы у пользователя оказался конфиг
нового формата.
Reverse/remote forwarding (`-R`) в этой модели не выдаётся вообще
(`AllowStreamLocalForwarding no`, никаких `permitlisten=`) — по вашему же
@@ -249,3 +283,9 @@ session" вместо тихой порчи данных. Если реальн
получение.
- Полностью параллельная работа нескольких админов не поддерживается
(см. «Конкурентность»).
- Переключение `ProxyJump`/`LocalForward` у уже существующего пользователя
не переиздаёт и не помечает его конфиг устаревшим автоматически (см.
выше, «Клиент (только удобство)») — после переключения нужно вручную
прогнать «Обновить конфиги» для этого пользователя, иначе у него на
диске останется конфиг старого формата (что ничего не ломает: доступ на
сервере не зависит от этого режима, только удобство пользователя).