2016年1月1日金曜日

たくさんiptablesに直接登録するよりipsetを使うと早いよ

あけましておめでとうございます。
どうか今年も皆様がお健やかに過ごせますように。

さて、いきなりですが前置きです。

最近、本当に久方ぶりでwindows上の開発環境を10日くらい使い続けてつくづく思ったのは、履歴管理にgitが便利だと思ったことです。

自宅では、(それこそ本当に)膨大な数のソースがCVSで管理されています。
CVSで管理するようになってからでこれですから、しかもCVSで管理する必要がない程度にすぐできちゃうものは登録されていませんから、よくこれだけ書き散らしたものだと感心します。
ですが、EclipseはCVSクライアントとして大変優秀でとても使いやすいのですが、VisualStudioからとなると、こりゃあちと考え物です。MSVSはVSSより最近はgit押しラシイ。

確かに使ってみてgitの素晴らしいところと感じたことは、サーバとつながらない環境でも手元でコミットでき、コミット記録を残せるところがスバラシイ。
移動先で編集しコミットした結果を丸ごとサーバにつながる環境に帰ったらサーバに反映させることができる点は、さすがにCVSではまねのできない芸当です。

おまけに、GitBucketというスバラシイソフトがあることを知り、まるでGitHub的な環境がCentOS5でもあっさり構築できました。Tomcatのwebappsに放り込むだけでおしまい。

・・・ですが、ここからが私の馬鹿なところで、「今回はCentOS5でもやっぱりなんとかなったけど、今後どんどん面倒になってくよなぁ・・・」とか小人閑居してCentOS7への移行作業を昨日はじめてしましました。

しんどいです。

何もかもが、みな新しい。

言語のバージョンが変わっているとかプリンタやスキャナのドライバが入らないとかの問題を現在一つ一つクリアしている最中です。

そんな問題の中の一つにsystemdがあります。
個人的には別に起動処理なんか並列処理なんぞしなくていいし、そもそも再起動なんかしないんだからほっとけよ、とは思いますが、そこはそれを採用しているディストリビューションを採用した自己責任ですから対応しなければなりません。

問題は、自作のネットワークに接続する前にうちに過去に攻撃した者のIPアドレスをiptablesに登録するというスクリプトでした。
簡単にというと、syslogでauthpriv.*;auth.*をfifoにも流しておいて、loginに失敗したやつを速攻でiptablesでdropさせるというものです。もちろん、http(s)やimap4とかも記録します。

すると、10年以上も経過するとこれが数万という数になります。
加えて、ARINやAPNICから得たリストから、やばそうな国からのパケットもdropしているのでえらい数です。

起動してからも攻撃はどんどん来るので、その都度iptablesに追加されていきます。

こいつらを起動時にiptablesに再登録しなければなりません。
もしもこれを

iptables -A INPUT -s 10.20.30.40 -j DROP

とかいう感じで一つ一つ丹精を込めた職人のスクリプトでもって登録しようとするとどうなるかというと、数十分かかります。

今まではこれでやってきました。

工夫しろ、というご意見はごもっともで、当然何とかすべきだったのですが、まずめったに再起動しないので放置していたうちに年月が流れ、ついにここまでになってしまったのと、いままで放置していたという時点で既に恥の記録なので、どうかこのブログの趣旨たる「恥を記録してゆく」という観点から、どうかご容赦いただければ幸甚です。

そこで、いくつか考えられることは、
  • 起動時はifupしないでおいて、登録処理が終わった段階でifupする
     → 本質的に何も変わってない。OSにログインできる時間が早まるだけが取り柄。
  • そもそも登録しない
     → ノーガード戦法は自分に強大な報復能力があるときのみ意味がある
  • 登録処理を早める
といったところだとおもいます。
といったわけで、大量のiptablesのエントリをipsetでもって事前にテーブル化しておいて使ってみようというお話です。

使い方は簡単です。
テーブルを作り、そこにアドレスを登録し、そのテーブルをiptablesに渡す。これが基本です。
テーブルの作成にipsetを使います。

CentOS7の場合ですと、
yum install ipset
でインストールできます。

まず、テーブルの作成ですが、ネットマスク付きのIPアドレス(192.168.0.0/24のような書式です)の場合は、
ipset create IGNORE_WITH_NETMASK hash:net
というように、 hash:netを指定します。
ipアドレス(192.168.1.100というような書式です)だけでいいなら、
ipset create IGNORE_IPADDRESS hash:ip
というように指定します。
ネットマスク付きはARINなどから取得したデータを登録するテーブル、IPアドレスのみのテーブルはうちに攻撃をかけてきたIPアドレスを登録すればいいので、この2種あれば今回の要件には十分です。

テーブルを作ったら、そのテーブルにそれぞれのデータを追加してゆきます。
それぞれ、
ipset add IGNORE_WITH_NETMASK 192.168.0.0/24
ipset add IGNORE_IPADDRESS  192.168.0.100
といった具合です。ループで回すと、うちの環境だと、およそ数十秒かかります。

そうして完成したリストを、今度はdropしてもらうためにiptables に渡してあげます。
一つ一つアドレスをiptablesに教えてあげる形式だと数十分かかる代物が、もう数十秒で完成されていますから、あとはもう一瞬です。
iptables -I INPUT -m set --match-set IGNORE_WITH_NETMASK src -j DROP
iptables -I INPUT -m set --match-set IGNORE_IPADDRESS  src -j DROP
こんな書式になります。
まず、-m set ですが、ipsetがsetというiptable-extensions名なのでこれは決めうちです。要は拡張パックの固有名です。
--match-set がipsetの固有のパラメータになり、第一パラメータにテーブル名、第二パラメータにそのテーブルをどういう風に使うか、ということを指定します。
今回は、サンプルだとIGNORE_WITH_NETMASK に登録されたIPアドレスをソースとして利用するのでsrcとなっています。

これで数十分から数十秒へと飛躍的な性能向上が図られたというわけです。

随分長くなってしまいましたので、これをsystemdを採用するCentOS7でどうやってネットワークにつなげる前にテーブルを登録するのか、というのは次のエントリで扱いたいと思います。

なお、作成したテーブルの削除は下記の手法で削除できます。
 ipset destroy IGNORE_WITH_NETMASK
 ipset destroy IGNORE_IPADDRESS
どうか皆さまにおかれましては良き年になりますように。

0 件のコメント:

コメントを投稿