12 9 6 3
just a phper
简易的云服务器登录监控

前言

近几年,云服务器迅速普及起来,但是安全问题频发
虽然安全措施基本都做了, 但是很多时候问题来的触不及防
所以监控服务器的登录情况,尽早发现异常登录

初步想法

想到登录监控,要监控登录状态就想到从ssh入手,或者系统的last 或者 w

  • 从ssh入手

但如果从阿里云服务器网页版控制台登录服务器的话,是不走ssh的,
所以ssh不够完美处理登录监控问题

  • 用w命令
    系统自带的w命令是很容易获得在线用户
    但是只能获取到当前在线终端, 要通过对比获取到新增的登录就比较麻烦了

  • 用last命令

用last可以获取到登录历史,
如果要通过对比获取到新增的登录的话,
得保存每次提取的最新记录

然后就是通知方式了, 想到了一下几种方式:

  • 通过邮件发到邮箱,手机接收邮件
  • 通过邮件发送到手机号绑定的139邮箱,以短信的形式接收
  • 通过微信企业号,微信客户端接收

第一种方式效率很低,而且有点古老
第二种方式通过短信看邮件不大人性化,而且效率也不咋地
第三种方式,效率相对高,而且接收也方便

具体实现

思路:

  • 通过上一次上报的记录,获取新增的记录,并更新上报的最新记录
  • 通过登录IP获取地理位置
  • 通过企业号发到微信客户端

不多解析了, 直接上php脚本

<?php
//@version wzs 服务器登录警报
//记录最后登录信息 
$lastFile = "/data/shell/lastlogin.time";
//企业号配置
$corpID = 'wx253036fe**********';
$corpSecret = 'Fft9orOhBVaIzn2aM3C1OG3bH25*******************************************';

$host = 'https://qyapi.weixin.qq.com';
$getAccessUri = "/cgi-bin/gettoken";
$sendMessageUri = "/cgi-bin/message/send";

if(file_exists($lastFile)){
        $lastReport = @file_get_contents($lastFile);
}
$lastReport = rtrim($lastReport);
//最后一次上报
if ($lastReport){
    $cmd = "last | grep -B 10 '{$lastReport}' | grep -v '{$lastReport}'";
}else{
    $cmd = "last | head -n 10";
}
exec($cmd, $ret);
if (!empty($ret)){
    //更新最新的登录
    preg_match('/(.*?)(\d{2}:\d{2})/', $ret[0], $matches);
    if (isset($matches[0])){
        @file_put_contents($lastFile, $matches[0]);
    }
    //获取物理地址
    foreach ($ret as &$t){
        preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $t, $matches);
        if (isset($matches[0])){
            $cmd = "curl -s 'http://www.ip.cn/index.php?ip={$matches[0]}'";
            unset($address);
            exec($cmd, $address);
            if (isset($address[0])){
                preg_match('/来自:(\S*)/', $address[0], $matches);
                $address[0] = isset($matches[0]) ? $matches[0] : "";
                $t = $address[0] . "\n\t" . $t;
            }
        }
    }
    //发到微信企业号
    $getAccessUrl = $host . $getAccessUri . "?corpid={$corpID}&corpsecret={$corpSecret}";
    $accessToken = httpGet($getAccessUrl);
    $token = json_decode($accessToken, true);
    if (!isset($token['access_token'])){
        exit("accessToken got failed");
    }
    $sendMessageUrl = $host . $sendMessageUri . "?access_token={$token['access_token']}";
    $data = [
        'touser' => 'wzs',
        'toparty' => '1',
        'totag' => '1',
        'msgtype' => 'text',
        'agentid' => 1,
        'text' => [
            "content" => implode("\n\n", $ret)
        ],
        'safe' => 0
    ];
    echo httpPost($sendMessageUrl, json_encode($data, JSON_UNESCAPED_UNICODE));
    //print_r($accessToken);
}

function httpGet($url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);   //秒

    $result = curl_exec($ch);
    $error = curl_error($ch);

    curl_close($ch);

    return $error ? $error : $result;
}

function httpPost($url, $params) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);   //秒
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //不验证证书,下同
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //

    $result = curl_exec($ch);
    $error = curl_error($ch);

    curl_close($ch);

    return $error ? $error : $result;
}

然后在登录的时候执行即可:
在/etc/bashrc 最后加上 php /data/shell/safe.php > /dev/null
或者配置crontab即可:

 * * * * * php /data/shell/safe.php > /dev/null &
 * * * * * sleep 15; php /data/shell/safe.php > /dev/null &
 * * * * * sleep 30; php /data/shell/safe.php > /dev/null &
 * * * * * sleep 45; php /data/shell/safe.php > /dev/null &

总结

安全,安全,安全,重要的事情说三遍
虽然阿里云盾也有异常登录警报,如果出现异地登录,会有短信警报的
但是呢, 亲力亲为,丰衣足食

自由转载-非商用-非衍生-保持署名(创意共享3.0许可证