sshへの接続を接続してきたクライアントのIPアドレスをgeoipを使って国によって接続を許可・禁止するための簡単なスクリプトと設定の方法です。
事情により外部からの仕事が多くなるため、家のサーバへのsshへ接続するIPアドレスが増えてきて設定が面倒になってきました。そこで、と言う訳だけでもないのですがgeoipを使って国別に接続拒否を行いセキュリティを確保する方法を考えてみました。設定に使ったのはCentOS 5ですが他のディストリビューションでも問題なく動くと思います。
- sshへの攻撃パターン
ざっとログをチェックして攻撃パターンにを見てみると、総当たり式に連続アタックをかけるものと、ありがちな名前を単発かゆっくり試すものがあり、連続なものは以前紹介したiptablesの設定で防ぐことができます。sshへの総当り攻撃をiptablesの2行で防ぐ方法 (blog@browncat.org)
このiptablesの設定は簡単な割りに効果的でアタックを結構防いでくれています。
- ゼロデイアタック
実効性のある対策はあまりないと思います。22番を変更しておけば少しましかもしれません。
後述のaclexec機能のあるtcp wrappersなら可能性を下げることは出来そうです。
hosts.allow, hosts.deny
hosts.allowとdenyには 1)外部ファイルのIPのリストを判断に使う、2)条件により外部コマンドを起動するという機能があり、これを使って特にアタックの多い国に対して拒否IPアドレスリストを自動で作ろうという訳です。(というより私の場合日本以外は全部禁止です)
設定手順
- スクリプトの設置
以下のスクリプトを/usr/local/sbinあたりに配置します。
実行にはgeoipとperlのライブラリが必要です。インストールはこちら(postfixでgeoipを使って国ごとにフィルタするpolicyスクリプト)を参考にして下さい。
check-deny-cc.pl-20070902.txt
- 9/2 許可国処理のバグを直しました
- 8/31 リンク切れ修正
DLしたらcheck-deny-cc.plとリネーム、/usr/local/sbinにコピー後chmod +xして下さい。
ログファイル・設定ファイルの処理がかなり適当なのでアクセス頻度の高いサーバではちゃんとflockとかした方がいいと思います。
拒否IPアドレスのリストは/usr/local/etc/hosts.deny.listとなっておりスクリプト内にハードコードされています。適当に移動するなりなんなりして下さい。他のポートも個別に監視したい場合もそのまま使えますが、リストを分けたい場合には適当にスクリプトをいじってみてください。
- hosts.denyの設定
設定はhosts.allowで全部やることにして、hosts.denyファイルは空にします。
- hosts.allowの設定例
sshd: <localnet> <localaddr> .... : ALLOW
sshd: .com .net : DENY
sshd: /usr/local/etc/hosts.deny.list : DENY
sshd: ALL: spawn /usr/local/sbin/check-deny-cc.pl %a & : ALLOW
説明
接続することがわかっているいくつかのIPアドレスなどは念のため明示的に許可しておきます。同じく絶対こないであろうIPアドレスやドメインはあらかじめ拒否しておきます。外部拒否IPリストにあるIPアドレスは全て拒否します。
残りはスクリプトに引き渡します。
このスクリプトでは 最初の一回目だけ接続を許可します。スクリプト内部でgeoipを使って国コードを調べ、拒否国であればすぐにそのIPアドレスを拒否リストに加えます。今回はやっていませんがdnsblを引いてもいいかもしれません。
欠点その他
このやりかたの欠点はACLのリストが長くなってしまうこと、ゼロデイアタックには無力なことです。また当然ながらホワイトな国からのアタックには無力です。ただ、ログでチェックした限り国内からの攻撃は少なくCN,KRからのアタックが80-90%程度を占めているため効果はあると思います。
今回の方法はsshに限らずimap/popなどtcp wrappersが使えるソフト全てに適用できます。結構アタックは頻繁なのでご注意くださいませ。ちなみに接続元が限られているならこんな面倒なことをしなくても普通に許可・拒否を固定で設定すればOKだと思います。
Debian unstable/Ubuntuのaclexec
試してはいないのですがDebian unstable/Ubuntuのtcp wrappersにはspawnの他にaclexec というコマンドが追加されており、外部コマンド実行結果のリザルトコードで接続の許可・禁止をきめることが出来るようになっています。こっちだとリストを作らずにスマートに処理できる上、一回目の接続から拒否出来るのでよさそうです。