SSHサーバの危機を知る
「Private Access Serverを作る」で書いた、外出先からアクセスするためのSSHサーバについて、例えば思い当たらないアクセスの痕跡とか、ログインがあったことを感知するための仕掛けを作った。
アクセスサーバはグローバル空間に晒され、外部からの進入される可能性がある。アクセスサーバへは暗号キーがないとログインできないし、ログインしたとしても専用の非特権ユーザでしかログインできないため、更に他のユーザのパスワードを知らない限りは大したことはできない。また重要なファイルは一切置かないが、それでも外部からの進入の形跡は出来る限り早く知っておきたい。
そこで、過去一定時間の間にあった次のイベントを調べ、イベント(セキュリティ分野では「インシデント」という呼び方が一般的だろうか)が発生していた場合はメールで知らせるプログラムを作った。
- SSHのコネクションがあり、ログインせずにクローズした。
- ログインに失敗した。
- ログインがあった。
これらのイベントはメールで管理する。
セキュリティの危機を調べるshell script
上のようなイベント(インシデント)が、過去の一定時間の間に発生していたらメールで知らせるシェル・スクリプト、chkloginを載せておく。Fedora 8で動作確認をしているが、他のLinuxでも使えるかと思う。取り急ぎ作成したので、引数の処理などの汎用性は考慮してない。
1 #! /bin/sh 2 3 # chklogin Ver 0.0000000001 (2008/04/01) 4 # Copyright (C) 2008 Adsaria 5 6 # This program is free software; you can redistribute it and/or 7 # modify it. This program is distributed in the hope 8 # that it will be useful, but WITHOUT ANY WARRANTY. 9 10 DEBUG=0 : Debug flag, 0 is no debug. 11 12 CMD=`basename $0` 13 14 #################### Environment dependent variables 15 MAIL_TO=xxxxx@xxxxx.jp 16 MAIL_SUBJECT="Security Warning from `hostname` by $CMD" 17 ZIP_PASSWORD="xxxxx" 18 #################### 19 20 NOW=`date +%s` : Current time in seconds since 1970-01-01 21 SPAN=3660 : Time span in seconds 22 PAST=`expr \`date +%s \` - $SPAN` : Spaned past tiem in seconds since 1970-01-01 23 SEND_MAIL="no" : Flag for sending mail or not 24 MARK="$CMD.$NOW.MARK" : Marking string definition 25 26 # Temporary files 27 MESSAGE_FILE=/tmp/${CMD}_${NOW}_$$_massagel.txt ; rm -f $MESSAGE_FILE 28 MESSAGE_ZIP_FILE=${MESSAGE_FILE}.zip 29 TMP_FILE_1=/tmp/${CMD}_${NOW}_$$_secure_tmp_1 ; rm -f $TMP_FILE_1 30 TMP_FILE_2=/tmp/${CMD}_${NOW}_$$_secure_tmp_2 ; rm -f $TMP_FILE_2 31 32 echo "`date` = `date -u`" >> $MESSAGE_FILE; echo >> $MESSAGE_FILE 33 34 ##### Check SSH connection close 35 36 grep -E "sshd\[[0-9]+\]: Connection closed" /var/log/secure > $TMP_FILE_1 37 date -u -d @"$PAST" +"%b %e %T $MARK" >> $TMP_FILE_1 38 sort -k 1M -k 2n -k 3 $TMP_FILE_1 > $TMP_FILE_2 39 40 if [ $DEBUG -gt 0 ]; then tail $TMP_FILE_2; echo; fi 41 42 sed -i -e "1,/.* $MARK/d" \ 43 -e "/^[ ]*$/d" \ 44 $TMP_FILE_2 45 46 if [ $DEBUG -gt 0 ]; then cat $TMP_FILE_2; echo; fi 47 48 if [ -s $TMP_FILE_2 ]; then 49 SEND_MAIL="yes" 50 echo "There is at least one SSH connection close in $SPAN seconds" >> $MESSAGE_FILE 51 cat $TMP_FILE_2 >> $MESSAGE_FILE ; echo >> $MESSAGE_FILE 52 fi 53 54 ##### Check failed login 55 56 grep "Failed password for" /var/log/secure > $TMP_FILE_1 57 date -u -d @"$PAST" +"%b %e %T $MARK" >> $TMP_FILE_1 58 sort -k 1M -k 2n -k 3 $TMP_FILE_1 > $TMP_FILE_2 59 60 if [ $DEBUG -gt 0 ]; then tail $TMP_FILE_2; echo; fi 61 62 sed -i -e "1,/.* $MARK/d" \ 63 -e "/^[ ]*$/d" \ 64 $TMP_FILE_2 65 66 if [ $DEBUG -gt 0 ]; then cat $TMP_FILE_2; echo; fi 67 68 if [ -s $TMP_FILE_2 ]; then 69 SEND_MAIL="yes" 70 echo "There is at least one password failure in $SPAN seconds" >> $MESSAGE_FILE 71 cat $TMP_FILE_2 >> $MESSAGE_FILE ; echo >> $MESSAGE_FILE 72 fi 73 74 ##### Check login 75 76 last -a | head -n -2 > $TMP_FILE_1 77 date -d @"$PAST" +"-------- ------------ %a %b %e %H:%M - ------------------ $MARK" >> $TMP_FILE_1 78 79 sort -t \* -f -k 1.27M -k 1.31n -k 1.34 $TMP_FILE_1 > $TMP_FILE_2 80 81 if [ $DEBUG -gt 0 ]; then tail $TMP_FILE_2; echo; fi 82 83 sed -i -e "1,/.* $MARK/d" \ 84 -e "/^[ ]*$/d" \ 85 $TMP_FILE_2 86 87 if [ $DEBUG -gt 0 ]; then cat $TMP_FILE_2; echo; fi 88 89 if [ -s $TMP_FILE_2 ]; then 90 SEND_MAIL="yes" 91 echo "There is at least one login in $SPAN seconds" >> $MESSAGE_FILE 92 cat $TMP_FILE_2 >> $MESSAGE_FILE ; echo >> $MESSAGE_FILE 93 fi 94 95 if [ $SEND_MAIL = "yes" ]; then 96 if [ $DEBUG -gt 0 ]; then 97 echo "+++++" 98 cat $MESSAGE_FILE 99 echo "+++++" 100 else 101 logger "Security Warning by $CMD" 102 zip -q -P "$ZIP_PASSWORD" - $MESSAGE_FILE > $MESSAGE_ZIP_FILE 103 echo "Security Warning by $CMD" | \ 104 mutt -s "$MAIL_SUBJECT" -a $MESSAGE_ZIP_FILE -- $MAIL_TO 105 fi 106 fi 107 108 rm -f $TMP_FILE_1 109 rm -f $TMP_FILE_2 110 rm -f $MESSAGE_FILE 111 rm -f $MESSAGE_ZIP_FILE
簡単なプログラムなので、特段説明は必要ないと思うが、"/var/log/secure "に記録されるログはUTC(Universal Time Coordinated)で記録されるが、lastコマンドの出力はローカルタイムなので、dateコマンドの扱いが微妙に異なっている。
この例では1時間1分(3660秒)以内にSSHコネクションのグローズやログインがあった場合には、そのログをZIPで暗号化してメールに添付して送信する。メールに添付した暗号付きZIPファイルを解凍すると次のようなメッセージが報告される。
Wed Apr 2 00:33:58 JST 2008 = Tue Apr 1 15:33:58 UTC 2008 There is at least one SSH connection close in 3660 seconds Apr 1 15:01:03 accessserv sshd[18438]: Connection closed by 192.168.0.128 There is at least one login in 3660 seconds guest pts/3 Wed Apr 2 00:02 - 00:02 (00:00) 192.168.0.128
(最初の行にローカルタイムと協定世界時の両方を記録している。)
このchkloginというシェル・スクリプトをcronで1時間おきに動かすようにしている。
[root@accessserv ~]# crontab -e 0 * * * * /usr/local/bin/chklogin
なお、チェックの頻度を短くする場合はcronの設定を変えると同時に、chkloginの21行目の"SPAN"という変数の値も変更する。例えば15分であれば960にする(15分=900秒よりも若干長い時間にしておく。cronの間隔と丁度同じ間隔で設定した場合、マシンの負荷が多くコマンドの起動に時間がかかると、空白の時間が出来きてしまう。)