266 lines
24 KiB
Markdown
266 lines
24 KiB
Markdown
# Руководство администратора
|
||
|
||
Практическое руководство по экранам `ssh-access-tui` и типовым сценариям.
|
||
Архитектурные решения и модель угроз — в [ARCHITECTURE.md](ARCHITECTURE.md).
|
||
|
||
## Запуск
|
||
|
||
```sh
|
||
sudo ssh-access-tui
|
||
```
|
||
|
||
Требует root (создаёт/блокирует системных пользователей, правит
|
||
`authorized_keys`). Инструмент рассчитан на **одного** администратора
|
||
одновременно на хосте — если кто-то ещё уже открыл TUI, вторая сессия
|
||
получит ошибку про занятую блокировку состояния (`state.lock`), а не молча
|
||
перезапишет чужие изменения.
|
||
|
||
Навигация везде: стрелки/`j`/`k` — перемещение по списку, `enter` —
|
||
выбрать/подтвердить, `esc` — назад, `ctrl+c` — жёсткий выход в любой момент
|
||
(без каких-либо предупреждений — только для аварийных случаев). `q` как
|
||
быстрый выход работает **только в главном меню** (на остальных экранах он
|
||
ничего не делает — «esc» уводит назад). Обычный выход (пункт «Выход» или
|
||
`q` в главном меню) предупредит, если есть:
|
||
|
||
- пользователи, для которых ни разу не сгенерирован ключ (не были в
|
||
«Экспорт архивов»);
|
||
- пользователи, у которых **изменился доступ, но конфиг у них старый**
|
||
(кто-то поменял группы/каталог после их последнего экспорта или
|
||
обновления конфига) — им нужно прислать новый `config` через «Обновить
|
||
конфиги», иначе их локальный конфиг не соответствует их реальным правам.
|
||
|
||
В обоих случаях можно выйти всё равно (`enter`/`y`) или вернуться
|
||
(`esc`/`n`). Главное меню всегда показывает актуальное количество таких
|
||
пользователей под списком пунктов, если оно не нулевое — не нужно ждать
|
||
выхода, чтобы это заметить.
|
||
|
||
**Любое деструктивное действие** (блокировка/удаление пользователя,
|
||
удаление хоста/сервиса/группы, перевыпуск уже выданного ключа) требует не
|
||
`enter`/`y`, а **набрать вручную короткую случайную строку**, которую
|
||
инструмент показывает на экране подтверждения (например `kqzvm-482`) — она
|
||
каждый раз новая. Неверный ввод просто откатывает к полю ввода с пояснением
|
||
«не совпадает», ничего не меняя; `esc` отменяет действие полностью в любой
|
||
момент. Это защита именно от «промахнулся мимо enter» — набрать чужую
|
||
случайную строку по ошибке невозможно.
|
||
|
||
## Главное меню
|
||
|
||
```
|
||
Пользователи — создать / заблокировать / разблокировать / удалить
|
||
Хосты и сервисы — общий каталог адресов и портов (один раз на всех)
|
||
Группы — какие сервисы из каталога входят в каждую группу
|
||
Экспорт архивов — сгенерировать ключи и .7z для доставки
|
||
Обновить конфиги — перевыпустить config (без ключа) тем, у кого изменился доступ
|
||
Аудит / кто в сети — активные сессии и журнал подключений
|
||
Выход
|
||
```
|
||
|
||
Имена хостов, сервисов и групп должны быть без пробелов (латиница, цифры,
|
||
`. _ -`) — они попадают в конфиг и в `authorized_keys`; инструмент откажет с
|
||
понятной ошибкой, если ввести имя с пробелом.
|
||
|
||
## Модель: каталог → группы → пользователи
|
||
|
||
Хосты и их сервисы (порты) описываются **один раз** в общем каталоге
|
||
(«Хосты и сервисы»), а не заново для каждой группы. Доступ раздаётся **по
|
||
конкретным сервисам**, не по хосту целиком: группа выбирает нужные сервисы
|
||
из дерева «хост → его сервисы» — можно взять только `ssh` этого хоста, не
|
||
трогая его же `web-admin`. Если у вас в разных группах фигурируют одни и те
|
||
же хосты/сервисы — их не нужно набивать повторно, только отметить нужные
|
||
для очередной группы. Пользователь состоит в одной или нескольких группах;
|
||
его реальный доступ — объединение сервисов, выбранных во всех его группах.
|
||
|
||
## Сценарий 1: новый сотрудник, у которого уже есть роль/группа доступа
|
||
|
||
Обычно каталог и группы настраивают заранее (или один раз добавляют то,
|
||
чего не хватает), потом заводят пользователей пачками.
|
||
|
||
1. **Хосты и сервисы** → `n` → имя хоста (например `db-prod-1`), адрес (как
|
||
виден с jump-хоста), описание (необязательно) → `enter`. Сразу
|
||
открывается список сервисов этого хоста (изначально пуст).
|
||
2. `n` → имя сервиса (например `ssh` или `web-api`), порт на хосте
|
||
(например `22` или `7888`), описание (необязательно) → `enter`. Повторите
|
||
`n` для каждого сервиса на этом хосте.
|
||
3. `esc` — назад к списку хостов; повторите шаги 1-2 для остальных хостов,
|
||
затем `esc` — в главное меню. Если хост/сервис уже существует и нужно
|
||
только поправить адрес/порт/описание — `e` вместо `n` на нужной записи
|
||
(можно поменять и имя — все группы, которые ссылались на старое имя,
|
||
автоматически обновятся).
|
||
4. **Группы** → `n` → имя группы (например `team-billing`), описание
|
||
(необязательно) → `enter`.
|
||
5. Выбрать группу → `enter` — откроется дерево «хост → его сервисы».
|
||
`space` на строке сервиса отмечает именно его; `space` на строке хоста
|
||
(заголовок) отмечает/снимает сразу все его сервисы — удобно, когда нужен
|
||
весь хост, но при желании можно оставить только часть его сервисов.
|
||
`[x]` — выбрано всё, `[~]` — выбрано частично, `[ ]` — ничего. `enter`
|
||
сохранить.
|
||
6. **Пользователи** → `n` → имя пользователя (латиницей, будущий Linux-логин)
|
||
и комментарий (например ФИО) → `enter`.
|
||
7. Отметить `space` нужные группы (можно несколько). Ниже списка групп на
|
||
этом же экране — чекбокс «ProxyJump вместо LocalForward»: по умолчанию
|
||
выключен (клиентский конфиг — один `Host jump` с `LocalForward` на
|
||
каждый сервис, всегда открытое соединение); включите его, если вместо
|
||
этого нужен конфиг с отдельным `Host <alias>` на каждый сервис
|
||
(`ProxyJump jump`, обычная команда `ssh <alias>` без постоянно
|
||
открытого туннеля и без локальных портов) — например, для пользователей
|
||
с привычными SSH-клиентами не-Windows или когда локальные порты
|
||
неудобны. Права доступа на сервере не зависят от этого выбора — это
|
||
только формат выдаваемого файла `config`. → `enter`.
|
||
|
||
Это уже создаёт системный Linux-аккаунт (без пароля, без интерактивного
|
||
шелла) и запись в состоянии. **Ключ ещё не выдан** — статус в списке
|
||
пользователей покажет «ключ не выдан».
|
||
|
||
8. **Экспорт архивов** → отметить `space` только что созданного пользователя
|
||
(или `a` — отметить всех, удобно при пакетном найме) → `enter`. Для
|
||
пользователей без ключа архив генерируется сразу, без лишних вопросов —
|
||
это не деструктивное действие. Инструмент сгенерирует ключ, пропишет
|
||
`permitopen=` под текущие сервисы группы и соберёт `.7z`, показав **один
|
||
общий пароль на всю партию** — он показывается один раз,
|
||
запишите/скопируйте сразу.
|
||
9. Передайте каждому пользователю его архив (файл, путь показан на экране,
|
||
лежит в `export_dir`) любым каналом, а пароль от архива — **отдельным**
|
||
каналом (голосом, в личку) и по возможности не всем одним и тем же
|
||
сообщением в общем чате.
|
||
10. Пользователь распаковывает архив на Windows по инструкции
|
||
`INSTRUCTIONS.txt` внутри архива — она на простом русском языке, без
|
||
технических терминов: положить файлы в папку, ввести одну команду и
|
||
держать окно открытым, дальше список «такой адрес у вас локально → что
|
||
это → где реально находится», составленный под конкретные сервисы этого
|
||
пользователя.
|
||
|
||
## Сценарий 1а: изменить группы уже существующего пользователя
|
||
|
||
**Пользователи** → выбрать пользователя → `g` → отметить/снять `space`
|
||
нужные группы → `enter`. Если у пользователя уже выдан ключ, инструмент
|
||
сразу пересоберёт его `permitopen=` под новый набор сервисов — доступ
|
||
меняется немедленно, без переотправки архива и **без замены ключа**
|
||
(изменение групп никогда не ротирует ключ).
|
||
|
||
На этом же экране есть чекбокс «ProxyJump вместо LocalForward» (см.
|
||
сценарий 1, шаг 7) — можно переключить режим клиентского конфига и
|
||
существующему пользователю. В отличие от смены групп, переключение этого
|
||
чекбокса **не применяется автоматически**: инструмент не отслеживает смену
|
||
стиля конфига как «устаревший» (только смену состава сервисов), поэтому
|
||
после переключения нужно вручную прогнать «Обновить конфиги» (см. ниже),
|
||
иначе у пользователя на диске просто останется конфиг в старом формате —
|
||
доступ на сервере это никак не ограничивает.
|
||
|
||
Если хотите, чтобы у пользователя появился ещё и обновлённый клиентский
|
||
`config` с новыми адресами для новых сервисов (иначе он про них просто не
|
||
узнает, хотя доступ уже есть) — или чтобы применить только что
|
||
переключённый режим ProxyJump/LocalForward — **Пользователи** → выбрать
|
||
пользователя → `c` («обновить конфиг»), либо (то же самое, но сразу для
|
||
многих) **Обновить конфиги** в главном меню. Это генерирует архив только с
|
||
`config` + `INSTRUCTIONS.txt` (без ключа — ключ у пользователя уже есть и
|
||
остаётся тем же), пользователю нужно только подменить файл `config`. Не
|
||
требует подтверждения — ключ и доступ этим действием не меняются, это не
|
||
деструктивно.
|
||
|
||
**Обновить конфиги** в главном меню — то же самое, что `c`, но пакетно:
|
||
список покажет каждого уже выпущенного пользователя с пометкой «конфиг
|
||
актуален» или «конфиг устарел — доступ изменился» (см. ниже, как это
|
||
отслеживается), можно отметить `space` только устаревших или `a` — всех, и
|
||
получить один пароль на всю партию, как при обычном экспорте.
|
||
|
||
Инструмент сам запоминает, для какого набора сервисов последний раз
|
||
выдавался config каждому пользователю (полным экспортом или через
|
||
«обновить конфиг»), и сравнивает с тем, что должно быть сейчас — отсюда и
|
||
берётся статус «устарел». Полный экспорт (сценарий 1, шаги 8-9) тоже
|
||
обновляет этот статус (и заодно перевыпускает ключ, с подтверждением
|
||
строкой) — используйте `c`/«Обновить конфиги», когда переиздание ключа не
|
||
нужно.
|
||
|
||
## Сценарий 2: добавили новый сервис/хост, поменялся адрес или порт
|
||
|
||
- Новый сервис на существующем хосте: **Хосты и сервисы** → выбрать хост →
|
||
`enter` → `n`.
|
||
- Новый хост: **Хосты и сервисы** → `n`, затем добавить ему сервисы.
|
||
- Хост сменил адрес, сервис — порт, нужно поправить описание, или
|
||
переименовать: `e` на нужной записи. Переименование хоста или сервиса
|
||
автоматически обновляет ссылки на него во всех группах, которые его
|
||
используют — группы не «отваливаются» молча и не теряют доступ из-за
|
||
простого переименования.
|
||
|
||
Любое из этих изменений сразу обновляет `permitopen=` в `authorized_keys`
|
||
**всех активных пользователей всех групп**, ссылающихся на этот сервис, —
|
||
доступ появляется немедленно, без каких-либо действий на стороне
|
||
пользователя. Их клиентский конфиг (`~/.ssh/config`) при этом не
|
||
обновляется автоматически — их локальный файл начинает отставать от
|
||
реального доступа (инструмент это заметит и покажет как «конфиг устарел»,
|
||
см. выше); чтобы прислать актуальный конфиг — **Обновить конфиги** или `c`
|
||
(см. сценарий 1а).
|
||
|
||
**Группы** → выбрать группу → `enter` — то же дерево «хост → сервисы», там
|
||
же можно снять/добавить сервисы группе (не создавая и не удаляя сами
|
||
хосты/сервисы в каталоге).
|
||
|
||
Удаление хоста из каталога, сервиса на хосте или целой группы (`d` в
|
||
соответствующем списке) — все три действия **деструктивны** (мгновенно
|
||
отбирают доступ у всех затронутых пользователей) и требуют набрать
|
||
случайную строку подтверждения: экран покажет, что именно удаляется,
|
||
сколько пользователей это коснётся, и в каких группах используется
|
||
(для хоста/сервиса). Удаление хоста снимает с групп все его сервисы разом;
|
||
удаление одного сервиса снимает только его, остальные сервисы того же
|
||
хоста в группах остаются.
|
||
|
||
## Сценарий 3: сотрудник уволился
|
||
|
||
1. **Пользователи** → выбрать пользователя → `l` (заблокировать) → набрать
|
||
строку подтверждения, которую покажет экран → `enter`.
|
||
|
||
Блокировка — это одновременно: блокирует пароль аккаунта, устанавливает
|
||
немедленный срок истечения аккаунта (`chage -E 1`) и комментирует его ключ в
|
||
`authorized_keys` — три независимых механизма, чтобы блокировка не зависела
|
||
от одного из них. Статус в списке сменится на `locked`. Это **обратимо**.
|
||
|
||
Если сотрудник вернулся — `u` (разблокировать) на том же пользователе:
|
||
снимает блокировку аккаунта и восстанавливает его ключ в `authorized_keys`
|
||
с текущими правами групп. Разблокировка не деструктивна — подтверждения не
|
||
требует, срабатывает сразу.
|
||
|
||
Полное удаление аккаунта (`d`) — **необратимо** (`userdel -r`, домашняя папка
|
||
стирается) и тоже требует подтверждения строкой (экран явно предупредит,
|
||
что это необратимо и что для временного отключения лучше использовать
|
||
блокировку). Используйте удаление только для окончательного оффбординга, не
|
||
для временной блокировки.
|
||
|
||
## Сценарий 4: проверить, кто сейчас подключён / посмотреть историю
|
||
|
||
**Аудит / кто в сети** показывает:
|
||
|
||
- «Сейчас в сети» — список активных SSH-сессий (по процессам `sshd:
|
||
user [priv]`, а не по `who`, потому что форвардинг-сессии без
|
||
интерактивного терминала не создают обычных login-записей);
|
||
- «Последние события» — журнал подключений/отключений, который в фоне
|
||
собирает `ssh-access-tui-audit.service` из `journalctl`, независимо от
|
||
ротации системного журнала.
|
||
|
||
`r` — обновить оба списка. Если журнал подключений пуст, хотя пользователи
|
||
явно заходили — проверьте, что фоновый сервис запущен:
|
||
`systemctl status ssh-access-tui-audit.service`.
|
||
|
||
## Сценарий 5: перенос на новый хост / хост умер
|
||
|
||
См. подробности в [ARCHITECTURE.md](ARCHITECTURE.md#бэкап-и-аварийное-восстановление).
|
||
Коротко: состояние (каталог хостов/сервисов, группы, пользователи,
|
||
публичные ключи, статусы) — git-репозиторий, бэкапится часовым таймером в
|
||
удалённый git. На новом хосте: поднять `ssh-access-tui`, влить `state.yaml`
|
||
из бэкапа, создать системные Linux-аккаунты для активных пользователей,
|
||
выпустить им новые ключи через **Экспорт архивов** (старые приватные ключи
|
||
специально нигде не хранятся — это не баг, см. архитектурный документ).
|
||
|
||
## Что не делает эта версия (нужно руками)
|
||
|
||
- Не удаляет `.7z` из `export_dir` после доставки — сделайте это сами,
|
||
когда получатель подтвердил, что распаковал архив (это касается и
|
||
архивов от «обновить конфиг», хоть они и не содержат ключ).
|
||
- Не обновляет клиентский `~/.ssh/config` пользователя автоматически при
|
||
появлении нового сервиса/хоста в его группе — это ручной шаг («Обновить
|
||
конфиги», сценарий 1а), хоть инструмент и отслеживает, кому именно он
|
||
нужен (главное меню и предупреждение при выходе). Права доступа на
|
||
сервере при этом обновляются немедленно и без участия администратора
|
||
(см. сценарий 2).
|
||
- Не поддерживает одновременную работу нескольких администраторов —
|
||
рассчитан на одного за раз (см. «Конкурентность» в ARCHITECTURE.md).
|