2006年04月20日

ssh の brute force アタックパケットの制限 -- DOS 的パケットをフィルタリングする

はてなブックマークに登録

KLab はコンテンツの開発と共に運用も日々担っていますが,その活動の全ての拠点は社内のシステムです.そのため,社のシステムにはいつでも外からアクセスできる必要があります.システムへのアクセスは ssh を使うのですが,この ssh へのアクセスは前記の理由で世界中からアクセスできる必要があります.こういった公開されている ssh のポートへは日々飽きもせずに brute force アタックが繰り返されています.sshd はこのような成功するはずのないアタックであっても律儀にログを出力してくれます.しかしながら,無意味なログの羅列は,重要なログが埋もれる結果になって嬉しくありません.それに,アタックによるログインの試行のために CPU 時間を無駄に費やすのもばかばかしいことです.

ログの出力や CPU 時間の浪費を低減するには,これらの攻撃パケットをフィルタリングしてやればいいのですが,如何せん攻撃元のアドレスは事前に分からず,また攻撃を確認してから攻撃元をフィルタリング対象に追加するにしても,手動でするのではコストが大きすぎます.ログインが連続して失敗したアクセス元を自動でログから探し出してフィルタリングルールを追加する,ということも考えましたが,自動でやる場合,フィルタリング対象のアドレスは,相当の工夫をしない限り,アドレス帯域ではなく個々のアドレスを登録することになりますので,テーブルがすぐに肥大化してしまいそうです.

今回のそもそもの問題は,短時間に繰り返し送りつけられる無意味なパケットです.これがそんなに高頻度ではなく,たまにあるものでしたら許容することもできます.即ち,発想の転換をして,一定期間に受け入れるパケットの数を制限してやれば,大体の目的は達成することができます.Linux のパケットフィルタリングのシステムである netfilter には色々便利な機能が提供されています.今回この目的に合いそうなモジュールは無いかと iptables(8) をつらつらと眺めていたところ,hashlimit というものを見つけました(※1※2).同じようなものに limit というモジュールもあります.この二つの基本的な機能は同じです.即ち,指定した期間に受け入れる(match する)パケットの数を制限できる,というものです.limithashlimit の違いは,limit はこのレイティングが一つの iptables のルールにつき一つなのですが,hashlimit は一つのルールの中でアクセス元/アクセス先の IPアドレスやポート番号の違いによってそれぞれ個々にレイティングを管理できる点です.つまり limit で tcp/22 (ssh のサーバポート)へのアクセスの受け入れレイティングを設定した場合,攻撃的パケットもそうでない真っ当なパケットも一緒に管理されますので,アタックが行われている間は,権限を持った人間もアクセスが制限されてしまいます.一方 hashlimit を使えば,アクセス元毎にレイティングを管理できますので,アタックが行われている間でも,権限のある人は正常にアクセスすることができます.
(※1 元は dstlimit という名前だったようです.その名残が iptables に残っています.)
(※2 後から検索してみると,recent モジュールを使ったが多いようでした.おおむね同じことが実現できるようですが,hashlimit を使う方がルールの数が少なくて済みそうです.)

ということで,hashlimit を使って攻撃パケットの受け入れを抑制してみました.今回追加したルールは次のようなものです.

iptables -A INPUT -j ACCEPT -p tcp --dport 22 \
-m state --state ESTABLISHED,RELATED
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
(iptables のバージョンは,1.3.0 以後のものを使ってください.hashlimit のカーネルモジュールのコードは,2.6.10 以後でしたらソースに入っているようです.)

一つめのルールは,connection tracking を使って,一度受け入れたコネクションに続くパケットは全て受け入れるためのものです.これが無いと tcp/22 のパケットの受け入れが全てレイティングされてしまいますので,正常なアクセスもまともに動作しなくなってしまいます.二つめが今回の目的のものです.指定のレイトの範囲内の数のパケットは,このルールに合致して受け入れ(ACCEPT)ます.詳細は後述します.3つめのルールは,2つめのルールに合致しないパケット,即ちレイトを超えたパケットが合致して,捨て(DROP)られます.


さて,2つめのルールにある hashlimit モジュールのオプションの意味ですが,一つずつ解説していきます.

  • --hashlimit 3/minute
     パケットの受け入れレイトを指定します.即ち 1分につき3パケット(=20秒に1パケット)受け入れます.指定の時間内に新たなパケットがこなければ,繰り越されます.繰り越しの最大値は次の --hashlimit-burst で指定します.
  • --hashlimit-burst 3
     指定の時間にパケットが一つもこなければ,その"受入れスロット"は繰り越されますが,その繰越しの最大値を指定します.ルールが初期化された直後は,この"受入れスロット"は一杯まで貯まっています.即ち一番最初は連続して 3パケットまで受け入れます.その後,--hashlimitのレイティングに従います.
  • --hashlimit-name out_ssh
     このルールで使用する hash テーブルの名前です.テーブルの内容は,/proc/net/ipt_hashlimit/ の下にできるファイルで確認できます.ここで指定する名前は,proc ファイルの名前にも使われます.即ち,この場合でしたら /proc/net/ipt_hashlimit/out_ssh になります.
  • --hashlimit-mode srcip,dstip
     レイティングを管理する単位を指定します.この例ではアクセス元の IPアドレスとアクセス先のアドレスの組みが同じパケットに対して,それぞれにレイティングを管理します.指定できるのは dstipsrcipdstportsrcport で,カンマで区切って必要な分だけ指定します(iptables の1.3.5 付属の man ページで述べられている,このオプションで指定できる値の一覧は間違っているようです.iptables -m hashlimit -h とすれば,実際に使用できる値が表示されます).
  • --hashlimit-htable-expire 60000
     レイティングの管理レコードが保持される時間を,ミリ秒で指定します.最後にパケットが到達した時からこの時間を過ぎるとレコードは破棄され,リセットされます.ですので,--hashlimit で指定した時間よりも短い時間をここで指定すると,意図したレイトでの制限が実現できないことになります.今回のルールの場合,--hashlimit が 3/minute なので,20000(20秒)未満の値を指定してはいけません.
    この値を 20000(20秒)にした場合,連続してパケットが到達している状況では20秒ごとに1パケットを受け入れますが,例えば最後のパケットが到着してから21秒間新たなパケットが到着しなければ,(レコードが破棄されて)次の瞬間から新たに --hashlimit-burst の数のパケットを受け入れだします.
    /proc/net/ipt_hashlimit/ の下のできるファイルの1カラム目の値は,この値です.

最後に,今回この設定をしたマシンのカーネルと iptables のバージョンはそれぞれは 2.6.14.6 と 1.3.5 だったのですが, --hashlimit-htable-expire 等の値を変更しようとして,iptables で hashlimit を使うルールをいったん削除した後,新たに数値を変えてルールを追加したりしたのですが,更新されないことがありました.どういう状況で更新されないのかまだ追い込み切れていないのですが,パラメータを調整する場合は留意する必要があります.この問題を回避するには,hashlimit を使用するルールを全て削除した後,モジュールを modprobe で unload すれば,確実に値を更新することができます.最新のカーネルでは試せていませんので,もしかすると直っているのかもしれません.


<2006/05/02追記>
どうやら,/proc/net/ipt_hashlimit/ の下のファイルが残っていると,iptables 上のルールを差し替えても,新しい値が反映されないようです./proc/net/ipt_hashlimit/ の下のファイルは,-D オプションでその --hashlimit-name を持つ全てのルールを削除すれば,消えます.ルールの差し替えを -R オプションで行うと,テーブルは残ったままになり,新しく差し替えたルールの値では無く,古いルールの値に従って動作するようです.



参考文献:


klab_gijutsu2 at 22:41│Comments(0)TrackBack(2)

トラックバックURL

この記事へのトラックバック

1. [linux][net] SSHのbrute forceアタックを撃退 その2  [ (ひ)メモ ]   2006年04月21日 20:04
[http://d.hatena.ne.jp/hirose31/20050920/1127208718:title=以前紹介したiptablesのrecent module]より、 DSAS開発者の部屋:ssh の brute force アタックパケットの制限 -- DOS 的パケットをフィルタリングする で紹介されてるhashlimitの方がよさげっぽい。
2. sshのアタック対策=ポート番号変えるのも手っ取り早い  [ Web屋のネタ帳 ]   2006年08月17日 16:26
たまには技術TIPSなど。 自宅サーバなりレンタルサーバなりに対してリモートで...

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
KLabについて
KLab株式会社は、信頼性の高いクラウドサービス、ソフトウェアパッケージ、自社で企画・開発したソーシャルアプリやデジタルコンテンツを提供しています。
Blog内検索
最新コメント
最新トラックバック
Archives