惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Blog of Author Tim Ferriss
S
Securelist
D
Docker
The Register - Security
The Register - Security
GbyAI
GbyAI
Recorded Future
Recorded Future
Engineering at Meta
Engineering at Meta
Stack Overflow Blog
Stack Overflow Blog
云风的 BLOG
云风的 BLOG
P
Proofpoint News Feed
罗磊的独立博客
博客园 - 【当耐特】
F
Full Disclosure
WordPress大学
WordPress大学
腾讯CDC
小众软件
小众软件
大猫的无限游戏
大猫的无限游戏
D
DataBreaches.Net
SecWiki News
SecWiki News
L
Lohrmann on Cybersecurity
I
InfoQ
MyScale Blog
MyScale Blog
量子位
Cyberwarzone
Cyberwarzone
博客园 - 三生石上(FineUI控件)
The Hacker News
The Hacker News
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Jina AI
Jina AI
博客园_首页
H
Help Net Security
K
Kaspersky official blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Webroot Blog
Webroot Blog
Blog — PlanetScale
Blog — PlanetScale
V
Vulnerabilities – Threatpost
Y
Y Combinator Blog
The Cloudflare Blog
P
Proofpoint News Feed
V
Visual Studio Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Tailwind CSS Blog
爱范儿
爱范儿
P
Privacy International News Feed
Security Archives - TechRepublic
Security Archives - TechRepublic
The GitHub Blog
The GitHub Blog
C
Cybersecurity and Infrastructure Security Agency CISA
B
Blog RSS Feed

博客园 - Nihaorz

告别闪烁,拥抱流畅:在 Windows Terminal 中完美配置 Cygwin 环境 Nginx 透明代理 + 自动回源存储:访问即缓存,落盘即文件 创建 docker ipvlan,让 docke 容器获取独立ip 电犀牛 R68s iStoreOS 2.5G 网口速率优化 解决 openwrt ssh 命令行终端 home、end 键不可用问题 一键添加视频封面脚本 ffmpeg 转码参数 docker save 远程 ssh 主机直接 load,不产生本地文件 AutoHotKey 脚本 - win10 自动连接无线显示器 OpenClaw 安装部署,配置 deepseek curl 断点续传下载 debian iso 镜像下载地址 linux 安装 zerotier,加入网络 基于 Fail2ban 的 SSH 入侵自动反制方案 ssh 配置密钥登录,关闭密码登录 memc - 基于 shell 的交互式清理内存脚本 基于 Fail2ban 的 OpenWRT SSH 入侵自动反制方案 Linux Screen 命令速查 使用 ofelia 在 docker 容器中执行计划任务 linux 磁盘挂载示例
SSH 登录/退出实时监控脚本
Nihaorz · 2026-03-06 · via 博客园 - Nihaorz

ssh-monitor.sh

#!/bin/bash

SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
# 配置文件路径
CONFIG_FILE="${SCRIPT_DIR}/ssh-login-sessions.conf"
# 日志文件路径
LOG_FILE="/var/log/ssh-monitor.log"

# ==================== 微信推送配置 ====================
WXPUSHER_APP_TOKEN="xxxxxx"
WXPUSHER_UID="xxxxxx"

# 确保配置文件和日志文件存在
touch "$CONFIG_FILE" "$LOG_FILE"

# 微信推送函数
send_wx_message() {
    local summary="$1"
    local content="$2"
    
    curl -s -H 'Content-Type: application/json' \
         -XPOST https://wxpusher.zjiecode.com/api/send/message \
         -d "{
    \"appToken\":\"${WXPUSHER_APP_TOKEN}\",
    \"content\":\"$content\",
    \"summary\":\"$summary\",
    \"contentType\":3,
    \"uids\":[\"${WXPUSHER_UID}\"]
}" > /dev/null 2>&1
}

# 获取sessionid的函数
get_sessionid() {
    local pid=$1
    if [ -f "/proc/$pid/sessionid" ]; then
        cat "/proc/$pid/sessionid" 2>/dev/null || echo "unknown"
    else
        echo "unknown"
    fi
}

# 解析登录信息的函数
parse_login_info() {
    local json_line="$1"
    local pid=$(echo "$json_line" | jq -r '._PID')
    local message=$(echo "$json_line" | jq -r '.MESSAGE')
    
    # 从MESSAGE中提取用户名和IP
    if [[ "$message" == Accepted* ]]; then
        # Accepted publickey for root from 192.168.100.53 port 11105 ssh2: ...
        local username=$(echo "$message" | grep -oP 'for \K[^ ]+')
        local ip=$(echo "$message" | grep -oP 'from \K[^ ]+')
        
        # 提取登录方式 (publickey / password / keyboard-interactive 等)
        local auth_method=$(echo "$message" | grep -oP 'Accepted \K[^ ]+')
        
        # 获取当前时间
        local login_time=$(date '+%Y-%m-%d %H:%M:%S')
        
        # 获取sessionid
        local sessionid=$(get_sessionid "$pid")
        
        # 返回JSON格式的登录信息
        echo "{\"pid\":\"$pid\",\"username\":\"$username\",\"ip\":\"$ip\",\"login_time\":\"$login_time\",\"sessionid\":\"$sessionid\",\"auth_method\":\"$auth_method\"}"
    fi
}

# 解析退出信息的函数
parse_logout_info() {
    local json_line="$1"
    local pid=$(echo "$json_line" | jq -r '._PID')
    local message=$(echo "$json_line" | jq -r '.MESSAGE')
    
    if [[ "$message" == "Disconnected from user"* ]]; then
        # Disconnected from user root 192.168.100.53 port 11105
        local username=$(echo "$message" | grep -oP 'user \K[^ ]+')
        local ip=$(echo "$message" | grep -oP '\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')
        
        echo "{\"pid\":\"$pid\",\"username\":\"$username\",\"ip\":\"$ip\"}"
    fi
}

# 记录登录事件
handle_login() {
    local login_info="$1"
    local pid=$(echo "$login_info" | jq -r '.pid')
    local username=$(echo "$login_info" | jq -r '.username')
    local ip=$(echo "$login_info" | jq -r '.ip')
    local login_time=$(echo "$login_info" | jq -r '.login_time')
    local sessionid=$(echo "$login_info" | jq -r '.sessionid')
    local auth_method=$(echo "$login_info" | jq -r '.auth_method')
    echo "login_info: $login_info"
    # 写入配置文件
    echo "$pid|$login_info" >> "$CONFIG_FILE"
    
    # 写入日志文件
    echo "[LOGIN ] $login_time - User: $username, IP: $ip, PID: $pid, SessionID: $sessionid, AuthMethod: $auth_method" >> "$LOG_FILE"
    
    # 发送微信通知
    local summary="✅ SSH登录通知 - $ip"
    local content=""
    content+="| 字段 | 信息 |\n"
    content+="|------|------|\n"
    content+="| 用户名 | **$username** |\n"
    content+="| IP地址 | \`$ip\` |\n"
    content+="| 登录时间 | $login_time |\n"
    content+="| PID | \`$pid\` |\n"
    content+="| SessionID | \`$sessionid\` |\n"
    content+="| 认证方式 | \`$auth_method\` |\n"
    
    send_wx_message "$summary" "$content"
    
    echo "✅ 登录事件已记录 - PID: $pid, User: $username"
}

# 处理退出事件
handle_logout() {
    local logout_info="$1"
    local pid=$(echo "$logout_info" | jq -r '.pid')
    local username=$(echo "$logout_info" | jq -r '.username')
    local ip=$(echo "$logout_info" | jq -r '.ip')
    
    # 从配置文件中查找对应的登录记录
    local login_line=$(grep "^$pid|" "$CONFIG_FILE" | tail -1)
    
    if [ -n "$login_line" ]; then
        # 提取登录信息
        local login_info=$(echo "$login_line" | cut -d'|' -f2-)
        local login_time=$(echo "$login_info" | jq -r '.login_time')
        local sessionid=$(echo "$login_info" | jq -r '.sessionid')
        
        # 计算连接时间
        local logout_time=$(date '+%Y-%m-%d %H:%M:%S')
        local login_epoch=$(date -d "$login_time" +%s)
        local logout_epoch=$(date -d "$logout_time" +%s)
        local duration=$((logout_epoch - login_epoch))
        local duration_str=""
        
        if [ $duration -ge 3600 ]; then
            duration_str="$((duration/3600))小时$(((duration%3600)/60))分钟"
        elif [ $duration -ge 60 ]; then
            duration_str="$((duration/60))分钟$((duration%60))秒"
        else
            duration_str="${duration}秒"
        fi
        
        # 写入日志文件
        echo "[LOGOUT] $logout_time - User: $username, IP: $ip, PID: $pid, Login: $login_time, Duration: $duration_str" >> "$LOG_FILE"
        
        # 从配置文件中删除该记录
        sed -i "/^$pid|/d" "$CONFIG_FILE"
        
        # 发送微信通知
        local summary="❌ SSH退出通知 - $ip"
        local content=""
        content+="| 字段 | 信息 |\n"
        content+="|------|------|\n"
        content+="| 用户名 | **$username** |\n"
        content+="| IP地址 | \`$ip\` |\n"
        content+="| 登录时间 | $login_time |\n"
        content+="| 退出时间 | $logout_time |\n"
        content+="| 连接时长 | **$duration_str** |\n"
        content+="| PID | \`$pid\` |\n"
        content+="| SessionID | \`$sessionid\` |\n"
        
        send_wx_message "$summary" "$content"
        
        echo "❌ 退出事件已记录 - PID: $pid, User: $username, Duration: $duration_str"
    else
        # 如果没有找到登录记录,只记录退出信息
        echo "[LOGOUT] $(date '+%Y-%m-%d %H:%M:%S') - User: $username, IP: $ip, PID: $pid (no login record found)" >> "$LOG_FILE"
        echo "❌ 退出事件 - PID: $pid (未找到登录记录)"
    fi
}

# 主循环
echo "开始监控SSH登录/退出事件..."
echo "配置文件: $CONFIG_FILE"
echo "日志文件: $LOG_FILE"

journalctl _COMM=sshd -n 0 -f -o json | while read -r line; do
    message=$(echo "$line" | jq -r '.MESSAGE')
    
    # 处理登录事件
    if [[ "$message" == Accepted* ]]; then
        login_info=$(parse_login_info "$line")
        if [ -n "$login_info" ]; then
            handle_login "$login_info"
        fi
    
    # 处理退出事件
    elif [[ "$message" == "Disconnected from user"* ]]; then
        logout_info=$(parse_logout_info "$line")
        if [ -n "$logout_info" ]; then
            handle_logout "$logout_info"
        fi
    fi
done

这是一个 SSH 登录/退出实时监控脚本,主要功能如下:

核心功能

表格

功能说明
实时监控 通过 journalctl 实时监听 sshd 服务的日志事件
登录检测 捕获 SSH 成功登录事件(Accepted 关键字)
退出检测 捕获 SSH 断开连接事件(Disconnected from user
会话追踪 通过 PID 和 SessionID 匹配登录和退出事件,计算连接时长
微信推送 使用 WxPusher 服务发送登录/退出通知到微信
日志记录 将事件记录到 /var/log/ssh-monitor.log

工作流程

  1. 登录时:提取用户名、IP、PID、SessionID → 写入配置文件 → 发送微信通知

  2. 退出时:通过 PID 查找对应登录记录 → 计算连接时长 → 发送微信通知(含时长统计)→ 清理配置记录

关键文件

  • ssh-login-sessions.conf - 存储活跃会话信息(PID 与登录详情映射)

  • /var/log/ssh-monitor.log - 持久化日志记录

使用场景

适合需要 实时掌握服务器 SSH 访问情况 的场景,如:

  • 云服务器安全监控

  • 运维审计追踪

  • 异常登录告警