Table des matières
Bonjour à tous,
J’ai mis en place ce mécanisme il y a déjà pas mal de temps, je le partage ici si ça peut servir à d’autres, ou encore mieux vous donner des idées.
Edit 21 février 2024
Ajout de la section en utilisant l’API PAM.
Objectif
L’objectif est plutôt simple, ça permet de rajouter une couche de sécurité sur mes différents serveurs. En effet, le seul moyen de s’y connecter est de passer par une connexion SSH. Je cherchais une application mobile pour faire du SSH en cas d’extrême urgence, mais si j’étais tombé sur une application malveillante, une attaque man-in-the-middle aurait été possible.
L’idée à travers ce tutoriel est de pouvoir recevoir une alerte à chaque connexion SSH. De ce fait, je serai rapidement au courant si ma clé privée (et son mot de passe) serait tombée entre de mauvaises mains.
Encore une fois, c’est un petit plus sympa, mais ce n’est pas un mécanisme de sécurité fiable, il ne faut pas s’y reposer et penser qu’aucune intrusion ne sera désormais possible sans que vous n’y soyez au courant.
Types d’alertes
Là, le champ des possibles est infini. Globalement, on va pouvoir recevoir une alerte avec tous les systèmes possédant une API HTTP, un client Linux, etc …
Ici, on va montrer comment faire avec Telegram et ntfy.
Telegram
Envoyer un message sur Telegram est vraiment très simple, il suffit d’une requête curl et le tour est joué.
Avant cela, il faut tout de même créer un bot et récupérer son token.
Il existera ensuite 2 types de conversations:
- une conversation en tête à tête avec votre bot
- un groupe Telegram (1 ou plusieurs bots et 1 ou plusieurs “vraies personnes”)
Je ne vais pas réécrire une procédure que l’on peut trouver partout sur le net. Je peux tout de même vous diriger vers une procédure écrite par mes soins.
Ensuite, on peut faire un petit script bash assez simple pour faire ce fameux curl:
#!/bin/bash
if [ "$#" -ne 3 ]; then
echo "Usage: $0 <bot_token> <group_id> <text message>"
exit 1
fi
BOT_TOKEN=$1
GROUP_ID=$2
TEXT=$3
URL="https://api.telegram.org/bot${BOT_TOKEN}/sendMessage"
RES=$(curl -i -s -X POST "${URL}" --data "text=${TEXT}" --data "chat_id=${GROUP_ID}")
STATUS_CODE=$(echo "$RES" | head -n 1 | awk -F' ' '{print $2}')
if [[ $STATUS_CODE -ne 200 ]] ; then
echo "error while sending message"
echo "${RES}"
exit 1
fi
Je vous laisse placer ce script à l’endroit de votre choix, on pourrait par exemple le placer dans /usr/bin
ou tout autre dossier de votre $PATH
.
Il ne faut ensuite pas oublier de lui donner les droits d’exécution avec chmod +x <script-path>
.
Faites attention lorsque vous lancez ce script pour le tester dans votre terminal, je vous conseille de rajouter un espace devant la commande comme le conseille le man de bash. Cela vous permettra de ne pas enregistrer la commande dans votre historique et de ne pas y dévoiler le token.
Ntfy
Au début, j’avais prévu de faire l’article en ne parlant que de Telegram. J’avais également prévu de mettre un petit encadré pour dire “faites attention, par design, Telegram aura accès à toutes vos alertes, blabla”.
Et puis je me suis souvenu de ntfy que je n’avais jamais pris le temps de tester. Pour faire simple, c’est un service de notification/alerte en publish-subscribe Open Source et qu’il est possible d’auto-héberger. Le slogan est Push notifications made easy et c’est vraiment le cas. Je vous invite à faire un tour sur leur site et la documentation associée, ça marche bien, vite et simplement.
Voilà un script équivalent à celui pour Telegram:
#!/bin/bash
set -eu
if [ "$#" -ne 4 ]; then
echo "Usage: $0 <ntfy_url> <basic_auth> <topic> <text message>"
echo "Help basic_auth: \"echo -n 'testuser:fakepassword' | base64\""
exit 1
fi
NTFY_URL=$1
BASIC_AUTH=$2
TOPIC=$3
TEXT=$4
RES=$(curl -i -s -X POST -H "Authorization: Basic ${BASIC_AUTH}" -d "${TEXT}" "${NTFY_URL}/${TOPIC}")
STATUS_CODE=$(echo "$RES" | head -n 1 | awk -F' ' '{print $2}')
if [[ $STATUS_CODE -ne 200 ]] ; then
echo "error while sending alert"
echo "${RES}"
exit 1
fi
On pourrait également utiliser la CLI pour quelque-chose de similaire (voir ici).
L’avantage en auto-hébergement est que vous restez maitre de vos données. L’inconvénient vient des cas d’usages. Par exemple, si vous avez:
- un serveur A avec quelques services dont ntfy
- un serveur B avec un service de monitoring comme uptime-kuma qui est censé envoyer un message quand un service est tombé sur le serveur A, si le service en question est ntfy, c’est ballot (idem si c’est tout le serveur A qui tombe).
Envoyer l’alerte à la connexion SSH
Avec profile.d
L’astuce va être de profiter de /etc/profile
qui va exécuter des scripts à chaque connexion (SSH ou pas) pour chaque utilisateur de la machine.
Il suffit alors de créer un fichier (disons login-notify.sh
) dans le dossier /etc/profile.d/
avec ce type de contenu (à adapter selon vos besoins/envies):
#!/bin/bash
BOT_TOKEN="to-replace"
GROUP_ID="to-replace"
IP="$(echo "${SSH_CONNECTION}" | awk -F' ' '{print $1}')" # does not work for local connection
DATE="$(date)"
NAME="$(whoami)"
NB_USERS=$(who | wc -l)
MESSAGE="""
New login to ${HOSTNAME} server!
\"${NAME}\" from \"${IP}\"
${NB_USERS} users connected
${DATE}
"""
/path/to/send-telegram.sh "${BOT_TOKEN}" "${GROUP_ID}" "${MESSAGE}"
Vous pouvez ne mettre les droits de lecture à ce fichier qu’à l’utilisateur root
si vous y mettez votre BOT_TOKEN
par exemple.
Là, j’utilise le script pour Telegram, mais on pourrait utiliser celui pour ntfy (avec pourquoi pas du retry sur Telegram en cas d’échec).
On pourrait également n’envoyer une notification que via le ~/.bash_profile
pour rester uniquement à notre utilisateur.
Avec l’API PAM
L’astuce avec profile.d
marche bien dans le cas de connexions ssh classiques.
Mais si je fais ce type de commande ssh user@mymachine uptime
, aucune notification ne sera envoyée parce qu’au environnement ne sera créé.
On peut alors dans ce cas plutôt se tourner vers l’API PAM dont voici sa description selon Wikipédia:
Pluggable Authentication Modules est une API permettant d’offrir aux programmes des services d’authentification, d’autorisation et de contrôle d’ouverture de sessions.
Bref, entrons dans le vif du sujet.
Dans votre fichier /etc/ssh/sshd_config
, assurez-vous de mettre la valeur UsePAM
à yes
.
Personnellement, j’ai également ChallengeResponseAuthentication
et PasswordAuthentication
à no
: dans ce cas, on utilise pas PAM pour l’authentification (je continue à utiliser uniquement l’authentification par clés).
On peut ensuite commencer:
- on crée un dossier:
mkdir -p /etc/pam.scripts
- on y place le même contenu que le fichier
login-notify.sh
de la section précédente - on ajoute les droits exécutables sur notre fichier:
chmod +x /etc/pam.scripts/login-notify.sh
- à la fin de
/etc/pam.d/sshd
, on rajoutesession optional pam_exec.so /etc/pam.scripts/login-notify.sh
Le mot clé optional
est important, parce que si votre message Telegram ou votre notification ntfy retourne une erreur, alors la connexion SSH sera bloquée.
Or si c’est le même serveur qui gère votre serveur ntfy et qu’il est tombé, vous ne pourrez plus vous y connecter.
On reste donc sur du “best-effort”, ce n’est donc pas une sécurité absolue mais un petit plus (comme toujours en sécurité).
Aussi, il est possible d’utiliser if [ ${PAM_TYPE} = "open_session" ]; then
autour de l’envoi du message (sans oublier le fi
à la fin) pour notifier uniquement en cas d’ouverture de session, sinon il y aura aussi les fermetures.
Conclusion
Voilà qui clôt ce tutoriel. Je ne vous invite pas forcément à copier-coller les scripts mais plutôt à les adapter à vos besoins. Le but était surtout de donner des idées: on peut en effet exécuter des scripts à chaque login (et donc par extension faire ce que l’on veut).
Dans le même esprit, si vous avez d’autres idées dans le même genre, n’hésitez pas à les partager dans les commentaires ci-dessous.