Introduction
Reference:
Features
Login notifications: When a user logs in via SSH, a notification is sent to Gotify containing:
- Login user
- IP address
- Hostname
- Geolocation
- Time
Exclusions: Connections from the local network and the server's own public IP are excluded.
Example notification:
User elvish logged in to Ubuntu-24.04-server from 114.51.14.11
Time: 2026-01-01 10:00:00 CST
🌍 Location: Shanghai, Shanghai, CN 🇨🇳
Notes
- Geolocation is looked up from the IP address using the ipinfo.io API, which is subject to rate limits.
- The script runs on every SSH login, including non-interactive logins such as SCP and SFTP.
Usage
1. Create the script:
sudo nano /usr/local/bin/ssh-login-notify.sh
Paste the following content:
#!/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"
}
# 检查是否为局域网、回环地址或服务器自身公网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
}
# 如果是局域网、回环地址或服务器自身公网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. Make the script executable:
sudo chmod +x /usr/local/bin/ssh-login-notify.sh
3. Install the dependencies (if they are not already installed):
sudo apt update && sudo apt install jq dnsutils curl -y
4. Configure PAM:
Edit the PAM configuration file for SSH:
sudo nano /etc/pam.d/sshd
Add the following line to the end of the file:
session optional pam_exec.so /usr/local/bin/ssh-login-notify.sh
5. Restart the SSH service:
sudo systemctl restart sshd
