2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FluentdとNorikraでDoS攻撃検知→Fluentdでexec Output Pluginを使用してメール通知

Last updated at Posted at 2018-10-28

##はじめに
最近、DoS攻撃検知させる案件があり、
FluentdとNorikraで検証したときの構成を投稿します。

##参考
以下サイトを参考にさせていただきました。
https://dev.classmethod.jp/cloud/aws/block_dos_attack_by_norikra/

##インストール環境

  • CentOS 6
  • Apache access_log
  • Fluentd (v1.0 td-agent3)
  • jdk 1.8
  • rbenv
  • jruby
  • Norikra

##構築
###rbenvインストール

yum install -y git gcc-c++
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
exec $SHELL -l

#確認
rbenv --version

###JRubyインストール
今回はとりあえず最新で jruby-9.2.0.0 をインストールしました。

git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
rbenv install -l|grep jruby
rbenv install jruby-9.2.0.0
rbenv global jruby-9.2.0.0

#確認
ruby -v

###Norikraインストール

gem install norikra --no-ri --no-rdoc
rbenv rehash
mkdir /{etc,var/log,var/run}/norikra

#確認
which norikra
gem list --local | grep norikra

####Norikra Initスクリプト作成
@Kedamariさんのものを参考にさせていただきました。
https://qiita.com/Kedamari/items/6ee6252025ca6ddc6b21

####環境変数系の設定

# 環境変数系の設定
cat <<'EOT' > /etc/sysconfig/norikra
NORIKRA_OPT="-Xmx512m -d"
EOT

####Initスクリプト
起動ユーザはrootに変えています。
※サボってます、、

cat <<'EOT' > /etc/init.d/norikra
#!/bin/bash
#
# Norikra init shellscript
#   Copyright 2015, Kedamari
#
# /etc/rc.d/init.d/norikra
#
# chkconfig: - 80 20
# description: norikra
# processname: norikra
# pidfile: /var/run/norikra/norikra.pid

##########################
### Disabled http_proxy
##########################
unset http_proxy
unset HTTP_PROXY

##########################
### Definition
##########################
export PATH=/sbin:/usr/sbin:/bin:/usr/bin

NORIKRA_NAME=Norikra
NORIKRA_HOME=/etc/norikra
NORIKRA_DEFAULT=/etc/sysconfig/norikra

NORIKRA_USER=root
NORIKRA_GROUP=root

NORIKRA_LOG_DIR=/var/log/norikra
NORIKRA_BIN_FILE=/root/.rbenv/shims/norikra
NORIKRA_PID_FILE=/var/run/norikra/norikra.pid
NORIKRA_LOCK_FILE=/var/run/norikra/norikra.lock
NORIKRA_STATS_FILE=${NORIKRA_HOME}/norikra.json

. "${NORIKRA_DEFAULT}"
NORIKRA_OPTIONS="start ${NORIKRA_OPT} --pidfile=${NORIKRA_PID_FILE} --stats=${NORIKRA_STATS_FILE} --logdir=${NORIKRA_LOG_DIR} --middle"

START_STOP_DAEMON_ARGS="-l ${NORIKRA_USER} -g ${NORIKRA_GROUP}"

# timeout can be overridden from /etc/sysconfig/td-agent
STOPTIMEOUT=120
RETVAL=0

# Source function library.
. /etc/init.d/functions

##########################
### Function that starts
##########################

do_start() {
  ulimit -n 65536 1>/dev/null 2>&1 || true
  echo -n "Starting ${NORIKRA_NAME}: "
  local RETVAL=0
  runuser ${START_STOP_DAEMON_ARGS} -c bash -c "${NORIKRA_BIN_FILE} ${NORIKRA_OPTIONS}" || RETVAL="$?"
  [ $RETVAL -eq 0 ] && success && touch "${NORIKRA_LOCK_FILE}" || failure
  echo
  return $RETVAL
}

##########################
### Function that stops
##########################
do_stop() {
  echo -n "Shutting down ${NORIKRA_NAME}: "
  local RETVAL=0
  if [ -e "${NORIKRA_PID_FILE}" ]; then
    # Use own process termination instead of killproc because killproc can't wait SIGTERM
    NORIKRA_PID=`cat "${NORIKRA_PID_FILE}" 2>/dev/null`

    ${NORIKRA_BIN_FILE} stop >/dev/null 2>&1 || RETVAL="$?"
    if [ $RETVAL -eq 0 ]; then
   TIMEOUT="$STOPTIMEOUT"
   while [ $TIMEOUT -gt 0 ]; do
     ${NORIKRA_BIN_FILE} stop >/dev/null 2>&1 || break
     sleep 1
     let TIMEOUT="${TIMEOUT}-1" || true
   done
   if [ "$TIMEOUT" -eq 0 ]; then
     echo -n "Timeout error occurred trying to stop ${NORIKRA_NAME}..."
     RETVAL=1
     failure || true
   else
     RETVAL=0
     success
   fi
    else
   failure || true
    fi
  else
    failure || true
    RETVAL=4
  fi

  echo
  [ $RETVAL -eq 0 ] && rm -f "${NORIKRA_PID_FILE}" && rm -f "${NORIKRA_LOCK_FILE}"
  return $RETVAL
}

##########################
### Function that restarts
##########################
do_restart() {
  do_stop || true
  do_start
}

case "$1" in
"start" )
  test -f ${NORIKRA_LOCK_FILE} || false
  do_start
  ;;
"stop" )
  do_stop
  ;;
"restart" )
  do_restart
  ;;
"status" )
  status -p "${NORIKRA_PID_FILE}" "${NORIKRA_NAME}"
  ;;
* )
  echo "Usage: $0 {start|stop|restart|status}" >&2
  exit 1
  ;;
esac
EOT

####自動起動

chkconfig --add norikra
chkconfig norikra on
chmod +x /etc/init.d/norikra

####Norikraクエリ設定
例です。SQL文は初心者なのですみません。

  • 画像、CSS、JSなどは対象外
  • プライベートIPからのアクセスは除外
  • 1分間で60以上アクセスで検知
select CURRENT_TIMESTAMP() as timestamp,
  host,
  count(*) as requests
from access.win:time_batch(1 min)
where not (
  path like '%.gif'  or
  path like '%.jpg'  or
  path like '%.jpeg' or
  path like '%.png'  or
  path like '%.JPG'  or
  path like '%.ico'  or
  path like '%.js'   or
  path like '%.css'  or
  path like '%.svg'  or
  path like '%.woff'
  ) and not (
  host like '10.%'      or
  host like '172.16.%'  or
  host like '172.17.%'  or
  host like '172.18.%'  or
  host like '172.19.%'  or
  host like '172.2%'    or
  host like '172.30.%'  or
  host like '172.31.%'  or
  host like '192.168.%'
  )
group by host
having count(*) >= 60

###Fluentdインストール

公式サイト手順
https://docs.fluentd.org/v1.0/articles/install-by-rpm

curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent3.sh | sh

####プラグインインストール
今回使うのは、fluent-plugin-norikraだけです。

td-agent-gem install fluent-plugin-norikra

####Initスクリプト調整
またサボってます、、

# 起動ユーザをrootに変更
vi /etc/init.d/td-agent
TD_AGENT_USER=root
TD_AGENT_GROUP=root

####自動起動

chkconfig td-agent on

####Fluentd設定
ログフォーマットはApacheのcombinedに%Dをつけたもの

LogFormat "%h %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\""
cp -p /etc/td-agent/td-agent.conf /etc/td-agent/td-agent.conf_org &&\
cat <<'EOT' > /etc/td-agent/td-agent.conf
#### Global Setting
<source>
  @type monitor_agent
  bind 0.0.0.0
  port 24220
</source>

#### Input
### Apache
<source>
  @type tail
  tag apache.access
  format /^(?<remote_host>[^ ]*) (?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*) (?<response_time>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/
  time_format %d/%b/%Y:%H:%M:%S %z
  types code:integer,size:integer,response_time:float
  path /var/log/httpd/access_log
  pos_file /var/log/td-agent/apache.access.pos
  read_from_head true
</source>

###norikra
<source>
  @type norikra
  norikra localhost:26571
  <fetch>
    method sweep
    tag query_name
    tag_prefix norikra.query
    interval 1s
  </fetch>
</source>

#### Output
### Apache
<match apache.access>
  @type norikra
  norikra localhost:26571
  buffer_queue_limit 1
  retry_limit 0
  remove_tag_prefix apache
  target_map_tag true
  <buffer>
    @type file
    path /var/log/td-agent/buffer/apache.access
    flush_interval 1s
  </buffer>
</match>

### ExecuteCommand
<match norikra.query.**>
  @type exec
  command /bin/bash /usr/local/bin/action.sh
  <format>
    @type  json
    keys timestamp,host,requests
  </format>
  <inject>
    time_key timestamp
    time_format %Y-%m-%dT%H:%M:%S
    timezone Asia/Tokyo
  </inject>
  <buffer>
    @type file
    path /var/log/td-agent/buffer/norikra.query
    flush_interval 1s
  </buffer>
</match>
EOT

###jqコマンドインストール
exec Output Pluginでjson形式のファイルをシェルスクリプトに渡します。
jqを使うとパースがとても簡単なので入れます。

# ここからダウンロード
https://stedolan.github.io/jq/

# とりあえずダウンロードしたものをPATHの通っているところに置く
mv jq-linux64 /usr/bin/jq
chmod +x /usr/bin/jq

###exec output pluginで実行されるスクリプト
Fluentdからは目的のjsonが複数行渡されることもあるので、
一応すべての行を読み取るようにしてあります。

#!/bin/bash

logdir=/usr/local/bin
logfile=action.log

mail_server=localhost
mail_port=25
mail_from=norikra@example.com
mail_subject="題名入力"
mail_to[0]=user1@example.com
mail_to[1]=user2@example.com
mail_msg=$(cat <<EOT

・・・メール本文・・・

EOT
)

{
# Parse Json
mapfile -t msg < <(cat "$1")

for ((i=0; i<${#msg[@]}; i++)); do
  if [ $i -gt 0 ]; then
    mail_body+="\n"
  fi
  host="$(echo "${msg[$i]}" | jq -r '.host')"
  requests="$(echo "${msg[$i]}" | jq -r '.requests')"
  timestamp="$(echo "${msg[$i]}" | jq -r '.timestamp')"
  request_data+="検知日時: $timestamp, 送信元IP: $host, リクエスト数: $requests"
done

# ログ出力
echo -e "$request_data"

# メール本文合成
mail_body=$request_data
mail_body+=$mail_msg


# Send Mail
mail_tos=""
for ((i=0; i<${#mail_to[@]}; i++)); do
  mail_tos+="${mail_to[$i]} "
done

echo -e "$mail_body" | mail -S "smtp=smtp://$mail_server:$mail_port" \
-s "$mail_subject" -r "$mail_from" "$mail_tos"

} 2>&1 | awk '{print strftime("%Y-%m-%dT%H:%M:%S "),$0 } { fflush() } ' >> "${logdir}/${logfile}"

※execプラグインでは、実行するコマンドに引数として、バッファファイルが指定されて実行されます。
例えば今回の場合、以下コマンドをFluentdが実行します。

/bin/bash /usr/local/bin/action.sh /var/log/td-agent/buffer/norikra.query/<バッファファイル>
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?