はじめに
参考:
機能
ログイン通知:ユーザーがSSHでログインすると、Gotifyに以下の情報を含む通知を送信します
- ログインユーザー
- IP
- ホスト名
- 位置情報
- 時刻
除外対象:LAN内およびサーバー自身のグローバルIPからの接続は通知しません
通知例:
ユーザー elvish が 114.51.14.11 から Ubuntu-24.04-server にログインしました
時刻: 2026-01-01 10:00:00 CST
🌍 場所: Shanghai, Shanghai, CN 🇨🇳
補足
- 位置情報の取得にはipinfo.ioのAPIを使用し、IPアドレスから所在地を特定します。APIにはレート制限があります
- このスクリプトはSSHログインのたびに実行されます。SCPやSFTPなどの非対話型ログインも対象です
使い方
1. スクリプトを作成:
sudo nano /usr/local/bin/ssh-login-notify.sh
以下の内容を貼り付けます:
#!/bin/bash
USER_NAME=$PAM_USER
HOST_NAME=$(hostname)
TIME_NOW=$(TZ=Asia/Shanghai date "+%Y-%m-%d %H:%M:%S %Z")
GOTIFY_URL="http://127.0.0.1:8118" # <- ここのアドレスとtokenを変更
GOTIFY_TOKEN="<GOTIFY_TOKEN>"
# IPアドレスを取得 - SFTP接続時の表示問題を修正
if [ -n "$SSH_CONNECTION" ]; then
IP_ADDRESS=$(echo $SSH_CONNECTION | awk '{print $1}')
else
# whoコマンドにフォールバックし、無効な出力を除外
IP_ADDRESS=$(who | awk '{print $5}' | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -n 1)
fi
[ -z "$IP_ADDRESS" ] && IP_ADDRESS="unknown"
# サーバーのグローバルIPアドレスを取得
get_server_public_ip() {
# 複数の方法でグローバルIPの取得を試みる
local public_ip=""
# 方法1: digでDNSに問い合わせる
public_ip=$(dig +short myip.opendns.com @resolver1.opendns.com 2>/dev/null)
# 方法2: curlで外部サービスに問い合わせる
if [ -z "$public_ip" ]; then
public_ip=$(curl -s http://ifconfig.me 2>/dev/null)
fi
# 方法3: hostnameコマンドを使用
if [ -z "$public_ip" ]; then
public_ip=$(hostname -I | awk '{print $1}' 2>/dev/null)
fi
echo "$public_ip"
}
# LAN内、ループバックアドレス、またはサーバー自身のグローバルIPかどうかを確認
is_local_or_self_ip() {
local ip=$1
# ループバックアドレス
if [[ $ip == "127.0.0.1" || $ip == "::1" || $ip == "localhost" ]]; then
return 0
fi
# プライベートIPアドレスの範囲
if [[ $ip =~ ^10\. ]] || \
[[ $ip =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. ]] || \
[[ $ip =~ ^192\.168\. ]] || \
[[ $ip =~ ^169\.254\. ]]; then
return 0
fi
# サーバーのグローバルIPを取得
local server_public_ip=$(get_server_public_ip)
# サーバー自身のグローバルIPかどうかを確認
if [[ "$ip" == "$server_public_ip" ]]; then
return 0
fi
# ローカルIPでもサーバー自身のグローバルIPでもない
return 1
}
# LAN内、ループバックアドレス、またはサーバー自身のグローバルIPならそのまま終了
if is_local_or_self_ip "$IP_ADDRESS"; then
exit 0
fi
# セッション終了時は終了
if [[ $PAM_TYPE == 'close_session' ]]; then
exit 0
fi
# 位置情報を取得(任意)
GEO_INFO=""
if [[ "$IP_ADDRESS" != "unknown" && "$IP_ADDRESS" != "127.0.0.1" && "$IP_ADDRESS" != ::1 ]]; then
GEO_JSON=$(curl -s "https://ipinfo.io/$IP_ADDRESS/json")
CITY=$(echo "$GEO_JSON" | jq -r '.city')
REGION=$(echo "$GEO_JSON" | jq -r '.region')
COUNTRY=$(echo "$GEO_JSON" | jq -r '.country')
ORG=$(echo "$GEO_JSON" | jq -r '.org')
# 国コードを国旗の絵文字に変換する関数
flag() {
local code="${1^^}"
local first=$(( $(printf "%d" "'${code:0:1}") - 65 + 0x1F1E6 ))
local second=$(( $(printf "%d" "'${code:1:1}") - 65 + 0x1F1E6 ))
printf '%b' "$(printf '\\U%08X\\U%08X' "$first" "$second")"
}
# Unicodeエンコードの絵文字を使用
EARTH_ICON=$(printf '\U1F30D')
GEO_INFO="$EARTH_ICON 場所: $CITY, $REGION, $COUNTRY $(flag $COUNTRY)"
fi
# メッセージ内容を作成
TITLE="SSHログイン通知 - $HOST_NAME"
MESSAGE="ユーザー $USER_NAME が $IP_ADDRESS から $HOST_NAME にログインしました
時刻: $TIME_NOW
$GEO_INFO"
# Gotifyに送信
if [ -n "$SSH_CONNECTION" ] || [ -n "$SFTP_CONNECTION" ]; then
curl -s -o /dev/null \
-X POST \
-F "title=${TITLE}" \
-F "message=${MESSAGE}" \
-F "priority=5" \
"${GOTIFY_URL}/message?token=${GOTIFY_TOKEN}"
fi
2. 実行権限を付与:
sudo chmod +x /usr/local/bin/ssh-login-notify.sh
3. 依存パッケージをインストール(未インストールの場合):
sudo apt update && sudo apt install jq dnsutils curl -y
4. PAMを設定:
SSHのPAM設定ファイルを編集します:
sudo nano /etc/pam.d/sshd
ファイルの末尾に以下を追加します:
session optional pam_exec.so /usr/local/bin/ssh-login-notify.sh
5. SSHサービスを再起動:
sudo systemctl restart sshd
