iptables -I INPUT -s 222.186.190.6 -j DROP
service iptables save
(2017/07/16ここから)
最近、一日に一万回以上のアタックをしてくるところが何か所かある。それ自体はほとんどがrootでやってくるから、何万回やられても大丈夫なのだけど、tailでsecureログを見てると目が回るしなんとなくそこまでやられると怖いものがあると思って真面目にそれらを止める気になった。(ほんとに危険なアタックがログに埋もれるし。)
hashlimitが入っているのに何でこんなに来るのかとよーく考えてみたら、hashlimitはポート22を監視しているのに、攻撃側はいろいろなポートを変えながら来ている。だからhashlimitは無駄に多く来るアタックを防ぐのには役に立たない。だからと言ってhashlimitでtcm全体を規制にかけてしまうと他に影響が出てしまう。でも、自分のsshのポートをついてきてほんとに危ない時には守ってくれるから、hashlimitは絶対に入れておいた方がいい。
無駄に多く来るアタックを確実に止めるにはアタックしてくるipを登録して遮断するのが一番いいみたいだ。自動で登録できるようにスクリプトを書いてもいいし、logwatchを見て目立つipをdropに登録してもいい。
logwatchで目立つipを見つけるのには
秀丸検索窓にでもコピーして、正規表現
\d{3,5} time
秀丸マクロでは
searchdown "\\d{3,5} time", nocasesense, regular;
(マクロで使うときは\dの前にエスケープ文字\が入る)
をすると3桁から5桁の間でアタックしてきたアドレスを簡単に探せる。
とりあえず2つ登録しただけでだいぶアタックが少なくなった。
と思ったら、少し間をおいてみてみたら、ほかのipからのアタックが始まった。
なにこれ?連携しているのか?
2,3分静かになった後でまた、他のipからのアタックが始まる。
しばらくは、アタックをしてくるアドレスの登録を自動でしないとダメか。
自動登録は他の人のスクリプトを使わせてもらって、それを少し改造した。
下の方に書いた。
でも、こういうのは少し怖い。これをwebサイトのログインに仕掛けられたらたまったもんじゃない。(drupalなどは何もしなくとも単純なアタックには対策が取られている。)
(2017/07/16ここまで)
ここから下はかなり前に設定したhashlimitの設定。
iptablesを使って対策をする。
(自分はCentOS6を使っている。先月7月7日にCentOS7が公開されて、7ではiptablesがfirewalldという物になったそうだ。iptablesがfirewalldに含まれるということらしい。)
まず最初に、
/etc/sysconfig/iptables
が存在するかどうかの確認をする。
なければ、
#system-config-firewall-tui
(以前はsystem-config-secutitylevel-tui)
で、iptablesを有効にすると、iptablesが作成される。
現在の設定の確認。
#iptables -L
ついでに、ファイルも確認。
#vi /etc/sysconfig/iptables
次に、iptablesが、全部のパケットを受け入れる状態を作っていく。
(自分の場合は、外からの通信はルーターにファイアーウォールを任せてあるので、内側にあるサーバーはパケット全部をacceptするのが基本になっている。)
#service iptables stop
としてiptablesを止めて、全てのチェーンの削除。
#iptables -F
その後、念のため、ユーザー定義チェーンの削除。
#iptables -X
確認
#iptables -L
この設定は、今現在の一時的な設定で、保存をしないと/etc/sysconfig/iptablesが書き換えられないから、
#service iptables save
として/etc/sysconfig/iptablesに保存しておく。
次に、sshのbrute forceアタック等のパケットの制限--DOS的パケットをフィルタリングするための設定に移る。
コマンドiptablesを実行する。
次の3つ。
確立された接続はそのまま継続させる。
iptables -A INPUT -j ACCEPT -p tcp --dport 22 -m state --state ESTABLISHED,RELATED
sshのacceptする条件。(/proc/net/ipt_hashlimit/にout_sshファイルが作られる)
iptables -A INPUT -j ACCEPT -p tcp --dport 22 -m hashlimit --hashlimit 3/minute --hashlimit-burst 3 --hashlimit-name out_ssh --hashlimit-mode srcip,dstip --hashlimit-htable-expire 60000
iptables -A INPUT -j DROP -p tcp --dport 22
out_sshファイルの場所は
/proc/net/ipt_hashlimit/out_ssh
(この3つの設定は、8年くらい前から
http://dsas.blog.klab.org/archives/50208645.html
に書いてあったものを使わせてもらっている。
一つ一つの解説もそこに書いてある。)
その後、
#service iptables save
#service iptables start
確認
#iptables -L
又は
#vi /etc/sysconfig/iptables
一応、こっちも確認。
#chkconfig --list iptables
-----------------------------------------------------------------
例えば、下の設定は、他のサイトにあった設定なのだけど、このようにicmp(ping)に対してならばうまく動いている。
hashlimit.sh
# HASHCHECK チェインの作成
iptables -N HASHCHECK
# HASHLIMITを使用して接続数の制限実施し許可する
# -m hashlimit ・・・hashlimitモジュールを使用
# --hashlimit-name hashcheck_t ・・・ハッシュテーブル名を「hashcheck_t」とする
# --hashlimit 1/m ・・・時間あたりの接続数を「1/分」に設定
# --hashlimit-burst 30 ・・・バースト値(上記--hashlimit値を無視して接続できる数)を「30」に設定
# --hashlimit-mode srcip,dstport ・・・制限対象を「送信元IPアドレス」と「宛先ポート」で識別する
# --hashlimit-htable-expire 120000 ・・・ハッシュテーブル中のレコード保持期間を「120000ミリ秒」に設定
iptables -A HASHCHECK -m hashlimit --hashlimit-name hashcheck_t \
--hashlimit 1/m --hashlimit-burst 30 --hashlimit-mode srcip,dstport \
--hashlimit-htable-expire 120000 -j ACCEPT
# 許可されなかった通信をログに記録する
iptables -A HASHCHECK -m limit --limit 1/s -j LOG --log-prefix '[IPTABLES HASH DROP] : '
# 許可されなかった通信をDROPする
iptables -A HASHCHECK -j DROP
# チェインを適応
iptables -A INPUT -p icmp -j HASHCHECK #サンプル ICMPを制限
実際に使用する場合は、HASHLIMITでの制限を行う前に以下の処理を行ったほうがよいでしょう。
信頼できるIPアドレス/IPアドレス帯からの通信を許可(誤爆防止)
確立済み通信[ELATED,ESTABLISHED]の許可(確立済みの通信に対してhashlimit実施することは余り無いように思えます)
-------------------------------------------------------------------
man iptablesのhashlimiの部分
hashlimit
hashlimit uses hash buckets to express a rate limiting match (like the
limit match) for a group of connections using a single iptables rule.
Grouping can be done per-hostgroup (source and/or destination address)
and/or per-port. It gives you the ability to express "N packets per
time quantum per group":
matching on source host
"1000 packets per second for every host in 192.168.0.0/16"
matching on source prot
"100 packets per second for every service of 192.168.1.1"
matching on subnet
"10000 packets per minute for every /28 subnet in 10.0.0.0/8"
A hash limit option (--hashlimit-upto, --hashlimit-above) and --hash-
limit-name are required.
--hashlimit-upto amount[/second|/minute|/hour|/day]
Match if the rate is below or equal to amount/quantum. It is
specified as a number, with an optional time quantum suffix; the
default is 3/hour.
--hashlimit-above amount[/second|/minute|/hour|/day]
Match if the rate is above amount/quantum.
--hashlimit-burst amount
Maximum initial number of packets to match: this number gets
recharged by one every time the limit specified above is not
reached, up to this number; the default is 5.
--hashlimit-mode {srcip|srcport|dstip|dstport},...
A comma-separated list of objects to take into consideration. If
no --hashlimit-mode option is given, hashlimit acts like limit,
but at the expensive of doing the hash housekeeping.
--hashlimit-srcmask prefix
When --hashlimit-mode srcip is used, all source addresses
encountered will be grouped according to the given prefix length
and the so-created subnet will be subject to hashlimit. prefix
must be between (inclusive) 0 and 32. Note that --hashlimit-src-
mask 0 is basically doing the same thing as not specifying srcip
for --hashlimit-mode, but is technically more expensive.
--hashlimit-dstmask prefix
Like --hashlimit-srcmask, but for destination addresses.
--hashlimit-name foo
The name for the /proc/net/ipt_hashlimit/foo entry.
--hashlimit-htable-size buckets
The number of buckets of the hash table
--hashlimit-htable-max entries
Maximum entries in the hash.
--hashlimit-htable-expire msec
After how many milliseconds do hash entries expire.
--hashlimit-htable-gcinterval msec
How many milliseconds between garbage collection intervals.
------------------------------------------------------------------
filterテーブルをファイルに直接書くときは
行頭に「*(アスタリスク)」を付けて、最後を「COMMIT」で指定。その間に書かれた設定がfilterテーブル。
------------------------------------------------------------------
iptables -F はポリシーの変更は行わない。
ポリシーがrejectになっていて、それに続くルールでsshをacceptとして動作させているときには注意。
その場合、iptables -Fをやったとたんにsshが切断される。
それをやってしまった時の対処、対策
この場合iptablesファイルの書き換えはされていないので、
PC本体が近ければ、(pcにモニターとキーボードがついていれば直接操作できるけど・・・)pcの電源を切って再起動させればiptables -Fをやる前のiptablesファイルのセーブはされていないので、それを読み込んで立ち上がる。
もし、pcから遠いところでsshをやっているときは保険をかけておく。
atコマンドに/etc/init.d/iptables restartを登録しておくか、
(sleep 60; /etc/init.d/iptables restart) &
などとしておくか。
----------------------------------------------------------------
自ローカルアドレスをACCEPTしておくには
# iptables -I INPUT -s 192.168.1.0/24 -j ACCEPT
ipアドレス123.45.67.890を拒否したいとき
# iptables -I INPUT -s 123.45.67.890 -j DROP
123.45.67.*を全て拒否(123.45.67.0~123.45.67.255)
# iptables -I INPUT -s 123.45.67.0/24 -j DROP
もしくは iptables に下記を追加
-A INPUT -s 123.45.67.0/255.255.255.0 -j DROP
iptablesのルールを一行だけ削除したいときは
まず、ルールの行ナンバーを調べる。
iptables -L --line-numbers
次に、チェイン名に続いて行ナンバーを入力する。
iptables -D INPUT 1
----------------------------------------------------------------
さくらサーバーの解説を元に最後に自分に必要なルールを付け足した。
最初にDROPから入る。この方が自分の使っているのよりも安全。
# (1) ポリシーの設定 OUTPUTのみACCEPTにする
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# (2) ループバック(自分自身からの通信)を許可する
-A INPUT -i lo -j ACCEPT
# (3) データを持たないパケットの接続を破棄する
-A INPUT -p tcp --tcp-flags ALL NONE -j DROP
# (4) SYNflood攻撃と思われる接続を破棄する
-A INPUT -p tcp ! --syn -m state --state NEW -j DROP
# (5) ステルススキャンと思われる接続を破棄する
-A INPUT -p tcp --tcp-flags ALL ALL -j DROP
# (6) icmp(ping)の設定
# hashlimitを使う
# -m hashlimit hashlimitモジュールを使用する
# —hashlimit-name t_icmp 記録するファイル名
# —hashlimit 1/m リミット時には1分間に1パケットを上限とする
# —hashlimit-burst 10 規定時間内に10パケット受信すればリミットを有効にする
# —hashlimit-mode srcip ソースIPを元にアクセスを制限する
# —hashlimit-htable-expire 120000 リミットの有効期間。単位はms
-A INPUT -p icmp --icmp-type echo-request -m hashlimit --hashlimit-name t_icmp --hashlimit 1/m --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-htable-expire 120000 -j ACCEPT
# (7) 確立済みの通信は、ポート番号に関係なく許可する
-A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
# (8) 任意へのDNSアクセスの戻りパケットを受け付ける
-A INPUT -p udp --sport 53 -j ACCEPT
# (9) SSHを許可する設定
# hashlimitを使う
# -m hashlimit hashlimitモジュールを使用する
# —hashlimit-name t_sshd 記録するファイル名
# —hashlimit 1/m リミット時には1分間に1パケットを上限とする
# —hashlimit-burst 10 規定時間内に10パケット受信すればリミットを有効にする
# —hashlimit-mode srcip ソースIPを元にアクセスを制限する
# —hashlimit-htable-expire 120000 リミットの有効期間。単位はms
-A INPUT -p tcp -m state --syn --state NEW --dport 22 -m hashlimit --hashlimit-name t_sshd --hashlimit 1/m --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-htable-expire 120000 -j ACCEPT
# (10) 個別に許可するプロトコルとポートをここに書き込む。
# この例では、HTTP(TCP 80)とHTTPS(TCP 443)を許可している。
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
-A INPUT -p tcp --dport 110 -j ACCEPT
-A INPUT -p tcp --dport 25 -j ACCEPT
-A INPUT -p tcp --dport 465 -j ACCEPT
-A INPUT -p tcp --dport 587 -j ACCEPT
-A INPUT -p tcp --dport 993 -j ACCEPT
-A INPUT -p tcp --dport 995 -j ACCEPT
-A INPUT -p tcp --dport 53 -j ACCEPT
-A INPUT -p udp --dport 53 -j ACCEPT
-A INPUT -p tcp --dport 20 -j ACCEPT
-A INPUT -p tcp --dport 21 -j ACCEPT
-A INPUT -p tcp --dport 4000:4029 -j ACCEPT
COMMIT
-------------------------------------------------------------
hashlimitはicmp(ping)を規制するのにも使える。
-------------------------------------------------------
secureログ、その他のログからからアタックしてくるIPアドレスを探してそれをiptablesに登録してくれるシェルスクリプトを
Brute Force Attack対策+α(iptables) 20160117
という題のサイトのページで公開してくれている(CentOS6用iptablesだと思う。)。
ちなみに、
Brute Force Attack対策+α(Firewalld) 20170517
という、(たぶん)CentOS7用のファイアウォール版もある。
自分は、それを一日に一回くらいcronで回そうと思っている。
27 03 * * * root /usr/local/bin/brute_force_attack_iptables.sh
そのサイトの人は一分間隔で回しているみたいだけど、自分の場合は一日一回、cronで回しておけば、大丈夫だと思う(環境は人それぞれだから、1分でも間に合わない人もいるのだろう。)。
それで、そのスクリプトをそのまま使うと一回のアドレス登録で一回のメールが来る。
つまり、一回のスクリプトの実行で10個アドレスの登録があると、10通のメールが来てしまう。
一分間隔でcronで回すための設計なのでそれでいいのだけど、自分の場合はそれを一日に一回か二回cronで回す予定なので、
それをまとめて一回のメールで配信するように少しだけ自分用に変更させてもらった。
http://ry.tl/brute_force_attack_iptables.html
こちらを使わせてもらった。
------------------------------------------------------------------------------
#!/bin/sh
# 自分用設定 一時ファイル作成ディレクトリの指定/tmp から /dev/shm
# 自分用設定 メール送信先アドレス rootからhoge@hogepeke.comへ変更。
# 自分用設定 TMP3="$TMP_DIR/brute_force_attack3.tmp"を追加。
export LANG=ja_JP.UTF-8
# Ver 20160115
# 攻撃判断回数(デフォルト10回以上)
NG="10"
# ログ監視の有効化設定 「no」で無効。(デフォルトは全て有効)
# 自分用設定 FTPは未インストール、DNSはキャッシングサーバーとして外からは受け入れていない。named.logも作成していない。
TELNET_CHECK="yes"
SSH_CHECK="yes"
FTP_CHECK="no"
MAIL_CHECK="yes"
HTTP_CHECK="yes"
DNS_CHECK="no"
# 監視ログファイル
SSH_LOG="/var/log/secure"
FTP_LOG="/var/log/vsftpd.log"
MAIL_LOG="/var/log/maillog"
HTTP_LOG="/var/log/httpd/error_log"
HTTP_LOG2="/var/log/httpd/access_log"
DNS_LOG="/var/log/named.log"
# HTTP監視除外ディレクトリ及びファイル設定(デフォルトでは「/home」ディレクトリ 以下に存在するファイル全て)
#DIR="/home"
DIR="/var/www/html"
# 攻撃を検知した際に自分にメールを送信するか否か。「no」で無効。(デフォルト有効)
MAILMESSAGES="yes"
# メール送信先アドレス。(デフォルトroot宛)
MAILADDRESS="hoge@hogepeke.com"
# 動作ログのファイル名
BR_LOG="/var/log/brute_force_attack.log"
# ホワイトリストファイルの指定
WHITE_LIST="/root/allow_ip"
# ブラックリストファイルの指定
BLACK_LIST="/root/deny_ip"
#BLACK_LIST="/etc/hosts.deny"
# →TCP Wrapperを使用する場合は後者
# 一時ファイル作成ディレクトリの指定
TMP_DIR="/dev/shm"
# 以降設定不要
# タイムスタンプ設定
LOG_DATE=`date '+%Y/%m/%d %k:%M:%S'`
# 一時ファイル設定
TMP="$TMP_DIR/brute_force_attack.tmp"
TMP2="$TMP_DIR/brute_force_attack2.tmp"
TMP3="$TMP_DIR/brute_force_attack3.tmp"
# フラグ管理
UP_FLAG=0
function_CORE()
{
if [ $1 = "TELNET" -a $TELNET_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
grep "login:" $SSH_LOG | grep "FAILED LOGIN" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
elif [ $1 = "SSH" -a $SSH_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
grep "sshd" $SSH_LOG | grep "Invalid user" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
grep "sshd" $SSH_LOG | grep "Failed password" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
elif [ $1 = "FTP" -a $FTP_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
grep "FAIL LOGIN" $FTP_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
elif [ $1 = "MAIL" -a $MAIL_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
grep "authentication failure" $MAIL_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
grep "auth failed" $MAIL_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
elif [ $1 = "HTTP" -a $HTTP_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
DIR_LIST=`ls $DIR`
grep "error" $HTTP_LOG | grep "File does not exist" | grep -v "$DIR_LIST" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
grep "error" $HTTP_LOG | grep "script" | grep "not found or unable to stat" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep "\.\./" $HTTP_LOG2 | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep "//" $HTTP_LOG2 | grep -v http:// | grep -v https:// | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
elif [ $1 = "DNS" -a $DNS_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
grep "denied" $DNS_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
else
touch $TMP
fi
cp -p $TMP $TMP2
if [ -s $WHITE_LIST ]; then
WHITE=`cat $WHITE_LIST`
grep -v "$WHITE" $TMP2 > $TMP
cp -pr $TMP $TMP2
else
touch $TMP
fi
if [ -s $BLACK_LIST ]; then
BLACK=`cat $BLACK_LIST`
grep -v "$BLACK" $TMP2 > $TMP
else
touch $TMP
fi
LIST=`cat $TMP`
for IP in $LIST
do
COUNT=`grep -w "$IP" $TMP | wc -l`
if [ $COUNT -ge $NG ] ; then
if [ -s $WHITE_LIST ]; then
if [ `grep -w "$IP" $WHITE_LIST` ] ; then
WHITE_FLAG=1
else
WHITE_FLAG=0
fi
else
WHITE_FLAG=0
fi
if [ -s $BLACK_LIST ]; then
if [ `grep -w "$IP" $BLACK_LIST` ] ; then
BLACK_FLAG=1
else
BLACK_FLAG=0
fi
else
BLACK_FLAG=0
fi
if [ $WHITE_FLAG -eq 1 -o $BLACK_FLAG -eq 1 ] ; then
:
else
echo "["$LOG_DATE"] ""[$SYSTEM_NAMEログ監視] IP をブラックリストへ登録:"$COUNT "回 ["$IP"]" >> $BR_LOG
echo $IP >> $BLACK_LIST
iptables -I INPUT -s $IP -j DROP
UP_FLAG=1
if [ $MAILMESSAGES = "yes" ] ; then
# echo -e "[$SYSTEM_NAME]ログ監視にて、"$COUNT"回の不正なアクセスログを確認。\n対象ホストのIPアドレスをiptables及びブラッ クリストファイルへ追加しました。\n\nIPアドレス [$IP]"| mail -s "$0" $MAILADDRESS
# 自分用設定
echo -e "\n[$SYSTEM_NAME]ログ監視にて、"$COUNT"回の不正なアクセスログを確認。\n対象ホストのIPアドレスをiptables及びブラッ クリストファイルへ追加しました。\nIPアドレス [$IP]">> $TMP3
fi
fi
fi
done
# IPTABLES更新時に設定を保存
if [ $UP_FLAG -eq 1 ] ; then
/etc/rc.d/init.d/iptables save
fi
# 一時ファイル削除
rm -rf $TMP $TMP2
}
# 実行
function_CORE TELNET
function_CORE SSH
function_CORE FTP
function_CORE MAIL
function_CORE HTTP
function_CORE DNS
# 自分用設定
if [ $MAILMESSAGES = "yes" ] ; then
#TMP3ファイルはdropするファイルがないと作成されない。TMP3ファイルが存在しない場合。
if [ -e $TMP3 ]; then
cat $TMP3| mail -s "iptable drop 登録" $MAILADDRESS
else
echo "対象IPアドレスはありませんでした" | mail -s "iptables drop 登録" $MAILADDRESS
fi
fi
rm -rf $TMP3
20170726追加
blackリストには登録されるのに、iptablesファイルには登録されないIPが出てきてしまう。
そうなると、そのIPはいつまでたってもブロックされない。
iptablesコマンドが機能しない時があるってことだよなぁ。
iptablesコマンドで登録するのに時間がかかるのが原因なのか?
2重に使うと2回目のコマンドがきかなくなるのかなぁ。
シェルスクリプトはコマンドの処理が終わらないうちに次のコマンドを実行しないと思っていたけど、そうではないのか。
waitとかsleepで前のiptablesコマンドの実行が終わるのを待たせてから次のiptablesコマンドを使うようにすればいいのか?
ブラックリストを作るのをやめて、iptablesファイルをブラックリストとして使えばいいんじゃないか。
スクリプトを実行する前にブラックリストを全部消して、iptablesファイルからip部分だけをコピーするか。
そうしておけば、iptablesコマンドがたとえ失敗したとしても次回の網にかかる。
20170726以前追加
うまく動かなくなったときはブラックリストとiptablesに登録してあるリストを全部削除するとまた動き出す。
: > /root/deny_ip
(:このコロンは何もしない、空のという意味。)
iptables -F
service iptables save
/etc/init.d/iptables restart
(自分のうまく動かなくなった理由は、たぶん、cronでスクリプトを動かしたときにiptablesコマンドだけが動いていないときがあった。だから、ブラックリストにipアドレスが書き込まれて、iptablesのテーブルにipアドレスが書き込まれない状態になった。ブラックリストに書き込まれたアドレスは重複しないようにiptablesに書き込まないから、iptablesコマンドが動かないときにブラックリストに登録されたipアドレスは永遠にdropされない状態になる。たぶん、多く攻撃してくるアドレスは連動している。dropされると次のipアドレスに移るのだけど、dropされなければずーと、そのアドレスが攻撃を続ける。だから、そのipアドレスが生き続けたのかな?)
上のスクリプトの設定で
BLACK_LIST="/root/deny_ip"
#BLACK_LIST="/etc/hosts.deny"
# →TCP Wrapperを使用する場合は後者
の3行があるが、それで、そのスクリプトをそのまま使うと一回のアドレス登録で一回のメールが来る。
つまり、一回のスクリプトの実行で10個アドレスの登録があると、10通のメールが来てしまう。
一分間隔でcronで回すための設計なのでそれでいいのだけど、自分の場合はそれを一日に一回か二回cronで回す予定なので、
それをまとめて一回のメールで配信するように少しだけ自分用に変更させてもらった。
http://ry.tl/brute_force_attack_iptables.html
こちらを使わせてもらった。
------------------------------------------------------------------------------
#!/bin/sh
# 自分用設定 一時ファイル作成ディレクトリの指定/tmp から /dev/shm
# 自分用設定 メール送信先アドレス rootからhoge@hogepeke.comへ変更。
# 自分用設定 TMP3="$TMP_DIR/brute_force_attack3.tmp"を追加。
export LANG=ja_JP.UTF-8
# Ver 20160115
# 攻撃判断回数(デフォルト10回以上)
NG="10"
# ログ監視の有効化設定 「no」で無効。(デフォルトは全て有効)
# 自分用設定 FTPは未インストール、DNSはキャッシングサーバーとして外からは受け入れていない。named.logも作成していない。
TELNET_CHECK="yes"
SSH_CHECK="yes"
FTP_CHECK="no"
MAIL_CHECK="yes"
HTTP_CHECK="yes"
DNS_CHECK="no"
# 監視ログファイル
SSH_LOG="/var/log/secure"
FTP_LOG="/var/log/vsftpd.log"
MAIL_LOG="/var/log/maillog"
HTTP_LOG="/var/log/httpd/error_log"
HTTP_LOG2="/var/log/httpd/access_log"
DNS_LOG="/var/log/named.log"
# HTTP監視除外ディレクトリ及びファイル設定(デフォルトでは「/home」ディレクトリ 以下に存在するファイル全て)
#DIR="/home"
DIR="/var/www/html"
# 攻撃を検知した際に自分にメールを送信するか否か。「no」で無効。(デフォルト有効)
MAILMESSAGES="yes"
# メール送信先アドレス。(デフォルトroot宛)
MAILADDRESS="hoge@hogepeke.com"
# 動作ログのファイル名
BR_LOG="/var/log/brute_force_attack.log"
# ホワイトリストファイルの指定
WHITE_LIST="/root/allow_ip"
# ブラックリストファイルの指定
BLACK_LIST="/root/deny_ip"
#BLACK_LIST="/etc/hosts.deny"
# →TCP Wrapperを使用する場合は後者
# 一時ファイル作成ディレクトリの指定
TMP_DIR="/dev/shm"
# 以降設定不要
# タイムスタンプ設定
LOG_DATE=`date '+%Y/%m/%d %k:%M:%S'`
# 一時ファイル設定
TMP="$TMP_DIR/brute_force_attack.tmp"
TMP2="$TMP_DIR/brute_force_attack2.tmp"
TMP3="$TMP_DIR/brute_force_attack3.tmp"
# フラグ管理
UP_FLAG=0
function_CORE()
{
if [ $1 = "TELNET" -a $TELNET_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
grep "login:" $SSH_LOG | grep "FAILED LOGIN" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
elif [ $1 = "SSH" -a $SSH_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
grep "sshd" $SSH_LOG | grep "Invalid user" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
grep "sshd" $SSH_LOG | grep "Failed password" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
elif [ $1 = "FTP" -a $FTP_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
grep "FAIL LOGIN" $FTP_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
elif [ $1 = "MAIL" -a $MAIL_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
grep "authentication failure" $MAIL_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
grep "auth failed" $MAIL_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
elif [ $1 = "HTTP" -a $HTTP_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
DIR_LIST=`ls $DIR`
grep "error" $HTTP_LOG | grep "File does not exist" | grep -v "$DIR_LIST" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
grep "error" $HTTP_LOG | grep "script" | grep "not found or unable to stat" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep "\.\./" $HTTP_LOG2 | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep "//" $HTTP_LOG2 | grep -v http:// | grep -v https:// | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
elif [ $1 = "DNS" -a $DNS_CHECK = "yes" ] ; then
SYSTEM_NAME="$1"
grep "denied" $DNS_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
else
touch $TMP
fi
cp -p $TMP $TMP2
if [ -s $WHITE_LIST ]; then
WHITE=`cat $WHITE_LIST`
grep -v "$WHITE" $TMP2 > $TMP
cp -pr $TMP $TMP2
else
touch $TMP
fi
if [ -s $BLACK_LIST ]; then
BLACK=`cat $BLACK_LIST`
grep -v "$BLACK" $TMP2 > $TMP
else
touch $TMP
fi
LIST=`cat $TMP`
for IP in $LIST
do
COUNT=`grep -w "$IP" $TMP | wc -l`
if [ $COUNT -ge $NG ] ; then
if [ -s $WHITE_LIST ]; then
if [ `grep -w "$IP" $WHITE_LIST` ] ; then
WHITE_FLAG=1
else
WHITE_FLAG=0
fi
else
WHITE_FLAG=0
fi
if [ -s $BLACK_LIST ]; then
if [ `grep -w "$IP" $BLACK_LIST` ] ; then
BLACK_FLAG=1
else
BLACK_FLAG=0
fi
else
BLACK_FLAG=0
fi
if [ $WHITE_FLAG -eq 1 -o $BLACK_FLAG -eq 1 ] ; then
:
else
echo "["$LOG_DATE"] ""[$SYSTEM_NAMEログ監視] IP をブラックリストへ登録:"$COUNT "回 ["$IP"]" >> $BR_LOG
echo $IP >> $BLACK_LIST
iptables -I INPUT -s $IP -j DROP
UP_FLAG=1
if [ $MAILMESSAGES = "yes" ] ; then
# echo -e "[$SYSTEM_NAME]ログ監視にて、"$COUNT"回の不正なアクセスログを確認。\n対象ホストのIPアドレスをiptables及びブラッ クリストファイルへ追加しました。\n\nIPアドレス [$IP]"| mail -s "$0" $MAILADDRESS
# 自分用設定
echo -e "\n[$SYSTEM_NAME]ログ監視にて、"$COUNT"回の不正なアクセスログを確認。\n対象ホストのIPアドレスをiptables及びブラッ クリストファイルへ追加しました。\nIPアドレス [$IP]">> $TMP3
fi
fi
fi
done
# IPTABLES更新時に設定を保存
if [ $UP_FLAG -eq 1 ] ; then
/etc/rc.d/init.d/iptables save
fi
# 一時ファイル削除
rm -rf $TMP $TMP2
}
# 実行
function_CORE TELNET
function_CORE SSH
function_CORE FTP
function_CORE MAIL
function_CORE HTTP
function_CORE DNS
# 自分用設定
if [ $MAILMESSAGES = "yes" ] ; then
#TMP3ファイルはdropするファイルがないと作成されない。TMP3ファイルが存在しない場合。
if [ -e $TMP3 ]; then
cat $TMP3| mail -s "iptable drop 登録" $MAILADDRESS
else
echo "対象IPアドレスはありませんでした" | mail -s "iptables drop 登録" $MAILADDRESS
fi
fi
rm -rf $TMP3
# 自分用設定 ここまで
----------------------------------------------------
----------------------------------------------------
if [ -s /tmp/hoge.txt ]; then
# 1バイトでも中身があれば何もしない
else
# 0バイトだったら消す
rm /tmp/hoge.txt
fi
-------------------------------------------------
2桁の数字だけを検索したいとき [0-9][0-9]
1桁か2桁の数字だけを検索したいとき [0-9]+
参考:桁数を指定する場合。
\d{2,5}
この場合は、2桁から5桁の間。
-----------------------------------------------------# 1バイトでも中身があれば何もしない
else
# 0バイトだったら消す
rm /tmp/hoge.txt
fi
-------------------------------------------------
2桁の数字だけを検索したいとき [0-9][0-9]
1桁か2桁の数字だけを検索したいとき [0-9]+
参考:桁数を指定する場合。
\d{2,5}
この場合は、2桁から5桁の間。
20170726追加
blackリストには登録されるのに、iptablesファイルには登録されないIPが出てきてしまう。
そうなると、そのIPはいつまでたってもブロックされない。
iptablesコマンドが機能しない時があるってことだよなぁ。
iptablesコマンドで登録するのに時間がかかるのが原因なのか?
2重に使うと2回目のコマンドがきかなくなるのかなぁ。
シェルスクリプトはコマンドの処理が終わらないうちに次のコマンドを実行しないと思っていたけど、そうではないのか。
waitとかsleepで前のiptablesコマンドの実行が終わるのを待たせてから次のiptablesコマンドを使うようにすればいいのか?
ブラックリストを作るのをやめて、iptablesファイルをブラックリストとして使えばいいんじゃないか。
スクリプトを実行する前にブラックリストを全部消して、iptablesファイルからip部分だけをコピーするか。
そうしておけば、iptablesコマンドがたとえ失敗したとしても次回の網にかかる。
20170726以前追加
うまく動かなくなったときはブラックリストとiptablesに登録してあるリストを全部削除するとまた動き出す。
: > /root/deny_ip
(:このコロンは何もしない、空のという意味。)
iptables -F
service iptables save
/etc/init.d/iptables restart
(自分のうまく動かなくなった理由は、たぶん、cronでスクリプトを動かしたときにiptablesコマンドだけが動いていないときがあった。だから、ブラックリストにipアドレスが書き込まれて、iptablesのテーブルにipアドレスが書き込まれない状態になった。ブラックリストに書き込まれたアドレスは重複しないようにiptablesに書き込まないから、iptablesコマンドが動かないときにブラックリストに登録されたipアドレスは永遠にdropされない状態になる。たぶん、多く攻撃してくるアドレスは連動している。dropされると次のipアドレスに移るのだけど、dropされなければずーと、そのアドレスが攻撃を続ける。だから、そのipアドレスが生き続けたのかな?)
上のスクリプトの設定で
BLACK_LIST="/root/deny_ip"
#BLACK_LIST="/etc/hosts.deny"
# →TCP Wrapperを使用する場合は後者
hosts.denyなどを制御しているTCP_wrapperライブラリはかなり古いものだそうだ。
だから、sshdなどがそれをサポートしていれば使えるのだそうだけど、機能しないこともあるらしい。
iptablesはOSのカーネルの一部として動いているので、速くてより広い範囲でサポートしているらしい。
速いことは速いらしいがiptablesは処理が増えてくるとアクセスができなくなったり、いいことはないらしい。
iptablesには、あまり規則がたまらないようにスクリプトを書き換えた方が自分にはいいのかもしれない。
0 件のコメント:
コメントを投稿