🌊

UbuntuへのSSHログインをDiscordに通知してみる

2023/02/12に公開

概要

Linux PAM (Pluggable Authentication Modules)を使い、SSHログイン時にDiscordへログイン通知を送る方法を記載しています。
PAMはLinuxでのユーザーの認証処理をカスタマイズできるもので、例えば、Google Authenticator PAM moduleを使うとログイン時の二要素認証が実現できたりします(SSHやVSCodeのリモート接続は頻繁に使うので、普段使いの環境では二要素認証はそう使わないでしょうが)。また、指定の実行ファイルをキックさせることも可能です。
不正ログインを検知する仕組み作りに役立つかもしれません。知らんけど。メモとして残していますので、ご参考までにお願いします。

SSHログインをDiscordに通知

環境

  • Ubuntu Server 22.04.1
  • Python 3.11.1

実施した作業

  1. Discordで通知を受け取るチャンネルからWebhookを作成
  2. Pythonスクリプト、シェルスクリプトを作成
  3. PAM設定を修正しスクリプトを実行させるようにする(/etc/pam.d/sshd)

DiscordでのWebhook作成方法は以下を参考にしました。

PAMが何かについては、以下を参考に(ググれば情報はかなり出てきますが)。

コード

スクリプトは2つ作成しています。
・WebhookにPOSTリクエストを投げるPythonスクリプト(本体)
・Pythonを実行するシェルスクリプト

Pythonスクリプトではrequestsとnetifaces(NICのIPアドレスを取得するライブラリ)を使用していますので、事前に仮想環境を作成するなりして、pip installしておきます。

Pythonスクリプト

bashのシェルスクリプトでも作成できるかもしれませんが、今回はPythonで作成しました。
contentオブジェクト中のcontentが通知メッセージの本文、usernameが表示される名前です。
os.getenvは環境変数を取得しますが、pam_execのマニュアルによるとPAM_RHOSTで接続元ホストを取得できるとあります。

notify_discord.py
import requests
import socket
import os
import datetime
import netifaces as ni


webhook_url = "[WebhookのURL]"
header = "Content-Type: application/json"


def getIPaddress(nface: str) -> str:
    """
    Get IPv4 Address of NIC.

    Args:
        nface str: NIC Name

    Returns:
        IP Address v4
    """

    return ni.ifaddresses(nface)[ni.AF_INET][0]["addr"]


def main():

    bot_usernm = f'{socket.gethostname()} Login notification'
    login_dt = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')
    content = {
        'username': bot_usernm,
        'content': f'{os.getlogin()} logged in at {login_dt} from {os.getenv("PAM_RHOST")}\n'\
                   f'({getIPaddress("en01")}, {getIPaddress("ts01")})'
    }

    res = requests.post(webhook_url, data=content)


if __name__ == "__main__":
    main()

上記スクリプトを呼び出すbashシェルスクリプトは以下。なお、実行権限をつけておきます。

notify_discord.sh
#!/bin/bash

cd /home/yukim/notify_discord
source .env/bin/activate
python notify_discord.py

/etc/pam.d/sshdを修正

/etc/pam.d/sshdの下部に以下を追加。

/etc/pam.d/sshd
session optional pam_exec.so type=open_session /home/yukim/notify_discord/notify_discord.sh 

オプションの解説は上で挙げたマニュアルに記載されていますが、type=open_sessionを付けることでログイン時にのみスクリプトを実行させることができます。これがないとログアウト時にも実行されてしまいます。

通知を確認する

試しにSSHで接続してみます。

以下のような通知が来ました。

PAMだとSSHのログインだけでなく物理端末からのログイン操作に対しても処理ができるので、調べてみる余地がありそうです。

参考

Discussion