v0.2.0
This commit is contained in:
1 parent
23c7133fa9
commit
8c1b93c14e
16 files changed
+1187
-295
No files matched your search
+49
-12
@@ -19,6 +19,24 @@ TUI работает поверх уже существующего SSH+`sudo`-
|
||||
реализовать вручную (см. «Конкурентность» ниже). Для небольшой команды
|
||||
администраторов это разумный компромисс.
|
||||
|
||||
## Модель доступа: Host → Service → Forward
|
||||
|
||||
Хост (`Host`) — это адрес (например, `172.16.1.20`). На одном хосте может
|
||||
быть несколько сервисов (`Service`) — независимых TCP-портов: `ssh` на 22,
|
||||
какое-то приложение на 7888, и так далее. Группа (`Group`) — это набор
|
||||
хостов (со всеми их сервисами), пользователь может состоять в нескольких
|
||||
группах; его реальный эффективный доступ — объединение сервисов всех хостов
|
||||
всех его групп, это и называется `Forward` (`internal/model/types.go`,
|
||||
`State.AllowedForwards`).
|
||||
|
||||
Ключевой момент: пользователь **никогда не подключается по SSH к целевому
|
||||
хосту напрямую** (ни через `ProxyJump`, ни как-то ещё) — он открывает одно
|
||||
соединение к самому jump-хосту и локально пробрасывает (`LocalForward`)
|
||||
порты к каждому нужному сервису через это соединение. Не важно, слушает ли
|
||||
на целевом порту настоящий SSH (22) или произвольный TCP-сервис (7888) —
|
||||
для форвардинга это не имеет значения, оба варианта — это просто
|
||||
"локальный порт → host:port через jump".
|
||||
|
||||
## Как на самом деле работает ограничение "только прыжок/форвардинг"
|
||||
|
||||
Это два независимых слоя, и путать их нельзя:
|
||||
@@ -27,18 +45,20 @@ TUI работает поверх уже существующего SSH+`sudo`-
|
||||
`authorized_keys` пользователя приписаны опции:
|
||||
|
||||
```
|
||||
restrict,port-forwarding,permitopen="10.0.1.5:5432",permitopen="10.0.2.9:443" ssh-ed25519 AAAA... user@jump
|
||||
restrict,port-forwarding,permitopen="172.16.1.20:22",permitopen="172.16.1.20:7888",permitopen="172.16.1.21:22" ssh-ed25519 AAAA... user@jump
|
||||
```
|
||||
|
||||
`restrict` разом отключает pty, X11-forwarding, agent-forwarding,
|
||||
user-rc и port-forwarding; `port-forwarding` точечно возвращает именно
|
||||
форвардинг (не интерактивный доступ), а `permitopen=` разрешает
|
||||
форвардинг только к перечисленным `host:port` — это гарантия на уровне
|
||||
протокола SSH, не зависящая от того, что написано в клиентском конфиге.
|
||||
Shell пользователя — `/usr/sbin/nologin`: любой запрос shell/exec-канала
|
||||
(обычный интерактивный `ssh user@jump`) обрывается им же, а
|
||||
direct-tcpip-каналы (прыжок/форвардинг) идут отдельным путём и
|
||||
регулируются исключительно `permitopen=`.
|
||||
Один `permitopen=` на каждый `Forward` пользователя (`internal/sysadm/authorizedkeys.go`,
|
||||
`BuildKeyOptions`) — не важно, сколько сервисов на одном хосте или сколько
|
||||
разных хостов используют один и тот же номер порта, каждая пара
|
||||
`host:port` разрешается независимо. `restrict` разом отключает pty,
|
||||
X11-forwarding, agent-forwarding, user-rc и port-forwarding;
|
||||
`port-forwarding` точечно возвращает именно форвардинг (не интерактивный
|
||||
доступ). Это гарантия на уровне протокола SSH, не зависящая от того, что
|
||||
написано в клиентском конфиге. Shell пользователя — `/usr/sbin/nologin`:
|
||||
любой запрос shell/exec-канала (обычный интерактивный `ssh user@jump`)
|
||||
обрывается им же, а direct-tcpip-каналы (форвардинг) идут отдельным путём
|
||||
и регулируются исключительно `permitopen=`.
|
||||
|
||||
Дополнительно в `Match Group ssh-jump-users` (`internal/sysadm/sshdconfig.go`)
|
||||
продублированы базовые ограничения (`PermitTTY no`, `X11Forwarding no`,
|
||||
@@ -47,11 +67,12 @@ TUI работает поверх уже существующего SSH+`sudo`-
|
||||
повреждены/потеряны, а не основной механизм.
|
||||
|
||||
2. **Клиент (только удобство).** Файл `~/.ssh/config`, который уезжает
|
||||
пользователю в архиве, — это алиасы (`Host db-prod-1` → `ProxyJump jump`).
|
||||
пользователю в архиве, — это один блок `Host jump` с набором строк
|
||||
`LocalForward <локальный_порт> <host>:<port>` (`internal/keymgr/clientconfig.go`).
|
||||
Он ничего не запрещает: пользователь может отредактировать его или
|
||||
вызвать `ssh` руками с любым хостом. Единственная причина, по которой это
|
||||
не работает, — сервер отклонит `permitopen`, которого нет в списке.
|
||||
Список хостов в клиентском конфиге должен генерироваться из того же
|
||||
Список форвардов в клиентском конфиге должен генерироваться из того же
|
||||
источника правды (группы в `state.yaml`), что и `permitopen=`, — иначе
|
||||
конфиг и реальные права разойдутся.
|
||||
|
||||
@@ -60,6 +81,22 @@ Reverse/remote forwarding (`-R`) в этой модели не выдаётся
|
||||
решению при проектировании: этот джамп-хост используется только для входа
|
||||
внутрь сети, не для проброса портов наружу из неё.
|
||||
|
||||
## Коллизии локальных портов
|
||||
|
||||
Пользователю нужно пробросить сразу много сервисов за одно соединение
|
||||
(например, 10 хостов, на каждом порт 22 и порт 7888 — 20 форвардов сразу).
|
||||
Локально на его машине два разных удалённых назначения не могут занимать
|
||||
один и тот же локальный порт одновременно, а несколько хостов с одинаковым
|
||||
портом (22 или 7888 у всех десяти) — обычная ситуация. Поэтому локальный
|
||||
порт **не всегда равен удалённому**: `internal/keymgr/portalloc.go`
|
||||
(`AllocateLocalPorts`) для каждого форварда сначала пробует занять локальный
|
||||
порт, совпадающий с удалённым (удобно и предсказуемо, когда порт встречается
|
||||
один раз), а при конфликте — берёт следующий свободный из диапазона `20000+`.
|
||||
Присвоение детерминировано (зависит только от порядка и состава форвардов
|
||||
пользователя), поэтому повторный экспорт архива с тем же набором хостов даёт
|
||||
те же локальные порты. Итоговое соответствие "локальный порт → сервис"
|
||||
попадает в `INSTRUCTIONS.txt` внутри архива пользователя.
|
||||
|
||||
## Жизненный цикл приватного ключа
|
||||
|
||||
Приватный ключ **никогда не хранится на диске дольше одной операции
|
||||
|
||||
Reference in new issue
Block a user