Kotaです。久しぶりにちょっと技術系の事かけよ!!と怒られたので、ちょっと挑戦的なタイトルで書いてみました、が実際そういうシステム、多いんじゃないでしょうか。
セキュリティと利便性は相反するなんて良く言われますが、そこを素晴らしい方法でなんとかしたのがSSLとかSSHとかVPNだと思うのです
安全を確保する事によって逆に便利になる事って多いんじゃないでしょうか?
- モバイルデバイス主流なこんな時代、どんな場所でもメンテナンスせざるを得ないので、特定のIPからのみ接続を許す、、なんてことはできない
- 自宅は固定IPじゃない
- VPNの構築は面倒だし会社のルーターがPPTP/GREやL2TP/IPSecをパススルーしてくれない
結果こうなりがちです
- 現実的に無理だからセキュリティ対策せず全部通す←おい
- もちろん監査なんかしていないしヤバいだろうから考えたくないしたくない
- 心配しないでもSSHは大丈夫だよww -> CVE-2008-0166とかCVE-2008-5161とか
それか
- そもそも会社からしかアクセスできないようにする、自宅禁止、トラブルあったら深夜でも会社こい
- 禁止だから打合せしてたら社内の人間に電話で指示しろ
- そもそもSSHポートなんて空いてたらいくら公開鍵認証でもセキュリティ監査通らないよ、、
あるある、、
あーそいやポートノッキングというのがありまして、200個くらいのポートに順番にアクセスしていくと本当にアクセスしたい接続先のポートが空くのです。(これには批判あり)iPhone用のポートノッキングAppもあるのですがそもそもこれ面倒だしサーバーにも手をいれないとダメだしRFCにされそうにないしというか安くても(バッファローとかはだめだけど)VPN機器とか踏み台借りるなりしたほうがいいんじゃないでしょうか
前置きが長くなりましたが弊社でよく使うiptablesの設定をノウハウの一部を晒します。
iptablesのCHAINとか、SYN,ACK,FIN,RSTで細かく制御とか、調整の難しいlimit-burstは使いません。極めて単純な構造です。
名ずけて IMAPS before SSH!!
前提となる構成
こういう単純な構成をとりあえず想定想定してみました。routerは各ノードに接続ですね。
routerでしかやってませんがtraspearent bridgeでもいけるかもしれないです。
ルーターの先はWANです。LANは省略
2/15 画像間違っていたので差し替えました!
やりたい事
別のサーバーにメールチェックする→IMAPSがESTABLISHEDになっていれば60秒間そのIPから限定、SSHだけ接続を受け付ける
(iPhoneとかはメールチェック15-60分間隔なので3600くらいまで秒数を長くしてもいいかもしれない、ノートとかはIMAPSならほぼずっと接続してるのでいつでもつながるはず)
設定の前置き
設定はこの構成だとRouterに入れるだけで済みます。iptablesのrecentマッチを使います
recentマッチは特定の通信の後からくるパケットを「最近通信したリスト」なのを考慮したフィルタが使えます
とりあえずスクリプト先頭に
#MOBILESSH (mail.example.comにIMAPSで接続しているIPだけ60秒routerに新規SSHコネクション許可)
とかコメントを書いておきましょう
あ、ちなみにip64tablesと書いてあるのはミスではないです。ip6tablesも一括で設定できるように追加した関数です。
1 2 3 4 |
function ip64tables { iptables $* ip6tables $* } |
ip6tablesも忘れないでください!
ウチのサーバーはiptablesでチェインのデフォがDROPにしてるから大丈夫だよーあははwwww → 「ip6tables忘れてるせいで全開だぞそれ」→Σ(`Д´ )マジ!?
実際あります。IPv6のリンクローカルアドレス向けにSSHするには
1 |
ssh fe80::abcd%eth0 |
で余裕で通ります /(^o^)\ ちゃんとip6tablesも設定してください。iptablesとほとんど変わりませんので、、(PREROUTINGやらPOSTROUTINGがない分むしろ簡単かも)
iptablesの設定
まずこいつはmailサーバーIMAPSで接続してconntrack ctstate(stateはオワコン)で接続済みと認識されたものをrecentのMOBILEというテーブルにIPアドレスとタイムスタンプを記録してパケットを通します(暗黙的なルールで通過なんですが記録した後-j ACCEPTしないとREJECTにHitする為)
1 2 3 4 |
ip64tables -A FORWARD -d mail.expample.com \ -p tcp --dport imaps \ -m conntrack --ctstate ESTABLISHED \ -m recent --name MOBILE --set |
既存コネクションで接続終了=FINフラグ立っているパケットがあったらMOBILEリストから削除するルールを追加します(いらないっちゃいらない)
1 2 3 4 |
ip64tables -A INPUT -d router.expample.com \ -p tcp --dport ssh \ -m conntrack --ctstate ESTABLISHED \ -m recent --name MOBILE --remove --tcp-flags FIN FIN |
後続のパケットを対象に、既に確立されてるTCP接続の通信があったらリストを更新、通過させる
1 2 3 4 |
ip64tables -A INPUT -d router.example.com \ -p tcp --dport ssh \ -m conntrack --ctstate ESTABLISHED \ -m recent --name MOBILE --update -j ACCEPT |
MOBILEリストにIPアドレスが存在して、かつ新規でSSH宛で60秒以内なら接続を許可
1 2 3 4 |
ip64tables -A INPUT -d router.example.com \ -p tcp --dport ssh \ -m conntrack --ctstate NEW \ -m recent --name MOBILE --rcheck -j ACCEPT --seconds 60 |
最後に全拒否
1 |
ip64tables -A INPUT -p tcp --dport ssh -j REJECT --reject-with tcp-reset |
まとめてみた。set,remove,update,rcheck とありますが、iptablesは最初のマッチしたら後の部分は無視されるので、実はこの順番が重要です。setはFORWARDにあるので引っかかればどこでもいいですが、recentはremove,update,rcheckの順に実行されるようにしてください。
1 2 3 4 5 |
ip64tables -A FORWARD -d mail.example.com -p tcp --dport imaps -m conntrack --ctstate ESTABLISHED -m recent --name MOBILE --set ip64tables -A INPUT -d router.example.com -p tcp --dport ssh -m conntrack --ctstate ESTABLISHED -m recent --name MOBILE --remove --tcp-flags FIN FIN ip64tables -A INPUT -d router.example.com -p tcp --dport ssh -m conntrack --ctstate ESTABLISHED -m recent --name MOBILE --update --rttl -j ACCEPT ip64tables -A INPUT -d router.example.com -p tcp --dport ssh -m conntrack --ctstate NEW -m recent --name MOBILE --rcheck -j ACCEPT --seconds 60 ip64tables -A INPUT -p tcp --dport ssh -j REJECT --reject-with tcp-reset |
基本の考えはこうです
- のルールはメールサーバー宛で接続済みの接続元IPアドレスはMOBILEというリストに入る(iPhoneとか想定)
- のルールはMOBILE既存コネクションでFINのついた接続終了を検知したらMOBILEリストから削除して通過(モバイルからの接続終了のACKはMOBILEリスト削除してもサーバーは受け取らなければいけないよね?)
- 既に確立/まだ接続されてる(メールでもFINもついてないパケットは1.2をスルーする=既に接続済み)があったらMOBILEリストを更新してリストをアップデート(接続が切断されないよう対策)
- MOBILEのリストにIPがあって新規のSSH接続、リストで60秒以内に追加/更新された記録のあるIPならSSHを許可許可(ここでやっと許可)
- 最後に全拒否(何故DROPじゃないかって?filterdにならずfilterされてる事すら教えたくないので。REJECT –reject-with tcp-resetならnmapでちょっと見たくらいじゃポートが空いてないのとほとんど見分け不可能なはず)
切断の動きはこんな感じ
- SSHを切断したら2のルール、クライアントからのFIN検知ルールがキャッチしてMOBILEリストから削除、それ以降は1-3のルールはスルーしていきなり5のREJECTにあたる(再度メールチェックしないと再接続すら不可。メールチェックとかして再度MOBILEリストに登録されれば新規でのみ接続可能。ちなみに、RST,PSHなど考慮するならさらにルールを追加したほうがいい)
リストについて
- MOBILEリストからイチイチFINを検出して削除する理由はリストが溜まる一方でデフォは100件保存のため
- 気にしないならremoveのルールは必要ありません。どうせsecoundsで有効期限は指定してるし、古いものから勝手に消えます
- removeを入れると二本SSHを張って片方を抜けた時もう一本も切断されてしまう弊害もあり
- cat /sys/module/xt_recent/parameters/ip_list_tot するといくつまで保存できるか設定がわかる
- 100で足りなかったら/etc/modprobe.d/xt_recent.confのoptions xt_recent ip_list_tot=100を増やしましょう
- なにがMOBILEリストに登録されてるか見たい場合は cat /proc/net/xt_recent/MOBILE してみましょう
- アドレスを手動でリストに追加するにはecho +addr > /proc/net/xt_recent/MOBILE
- アドレスを手動でリストから削除するにはecho -addr > /proc/net/xt_recent/MOBILE
- アドレスを全部一括削除するにはecho / > /proc/net/xt_recent/MOBILE
ちょうどSSHのブルートフォース対策でrecentを使う例がよく見かけるのですが、recentの逆利用する感じですね。
もっとわかりやすく!!
いきなりのSSHで接続-→5のルールで拒否される
IMAPSでメールチェックする→1でリスト記録される→
SSHする→2&3は新規のSSH接続には該当しないのでスルー→
4に到着して通信開始→復路のパケットは3がフォローする→
切断したら2が検知、1のリストから削除
具体的にどう使うんだ?
いきなりつないでOK。iPhoneが60秒以内にバックグラウンドでメールチェックしてたらいきなりつながるはず。
→F/WがそのIPからの新規SSH接続を受け付けるからそのままiSSHやPrompt等でSSH操作→終わったら切断
もしつながらなかったらメール立ち上げてから接続してみよう。途中で切断される事はない。 モバイルノートとかMBPでも一緒です。実にシームレス!
ほんとにフィルターされてんの?
では定番のnmapで軽く挙動を確認しましょう
1 2 3 4 5 6 7 |
[root@atack ~]# nmap router.example.com -p22 Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2013-02-10 17:00 JST Interesting ports on router.example.com (10.10.10.1): PORT STATE SERVICE 22/tcp closed ssh Nmap finished: 1 IP address (1 host up) scanned in 0.255 seconds |
filterdでもなく、openでもなく、closedですね
IMAPSサーバーにつないでみます
1 |
[root@atack ~]# openssl s_client -connect mail.example.com:993 > /dev/null |
C-cでキャンセルしてすぐにまたnmapします。(フルコネクトスキャンの-sTならopenになる)
1 2 3 4 5 6 7 |
[root@atack ~]# nmap router.example.com -p22 Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2013-02-10 17:00 JST Interesting ports on router.expample.com (10.10.10.11): PORT STATE SERVICE 22/tcp closed ssh Nmap finished: 1 IP address (1 host up) scanned in 0.124 seconds |
パッっと見はclosedで閉まってるように見えるが、、、
1 2 3 |
[root@attack ~]# ssh router.example.com Last login: Sun Feb 10 16:59:59 2013 from proper-pc.example.co.jp [root@ssh-target ~]# echo from mobile | wall |
つながっちゃうんですね(ちなみにこの後再度接続しようとしてもFINを送ってMOBILEリストから消されてるので速攻蹴られます)
ごめんなさい技術解説がが長くなったからほんとのまとめ
いかがでしょうか?
セキュリティは知恵の輪とか錠前のようなもので、複雑に作れば作るほど破りにくいものです。ただし今回紹介したこの例のものに関しては、ただ機械的にメールサーバーに接続してきた奴は比較的信用できるやつ→ポート開けてもいいかという程度ポリシーなので実際もっとよく考えたほうがいいです。ポート開けたところでその先SSHで公開鍵認証を突破しなければいけませんが。
ただ、利用者に意識させる事無くちょっとした工夫すればF/Wを意識せずセキュリティも比較的高めれると思います。利便性を下げずにポートを閉じたい場合などは有用じゃないでしょうか。踏み台をクラウドに用意するにしても、IPアドレス変更の時とか、踏み台そのものセキュリティとか考えるものはたくさんありますので、、
モバイルデバイスならメールチェックは常にされてるはずなのでそこから信頼できるのアクセス元拾って、、
それを知らないとこから接続したらぱっと見SSHあいてないし、nmapで見てもclosedなってるし、
仮にそこ突破されてもその先は公開鍵認証あるし、メールチェックするだけで空いて、使い終わったら速やかに閉じて許可リストを捨てる、、、なかなかスマートでは??
iptables のstrings match (パケットの中の文字列チェック)とHTTP系を組み合わせるともっと面白い事が出来るかもしれません。
しかしいろんなアプライアンス系のフィルターも使いましたがnetfilter/iptablesの柔軟性は異常ですね、、
ではまた!