- Shell 63.3%
- Nix 36.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- cmd_status больше не дампит сырой конфиг с комментариями и journalctl -n 10 — вместо этого короткая цветная сводка (пути, PAM/UsePAM, сессии, кто логинился сегодня/сейчас, Gotify/гео/TTY/выход, списки исключений только если непустые) и 3 последних события из лога в том же стиле подсветки, что и ssh-notify logs. Уважает NO_COLOR и неинтерактивный вывод. - Убрано выравнивание через printf %-Ns: оно считает байты, а не символы, и с кириллицей (2 байта на букву) колонки съезжали вкривь. Формат упрощён до "Метка: значение". - Исправлен реальный баг: последней командой сабшелла с настройками была "[ -n "$IGNORE_PAIRS" ] && row ..." — при пустом списке она возвращает код 1, и под set -e это молча обрывало весь ssh-notify status кодом выхода 1 без единого сообщения об ошибке. Добавлен true последней командой сабшелла. |
||
| tools | ||
| .gitignore | ||
| install-ssh-notify.sh | ||
| LICENSE | ||
| README.md | ||
| ssh-notify.nix | ||
| ssh-notify.sh | ||
ssh-gotify-notify
Кто, откуда и насколько зашёл на твой сервер по SSH.
Подключается к PAM через pam_exec и шлёт уведомление в
Gotify при открытии и закрытии сессии.
Вход: пользователь, IP, rDNS, город/страна/ASN (через ipinfo.io), метод аутентификации (publickey/password), TTY, время, uptime хоста.
Выход: пользователь, IP, длительность сессии.
SSH вход: alice@web-01
Пользователь: alice
IP: 203.0.113.42
rDNS: host.example.net
Гео: Berlin, Berlin, DE, AS3320 Deutsche Telekom AG
Метод: publickey
TTY: pts/0
Время: 2026-09-17 21:30:24 MSK
Uptime: up 12 days, 4 hours
Установка
Скачай управляющую команду в /usr/local/bin и сделай исполняемой:
sudo curl -fsSL https://raw.githubusercontent.com/Mimic890/ssh-gotify-notify/main/install-ssh-notify.sh -o /usr/local/bin/ssh-notify && sudo chmod +x /usr/local/bin/ssh-notify
Дальше всё делается командой ssh-notify, путь помнить не нужно:
sudo ssh-notify install
Установщик спросит только URL и токен (это разово, дальше не спрашивает
ничего), поставит curl и jq, отправит два тестовых сообщения — и
только если они дошли, тронет PAM. Не дошли — ничего не меняется.
Остальные настройки (гео, списки исключений, файл лога, приоритеты) — не
через диалог, а прямо в /etc/ssh-notify.conf: файл со своими
комментариями на каждую настройку, правится руками и перечитывается на
каждый вход без перезапуска.
Поддерживаются Debian, Ubuntu, Arch, Fedora, openSUSE и Alpine: пакеты
ставятся через apt-get, pacman, dnf, yum, zypper или apk —
какой найдётся. Проверялось вживую только на Debian/Ubuntu.
Команды
ssh-notify
Без аргументов (нужен root) — список команд, версия, статус, кто сегодня
заходил и кто залогинен прямо сейчас. То же самое, что ssh-notify status,
только со справкой сверху.
sudo ssh-notify status
sudo ssh-notify test
sudo ssh-notify logs
sudo ssh-notify install
sudo ssh-notify update
Скачивает свежую версию установщика с GitHub, ставит её поверх себя и сразу переустанавливается с уже сохранёнными в конфиге настройками — ничего заново не спрашивает.
sudo ssh-notify uninstall
install можно запускать повторно: если конфиг уже есть, install ничего
не спрашивает, просто обновляет свои файлы и PAM-строку поверх. Сменить
настройки — не через install, а прямым редактированием /etc/ssh-notify.conf.
История входов
Каждое событие пишется в /var/log/ssh-notify.log тем же блоком, что
уходит в Gotify, независимо от того, дошло ли уведомление. Если
Gotify был недоступен, событие всё равно в истории, с пометкой:
=== 2026-09-17 21:30:24 MSK · вход · alice · 203.0.113.42 ===
Пользователь: alice
IP: 203.0.113.42
rDNS: host.example.net
Гео: Berlin, Berlin, DE, AS3320 Deutsche Telekom AG
Метод: publickey
TTY: pts/0
Время: 2026-09-17 21:30:24 MSK
Uptime: up 12 days, 4 hours
Смотреть можно прямо файлом, а можно командой — она умеет фильтровать и подсвечивать:
sudo ssh-notify logs -n 5
sudo ssh-notify logs -f
| Опция | Что делает |
|---|---|
-n N, --lines N |
последние N записей (по умолчанию 20) |
-a, --all |
все записи |
-f, --follow |
следить в реальном времени |
-u, --user ПОДСТР |
фильтр по пользователю |
-i, --ip ПОДСТР |
фильтр по адресу |
--open / --close |
только входы / только выходы |
--failed |
только недоставленные в Gotify |
-g, --grep ТЕКСТ |
поиск по всему тексту записи |
--since КОГДА |
новее указанного: today, вчера, 2026-09-17, -2 hours |
--stats |
сводка: сколько, кто чаще, откуда чаще |
--path |
путь к файлу лога |
--no-color |
без подсветки |
Фильтры комбинируются и работают в том числе с -f:
sudo ssh-notify logs -u alice --since today --failed
Ротацию ставит сам установщик в /etc/logrotate.d/ssh-notify: еженедельно,
8 архивов, сжатие. Файл создаётся с правами 640 root:adm — внутри имена
пользователей и адреса. Выключить логирование можно, задав пустой
LOG_FILE в конфиге или ответив нет при установке.
Параллельно каждое событие уходит в syslog одной структурированной строкой — это выручит, если файл удалят, и хорошо грепается:
journalctl -t ssh-notify | grep 'user=alice'
uninstall убирает всё: строку из PAM, скрипты, состояние, свои бэкапы
PAM и саму команду ssh-notify. Про конфиг с токеном спросит
отдельно (--yes — удалить без вопросов). sshd_config не трогается.
NixOS
Установщик здесь не нужен и откажется работать. Возьми из репозитория
один файл — ssh-notify.nix, — положи в свои модули и подключи:
imports = [ ./ssh-notify.nix ];
services.sshNotify = {
enable = true;
url = "https://gotify.example.com";
token = "AgNGgB5xE.sdkVy1O";
ignoreNets = [ "10.*" "192.168.*" ];
ignorePairs = [ "admin@192.168.1.10" ];
};
token — прямым текстом, проще всего для быстрой настройки, но попадает
в /nix/store и в конфиг в открытом виде, читаемые любым пользователем
системы. Если это важно — используй tokenFile вместо него:
services.sshNotify = {
# ...
tokenFile = config.sops.secrets.gotify-ssh-token.path;
};
Передавай путь строкой (sops-nix, agenix), а не literal-путь вида
./token: второй Nix скопировал бы в /nix/store точно так же. Если
заданы оба, побеждает tokenFile.
После enable = true и пересборки в системе появляется команда
ssh-notify — упрощённый аналог установщика для диагностики
(настройки на NixOS правятся не конфигом, а модулем, поэтому install,
uninstall и update ей не нужны — обновление модуля идёт через
nixos-rebuild):
ssh-notify # без аргументов — команды, версия, статус, кто заходил сегодня и кто сейчас в системе
ssh-notify status # PAM подключён? UsePAM? сколько записей в логе?
ssh-notify test # отправить тестовые сообщения о входе и выходе
ssh-notify logs # история входов и выходов, те же опции, что ниже
Настройки
Правятся в /etc/ssh-notify.conf — скрипт перечитывает файл на каждый
вход, перезапускать ничего не нужно. На NixOS это одноимённые опции
services.sshNotify в camelCase.
| Ключ | По умолчанию | Что делает |
|---|---|---|
GOTIFY_URL |
— | Базовый URL Gotify, завершающий слэш убирается сам |
GOTIFY_TOKEN |
— | Application-токен |
GOTIFY_TOKEN_FILE |
— | Путь к файлу с токеном вместо самого токена |
GEO_LOOKUP |
1 |
Гео через ipinfo.io |
REQUIRE_TTY |
0 |
1 — только интерактивные сессии, без sftp/scp/rsync |
NOTIFY_CLOSE |
1 |
0 — молчать про выход |
IGNORE_USERS |
пусто | Не уведомлять об этих пользователях |
IGNORE_NETS |
пусто | Не уведомлять об этих адресах |
IGNORE_PAIRS |
пусто | Не уведомлять об этих парах пользователь@адрес |
PRIORITY_OPEN |
7 |
Приоритет уведомления о входе |
PRIORITY_CLOSE |
3 |
Приоритет уведомления о выходе |
HTTP_TIMEOUT |
10 |
Таймаут запроса к Gotify, секунды |
GEO_TIMEOUT |
5 |
Таймаут запроса к ipinfo.io, секунды |
LOG_FILE |
/var/log/ssh-notify.log |
Файл истории, пусто — не вести |
Приоритет 8 и выше пробивает «не беспокоить» на Android.
Списки исключений
IGNORE_USERS и IGNORE_NETS — glob-шаблоны через пробел, не CIDR:
IGNORE_USERS="backup rsync-*"
IGNORE_NETS="10.* 192.168.* 203.0.113.5"
Без них резервные копии и мониторинг будут звонить в телефон круглые сутки, а алерты, которые звонят слишком часто, перестают работать.
IGNORE_USERS/IGNORE_NETS глушат пользователя или адрес целиком. Если
нужно молчать только про конкретную комбинацию — например, о своих
собственных заходах из дома, но не о заходах того же аккаунта откуда-то
ещё — используй IGNORE_PAIRS: список пользователь@адрес через пробел,
где обе части снова glob-шаблоны:
IGNORE_PAIRS="admin@192.168.1.10 admin@10.0.0.*"
Тот же admin, зашедший не из этих сетей, уведомление всё равно получит.
Диагностика
Скрипт пишет в syslog под тегом ssh-notify — и успехи, и любой сбой
доставки:
journalctl -t ssh-notify -n 20
Проверить отправку, не заходя по SSH:
sudo ssh-notify test
Если вход по SSH сломался, из уже открытой сессии:
sudo sed -i '/ssh-notify-wrap/d' /etc/pam.d/sshd
Как это устроено
pam_exec вызывается синхронно, поэтому недоступный Gotify задержал бы
логин на весь таймаут curl. Между PAM и основным скриптом стоит
обёртка: она запускает работу через setsid в фоне и возвращает
управление за миллисекунду. Правило PAM — optional, так что сломанный
скрипт не может закрыть тебе вход.
Раскладка файлов:
| Путь | Что это |
|---|---|
/usr/local/bin/ssh-notify |
управляющая команда (этот же установщик) |
/usr/local/bin/ssh-notify-worker |
рабочий скрипт, шлёт уведомление |
/usr/local/bin/ssh-notify-wrap |
обёртка для pam_exec |
/etc/ssh-notify.conf |
настройки и токен, права 600 |
/var/log/ssh-notify.log |
история входов и выходов, права 640 root:adm |
/etc/logrotate.d/ssh-notify |
правило ротации лога |
/run/ssh-notify/ |
время начала сессий, чтобы считать длительность |
Длительность считается через файл в /run/ssh-notify, имя файла — PID
процесса sshd, обслуживающего сессию. Если открытие и закрытие почему-то
обработали разные процессы, в уведомлении будет «Длительность:
неизвестно» — на сам вход это не влияет. Осиротевшие файлы старше недели
подчищаются автоматически.
ssh-notify.sh в репозитории — единственный источник правды.
ssh-notify.nix и install-ssh-notify.sh носят встроенные копии, чтобы
каждый из них ставился одним файлом — .nix без соседнего .sh, установщик
без ничего, кроме себя самого для curl. После правки скрипта:
./tools/sync-embedded.sh
Обновление с версии до 2.1
До 2.1 управляющая команда называлась ssh-notify-ctl, а
/usr/local/bin/ssh-notify был рабочим скриптом. С 2.1 наоборот: короткое
имя ssh-notify — у команды управления, воркер переехал в
ssh-notify-worker. Проще всего обновиться так — из уже установленной
2.0:
sudo ssh-notify-ctl update
если update ещё нет (совсем старая версия) — обычным способом, сразу
следом за установщиком:
sudo curl -fsSL https://raw.githubusercontent.com/Mimic890/ssh-gotify-notify/main/install-ssh-notify.sh -o /usr/local/bin/ssh-notify && sudo chmod +x /usr/local/bin/ssh-notify && sudo ssh-notify install
Между скачиванием и install — окно в пару секунд, когда уведомления не
приходят (curl перетирает старый воркер новой командой управления, пока
install не пересоздаст воркер под новым именем и не перепишет обёртку).
На сам вход это не влияет: правило PAM optional. install подтянет
текущий /etc/ssh-notify.conf без переспроса и уберёт ssh-notify-ctl
как устаревший файл.
Ограничения
- Уведомления приходят только на успешные входы. Для неудачных попыток нужен fail2ban или CrowdSec с действием в Gotify — иначе торчащий в интернет 22-й порт зальёт телефон.
- Гео определяется только для публичных адресов. ipinfo.io без токена отдаёт 1000 запросов в месяц.
- Заведи для SSH отдельное приложение в Gotify, чтобы эти уведомления можно было отключить отдельно от остальных.
- Записи дописываются в лог в момент обработки события, а вход ещё ждёт геозапрос. У совсем короткой сессии выход может оказаться в файле выше входа — ориентируйся на штамп времени в записи, он снимается до всех сетевых запросов.
- Тексты уведомлений русские и зашиты в
ssh-notify.sh.