LINUX.ORG.RU
решено ФорумAdmin

Уведомление о подключении по sftp/scp

 , , ,


2

2

Господа, всем доброго настроения.
Не пойму как решить задачу. Гугление, к сожалению, не поспособствовало, за сим прошу помощи у коллективного сознательного.

Имеется скрипт-уведомлялка, который говорит Telegram-боту отправить сообщение определённому абоненту при авторизации на сервере по SSH.
Лежит скрипт по пути /etc/profile.d/script.sh, выглядит следующим образом:

#!/bin/bash
BOT_TOKEN=token
CHAT_ID=id
USER_IP=$(echo $SSH_CLIENT | awk '{ print $1}')
BOT_MESSAGE="🔐 $(hostname): $(whoami) is authorized from $USER_IP"
curl -X POST -d "text=$BOT_MESSAGE" 'https://api.telegram.org/bot'$BOT_TOKEN'/sendMessage?chat_id='$CHAT_ID'' &>/dev/null

Проблема заключается в том, что работает он только при подключении по, непосредственно, интерактивному SSH. Если подключиться через условный WinSCP - уведомления не будет, как и если просто скопировать файл на сервер через scp.

Интуиция подсказывает, что заковыка связана с понятиями «интерактивности/неинтерактивности» устанавливаемой сессии, и, соответственно, в местоположении скрипта, но вот знаний, чтобы понять, как описанную проблему исправить – не хватает.

Подскажите, пожалуйста, что именно нужно сделать, чтобы получать желаемое уведомление при любого типа подключении на условный «22 порт», вне зависимости от того, было ли это SSH или SFTP?

Подскажите, пожалуйста, что именно нужно сделать, чтобы получать желаемое уведомление при любого типа подключении на условный «22 порт», вне зависимости от того, было ли это SSH или SFTP?

парси журнал

anonymous
()
Ответ на: комментарий от Dimez

Кажется получилось.
Использовал предложенный вами вариант, для истории – опишу полный порядок реализации (вдруг кто-то на эту тему в будущем наткнётся).

Расположил скрипт-уведомлялку по пути /bin/script.sh, установил владельцем root:root, права назначил по обратной маске 644 и дал права на запуск, получилось вот так:

-rwxr-xr-x 1 root root 325 Feb 11 12:42 /bin/script.sh*

Исходный код скрипта изменил, заменив $SSH_CLIENT на $PAM_RHOST, а $(whoami) на $PAM_USER.
Получилось так:

#!/bin/bash
BOT_TOKEN=token
CHAT_ID=id
USER_IP=$(echo $PAM_RHOST | awk '{ print $1}')
BOT_MESSAGE="🔐 $(hostname): $PAM_USER is authorized from $USER_IP"
curl -X POST -d "text=$BOT_MESSAGE" 'https://api.telegram.org/bot'$BOT_TOKEN'/sendMessage?chat_id='$CHAT_ID'' &>/dev/null

Также добавил в конец файла /etc/pam.d/sshd следующую строку:

session optional pam_exec.so type=open_session seteuid /bin/script.sh

Далее, установил yes в качестве значения директивы UsePAM в файле /etc/ssh/sshd_config, вместо имевшегося там по умолчанию no.

При этом в директивах KbdInteractiveAuthentication и PasswordAuthentication оставлено значение no, в соответствии с дающейся в стандартном конфигурационном файле рекомендацией и ввиду того, что авторизация на сервере разрешается только не из под root и только по ключам.

Делалось всё на Debian 12 с последними патчами.

takamushi
() автор топика
Ответ на: комментарий от takamushi

идея ставить скрипт, который синхронно лезет в интернет, на каждое ssh подключение имеет очевидные минусы:

  1. подвисание ssh подключения, если нет связи с api.telegram.org. В худшем случае сетевое соединение может зависнуть на неопределенное время. Как минимум следует обернуть curl в timeout

  2. отсутствие уведомления при недоступности api.telegram.org. Как я понимаю, в данной задаче это не критично, но вообще можно было создавать очередь сообщений и по крону засылать данные из нее на api.telegram.org (в чём-то схоже с вариантом парсинга логов).

MirandaUser2
()
Ответ на: комментарий от MirandaUser2

Благодарю за оказанное внимание :)

По комментариям – немного доработал скрипт, обработав кейсы вроде «не достучался до api.telegram.org», или «достучался, но код ответа отличается от 200».

Также теперь ограничены предельное время на установление подключения к api.telegram.org и предельное время ожидания ответа (за подсказку синтаксиса благодарю @Dimez).

Получилось следующее:

#!/bin/bash
BOT_TOKEN=token
CHAT_ID=id
USER_IP=$(echo $PAM_RHOST | awk '{ print $1}')
BOT_MESSAGE="🔐 $(hostname): $PAM_USER is authorized from $USER_IP"

http_response=$(curl -s -X GET -d "text=$BOT_MESSAGE" -o /dev/null -w '%{response_code}' \
 'https://api.telegram.org/bot'$BOT_TOKEN'/sendMessage?chat_id='$CHAT_ID'' \
 --connect-timeout 3 --max-time 10)

if [ $http_response != "200" ]; then
    echo "Telegram login notification wasn't sent! ("$PAM_USER" is authorized from "$USER_IP")" | sudo systemd-cat -t telegram-notify -p "crit"
else
    echo 'Telegram notification was sent ('$PAM_USER' is authorized from '$USER_IP')' | sudo systemd-cat -t telegram-notify -p "info"
fi

Также добавил логгирование успешных и неуспешных попыток отправки сообщения и настроил простенький motd, который показывает при логине последнюю из неуспешных попыток отправки уведомления за прошедшие 7 дней, если таковая найдётся.
Положил его по пути /etc/update-motd.d/20-telegram-notify-errors, содержимое такое:

#!/bin/bash

echo -e "\033[0;31m"
sudo journalctl -t 'telegram-notify' --since '7 day ago' -p crit -n 1 -r
echo -e "\033[0m"

Его ещё есть где доработать, потому как сейчас при отсутствии неуспешных попыток отправки он всё-равно вписывает в общий motd ярко красное -- No entries -- :)

А, и если уведомлять о неуспешных попытках отправки сообщения в motd, то в /etc/pam.d/sshd строка session optional pam_exec.so type=open_session seteuid /bin/script.sh должна располагаться выше, чем происходит вызов motd, чтобы в него попало событие о неуспешной отправке сообщения и при текущей попытке авторизации.

takamushi
() автор топика
Ответ на: комментарий от takamushi

UPD: поправил motd-файлик, теперь ругается только если что-то нашлось:

#!/bin/bash

NOTIFICATION_STATUS=$(sudo journalctl -t 'telegram-notify' --since '7 day ago' -p crit -n 1 -r)

if [ "$NOTIFICATION_STATUS" != "-- No entries --" ]; then
    echo -e "\033[0;31m"$NOTIFICATION_STATUS"\033[0m"
fi
takamushi
() автор топика
Ответ на: комментарий от Dimez
IP_INFOS="$(curl --max-time $MAX_TIME --connect-timeout $TIMEOUT https://ipinfo.io/$PAM_RHOST -s | jq -r '.org + " - " + .city + ", " + .region + ", " + .country')" #Client IP info parsing via jq

У меня ещё такое делается кое-где. Удобно.

Dimez ★★★★★
()
Последнее исправление: Dimez (всего исправлений: 1)
Ответ на: комментарий от anc

Но меня не совсем это интересовало, насчет точности данных не сравнивали?

Нет, не сравнивал. Если надо максимальной скорости (даже, скорее, сетевой предсказуемости) и точности, то надо geolite2 базу локально качать и использовать.

Dimez ★★★★★
()

Спасибо за поднятую тему, было очень интересно почитать. Кое-что почерпнул для себя. Я как раз только недавно сподобился сделать уведомления себе в телегу и накостылить бот на баше.

yaba ★★★
()