phpとか

自分用備忘録なので、自分が分かる程度にしか書いてません。

sshログインエラー通知

sshログイン失敗したら検知してslackに流すスクリプト作ってみました。

#!/bin/sh

#
# init
#

LOG=""
DATE_OK=1
IP=""
OK_IP_ARR=("許可IP" "許可IP")

#
# get the last line of log for login error.
#

getLog(){
  GREP=""
  for OK_IP in "${OK_IP_ARR[@]}"
  do
    GREP=${GREP}" | grep -v \"${OK_IP}\""
  done

  LOG_GET_SC="cat /var/log/secure | grep \"error\: Received\"${GREP} | tail -1 | awk '{print \$1\"__\"\$2\"__\"\$3\"__\"\$10}'"
  LOG=`eval ${LOG_GET_SC}`
  if test "$LOG" = ""; then
    exit 1
  fi
}


#
# investigate the date
#

investigateDate(){
  MONTH_ARR=(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
  MONTH=1
  DATETIME=""
  ARR=(`echo ${LOG} | tr -s '__' ' '`)
  _MONTH=${ARR[0]}
  DATE=${ARR[1]}
  TIME=${ARR[2]}
  IP=${ARR[3]%:}

  for ITEM in "${MONTH_ARR[@]}"
  do
    if [ ${ITEM} = ${_MONTH} ]; then
      break
    fi
    (( MONTH++ ))
  done

  DATETIME=`date "+%Y"`-`printf %02d ${MONTH}`"-${DATE} $TIME"
  TARGET_DATETIME=`date "+%Y-%m-%d %H:%M:%S" -d "5 minute ago"`

  if [ `date -d "${DATETIME}" "+%s"` -gt `date -d "${TARGET_DATETIME}" "+%s"` ];then
    DATE_OK=0
  fi
}


#
# report
#
report(){
  CHANNEL="#チャンネル名"
  USER="ユーザ名"
  BODY="第三者がsshログインを試み、失敗しました。IP:"$IP
  URL="取得したslackのapi用URL"
  ICON=":lock:"

  if [ ${DATE_OK} -lt 1 ]; then
    curl -X POST --data-urlencode "payload={\"channel\": \"${CHANNEL}\", \"username\": \"${USER}\", \"text\": \"${BODY}\", \"icon_emoji\": \"${ICON}\"}" ${URL}
  fi
}


getLog
investigateDate
report

5分毎にcronで回すようにしました。

【やってること】

sshログインエラー時のログの内容

Nov 25 19:25:02 自分のIP sshd[4494]: error: Received disconnect from アクセス元IP: 14: No supported authentication methods available [preauth]

1./var/log/secureの"error: Received"が含まれる行の最終行から、整形して抜き出し。(許可IPを除外)

GREP=""
for OK_IP in "${OK_IP_ARR[@]}"
do
  GREP=${GREP}" | grep -v \"${OK_IP}\""
done

LOG_GET_SC="cat /var/log/secure | grep \"error\: Received\"${GREP} | tail -1 | awk '{print \$1\"__\"\$2\"__\"\$3\"__\"\$10}'"
LOG=`eval ${LOG_GET_SC}`
if test "$LOG" = ""; then
  exit 1
fi


for文で許可IP除外grepを作ってます。
文字列をコマンドとして実行するために eval関数を使用。

以下みたいに抜き出せます。

Nov__25__19:25:02__182.171.253.148:

2.上記を「__」をデリミタとして、配列作成 & 年月日とかの値セット

ARR=(`echo ${LOG} | tr -s '__' ' '`)
_MONTH=${ARR[0]}
DATE=${ARR[1]}
TIME=${ARR[2]}
IP=${ARR[3]%:}

※IPは後ろに「:」ついてたので取ってます。

3.月が英語なので、数字に変換

for ITEM in "${MONTH_ARR[@]}"
do
  if [ ${ITEM} = ${_MONTH} ]; then
    break
  fi
  (( MONTH++ ))
done

4.エラー日時を日付に変換

DATETIME=`date "+%Y"`-`printf %02d ${MONTH}`"-${DATE} $TIME"

5.現在日付の5分前

TARGET_DATETIME=`date "+%Y-%m-%d %H:%M:%S" -d "5 minute ago"`

6.エラー日時が5分前なら、フラグたてる

if [ `date -d "${DATETIME}" "+%s"` -gt `date -d "${TARGET_DATETIME}" "+%s"` ];then
  DATE_OK=0
fi

7.フラグあれば、slackで通知

report(){
  CHANNEL="#チャンネル名"
 USER="存在しないユーザ名もOK"
 BODY="容量やばいよ"
 URL="自分のwebfook url"
  ICON=":lock:"

  if [ ${DATE_OK} -lt 1 ]; then
    curl -X POST --data-urlencode "payload={\"channel\": \"${CHANNEL}\", \"username\": \"${USER}\", \"text\": \"${BODY}\", \"icon_emoji\": \"${ICON}\"}" ${URL}
  fi
}

slackでの通知に関して、下記の記事でもう少しだけ詳しく書いたので、ご覧ください。
starfam.hatenablog.com