Ciscoルータ設定作業めも2【認証/telnet/ssh編】
勉強用にサービス統合型ルータ Cisco 1812-Jを購入し、設定を行ってみた際のめもです。学生の身には少し手痛い出費でしたが、色々勉強になりました。
コンソール接続、telnet接続、ssh接続と、それぞれのパスワードの設定の仕方について記載してます。
passwordコマンドとsecretコマンドの違いとクラックについても少し触れています。
もし何か記載に間違いがあれば指摘していただければ幸いです。
1. 用語
2. 作業環境
2.1. ノード
・Ciscoルータ 1812-J
・コンソール用PC(Windows 8.1、Tera Termインストール済み)
2.2. ケーブルなどの機材類
・コンソールケーブル
・LANケーブル
・LANアダプタ搭載USB3.0ハブ
3. 作業内容
3.1. I/FへのIPアドレス割り当て
後の作業のため、事前にIPアドレスを割り当てておきます。
1812-JのFast Ethernet 0に192.168.0.100/24を割り当てます。(※前回説明したので省略形で記述)
Ethernetポート0のI/Fコンフィギュレーションモードへ移行し、デフォルトではI/Fはダウンしているので、no shutdownで立ち上げます。
さらにip address [IPアドレス] [サブネットマスク]でIPアドレスを割り振っています。
show running-configで設定確認。
PCに静的に192.168.0.101/24を割り当ててルータのFE0に接続し、pingを飛ばしてみます。
ちゃんと届いてますね。
3.2. ルータへの接続方法
Ciscoルータへアクセスするための接続方法を確認します。
背面のポートを見ればわかりますが一応コマンドでも確認します。
特権モードに入り、show line。
Tty(Tele-typewriter)の種類が3つ表示されています。
CTY
コンソールポート経由の端末への接続。
この回線は"line console 0"で表す。
AUX
AUX(補助)ポート経由の端末への接続。
この回線は"line aux 0"で表す。
VTY
LANポート経由のtelnet/sshを受け付ける仮想端末への接続。
複数のtelnet/ssh接続を同時に受け付けられるようにVTYも複数個あります。
この回線は"line vty 0 4"等の表し方をします。
また、本機にはありませんでしたがもう1種類存在します。
TTY
モデムとの接続で利用されます
3.3. パスワード設定
3.3.1 パスワード設定方法
パスワードの設定方法には以下の2種類の方法があります。
①passwordコマンドで設定する方法(非推奨!今回は歴史をなぞる意味で一部使用していますが実際は②の方法を利用してください。)
②secretコマンドで設定する方法(推奨。今回は3.3.4の項目で手順説明。)
①の場合、configファイルに平文でパスワードが載ってしまうので、service password-encryptionコマンドを実行して暗号化を施す必要があります。しかしながら暗号化後のテキストから簡単に元のパスワードをクラックできてしまいます。
例えば"crack type 7 password"等とGoogle検索するとクラックのためのWebサービスが見つかります。実際にパスワードをクラックしてみた結果もこのブログに記載します(次の3.3.2の項目で記載)。
②の場合、パスワードのMD5一方向ハッシュ値を生成してconfigファイルに記載します。ハッシュ値に一致する文字列を特定することは①よりは難しいので、こちらの方がベターとされています。一応レインボーテーブルを使って推定するようなことは可能なので、これを少し意識してパスワードを作成した方が良いかも…
ちなみにpasswordコマンドとsecretコマンドの両方が設定された場合、secretコマンドのパスワードが優先される模様。
参考サイト様*1
3.3.2 パスワードのみの認証
前述した各接続方法に対してパスワードを設定できます。ここでは、試験的にpasswordコマンドでパスワードを設定してみます。
以下の画像では、まず特権モードからグローバルコンフィギュレーションモードへ移動。
その後、line console 0でコンソール回線のコンフィグモードへ移動しています。
※ここで仮にauxのコンフィグをしたい場合はline aux 0などとすればよいです。
さらにパスワード(ここではtestpass)を設定しています。
その後loginで、接続時のパスワード認証を有効にしています。
show running-configで、設定が反映されたか確認してみます。
該当部分は以下です。
ここまででctyにはパスワードが設定できましたがauxやvtyはパスワードが設定されていません。しかし同様の手順で登録できます。
exitしてctyアクセスを試みるとパスワード入力を求められる。
無事パスワード認証が機能している。
※パスワード暗号化は次の3.3.3にて
3.3.3. telnet接続を行う
vty接続に対してパスワード認証をオンにするとtelnet接続が可能になります。
※ただし特権モードにパスワードが設定されていないとtelnet接続できない模様
次の画像では、まず特権モード移行時のパスワード(testpass)をpasswordコマンドで設定しています(実運用時はsecretで行うこと)。そして、vty接続に対し、パスワードだけでなく、ユーザ名(user1)とパスワード(testpass)の組を設定しています。
※今回は便宜上パスワードをtestpassで統一していますが、実際は異なるパスワードを設定してください。
username [username] password [password]でユーザ名とパスワードを登録します(もしちゃんとsecretを使う場合は、passwordの部分をsecretに置き換えるだけでOKです)。
vtyのコンフィグモードへ入り、login localを実行することでユーザ名/パスワードをログイン時に利用できるようになります。
show running-configで設定が正しく済んでいるか確認します。
まずはユーザ名とパスワードの組みについて見てみます。
正しく設定されているようですね。passwordの後ろの数字0は、パスワードが平文であることを示しています。この数字が7の場合、パスワードが暗号化されていることを示します。今回は平文でそのまま表示されています。
もし暗号化した方が良いのでservice password-encryptionを実行してみます。
これを実行後、再度show running-configでuser1のパスワードを確認。
数字は7になっており、パスワードもtestpassではなく暗号化されたものになっています。
vtyの設定を見てみると、次のように変更が適用されていました。
transport input [all/none/telnet]はリモートアクセスプロトコルを制限するもの。allの場合すべて許可し、noneの場合すべて拒否し、telnetの場合はtelnetのみ許可します。今回はallなのでtelnetを許可しています。
PCでTera Termを起動し、ルータにtelnet接続を試みます。
ユーザ名とパスワードが要求され、登録した内容で認証できています。
ちなみに、前述したとおりpasswordコマンドで生成された平文をservice password-encryptionで暗号化したものは簡単にクラックできます。試しにクラックサイト*2に暗号文を入力してみます。
そして送信すると...
確かに平文testpassが特定されていますね。
ここまで非推奨のpasswordコマンドでの設定を行ってみましたが、secretコマンドで行いたい場合基本passwordの部分をsecretで実行すれば良いです。次の3.3.4からは実際に推奨のsecretコマンドでのパスワード設定の方法を記述します。
3.3.4. ssh接続
secretコマンドを用いてssh認証のための設定を行いたいと思います。
下記ではユーザとパスワードのセット(admin/testpass)を仮想端末の接続(line vty 0 4)に設定しています。
さらにsshで必要なRSA暗号鍵のための設定を行います。
まずはホスト名とドメイン名を設定します。(これを設定していなければRSA鍵を生成できないようです。)
次にRSA鍵を生成します。生成にあたって鍵のサイズを要求されるので入力します。ここでは2048としています。
次にsshのバージョンを指定します。
もし特権モードのsecretが設定されていなければ以下のようにenable secret [password]で行います。今回はパスワードをsecretpassとしています。
これで設定は終了です。
それでは外部から実際にアクセスしてみましょう。
Tera Termからユーザ名とパスワードでssh接続。
実際に特権モードへも移行できました。
さいごに
今回はCiscoのサービス統合型ルータで初歩的なコマンドの実行を行いました。今後も引き続き設定と動作検証を行っていく予定です。
あともしFW筐体(FortiGate 50E)を入手できればその設定の様子と攻撃の試験を行ってみる予定です。学生なので資金不足なのがつらいですが、買えるように頑張ります。
SlideshareからスライドをDL/画像DL【wget,python】
Slideshareのスライドをオフラインでも手元で確認したいことがあったので、手元にPDFとして保存できる手順をまとめました。(DLボタンが無い場合のDL方法です)
手順1.画像のクローリング
Slideshare上のスライド画像ファイルを、クローラを使ってDLします。ここでは、クローラとしてwget、言語としてPythonを用いることにします。
以下のコードを実行すれば画像を取得できます。
#!/usr/bin/env python #coding:utf-8 import os for x in range(ページ数-1): cmd = "wget https://image.slidesharecdn.com/該当パス" + str(x+1) + "固定番号.jpg" os.system(cmd)
具体例
例えば、
ChordアルゴリズムによるDHT入門からスライド画像ファイルをDLする場合は以下のようにします。
#!/usr/bin/env python #coding:utf-8 import os for x in range(335): cmd = "wget https://image.slidesharecdn.com/20110219chord004pict-110219072300-phpapp02/95/chorddht-" + str(x+1) + "-728.jpg" os.system(cmd)
実行時は、sudoなりをつけてwgetが通るようにしておきます。
手順2.DLした画像ファイルをPDFとして統合
ImageMagickを使って複数の画像をPDFへ変換します。
※参考サイト様:*1
以下のコマンドを実行すればOKです。
sudo convert 'ls -v' *.jpg 出力ファイル名.pdf
最後に(補足)
同様の手順(クロール技術)を応用すれば、Slideshare以外のサイトからファイルを取得してまとめることもできます。
ただし、時間当たりのクローリング回数が多いと対象サーバに思った以上の負荷を与えかねないので注意したいところです。
SSH設定
SSHを使えるように設定しました。
ただ、よく攻撃対象になる22番ポートの使用を避け、公開鍵認証でのアクセス設定を行いました。
SSH設定
使用ポートの変更
botのスキャン活動や不正アクセスの際、よくデフォルトのsshポート(port 22)への攻撃が行われます。そのため、今回扱うサーバではssh待ち受けポートを22以外へ変更しました。
やり方は非常に簡単です。
- /etc/ssh/sshd_configのPortの項目を22から待ち受けたいポート番号へ変更
- service sshd restartで変更適用と再起動
- 新たに設定したsshポートを通す様にiptables変更
公開鍵認証を設定
ユーザ名、パスワードのみでの認証だと、総当たり攻撃で突破されるリスクが高いので、公開鍵認証を採用します。
自分はサーバとしてLinux、クライアントとしてWindowsを使っています。
また、sshクライアントとしてWindows上でTera Termを使っています。Tera Termには鍵生成の機能と、公開鍵をSCP(Secure Copy:SSHを使ってファイル転送を行う機能)を使って配布する機能も持ち合わせているので簡単に認証の準備ができます。
今回はこちらのサイト様*1を参考に行いました。
こちらに書いてある通りにやれば問題ないです(解説丸投げ)。ちなみに、パスワード認証では認証先のログインパスワードなどを用いましたが、公開鍵認証時は鍵以外にも生成時に設定したパスフレーズを用います。
※公開鍵認証の簡単な説明
秘密鍵ファイルはid_rsa、公開鍵ファイルはid_rsa.pubとなります。秘密鍵はクライアントが使い、公開鍵はサーバが使うことになります。秘密鍵と公開鍵は暗号化と復号化のための対になっており、秘密鍵を使って暗号化したファイルは公開鍵を使ってしか復号化できません。従って、サーバ側は受け取った暗号を復号化できれば、相手がちゃんと秘密鍵を持った相手だと判別できるわけです。
公開鍵認証以外の認証を無効化
このままだと、公開鍵認証だけでなくパスワード認証もまだ使えます。
なのでパスワード認証を無効化します。
- /etc/ssh/sshd_configを開きます
- PasswordAuthentication yesをPasswordAuthentication noへ変更
- service sshd restartで変更適用と再起動
さいごに
これで比較的安全にSSH接続できると思います。ただ、秘密鍵の取扱いには気をつけたいですね。
ポート開放プロセスの特定/不要ポートの閉鎖
firewallでフィルタリングしていても、開放ポートが悪用される可能性も0ではないので、利用しないポートはなるべく閉じたいものです。
そこで今回は開いているポートを調べて、それがどのプロセスによって開放されているのか、そして不要であれば閉じるという過程を記して置こうと思います。
開放ポートの調査
netstat -antuで開放しているポートを調べます(t,uはtcp,udpに絞るオプションです)。中でも、意図的に開放した覚えのないポートを以下に表示します。
このうち、68はDHCPクライアント用なのでいいとして、他のポートは何故使われているのか調べることにします(何やら多いですね…)。
ポート開放プロセスの特定
root権限でlsofコマンドを使います。(root権限でないとうまく取得できません)
※lsofコマンドについて:manに書かれていることを簡単に説明します。lsof(list open files)はプロセスによって開かれているファイルの情報をリスト表示するコマンドです。-i オプションを使うことで、引数に指定したネットワークアドレスやポートで待ち受けているファイル/プロセスをリスト表示することができます。COMMANDはプロセスに関連付けられているコマンド名をあらわしています(デフォルトだと上限9文字)。PIDはプロセスIDで、FDはファイル記述子(read,writeなどで指定することで情報を読み書きできる番号)です。
各ポートを使っているプロセスを実際に表示させてみた結果は以下のとおりです。
rpcbind,rpc.statd,cupsd,mysqldの4つがヒットしました。
対処(閉鎖)
mysqldについて
まずmysqldについてですが、現在のところリモートから接続する機会はないので閉じておきます。閉じ方は検索するとすぐでてきます。今回参考にしたサイト様はこちら*1です。/etc/my.cnfファイルの[mysqld]配下に
を追加して、service mysqld restartで完了です。
外部からのmysqlへの接続を遮断しつつ自分だけ遠隔から利用したい場合、mysqldで3306待ち受けを行いFWでポート3306宛は落とす設定にしておいてSSHポートフォワーディングでつなぐのが良いんですかね。この辺も後でちゃんと理解しないといけませんね。
cupsdについて
CUPSはUNIX系OSで印刷を行うためのサービスのようです。このノードからの印刷は行わないのでこれも閉じます。このためにはサービスを停止して、マシン起動の度に自動で立ち上がらないようにしておく必要があります。今回参考にしたのはこちら*2
- サービスを特定
- サービスを停止
- 再起動時の起動を停止
rpcbind,rpc.statdについて
ちなみにこれらのrpc絡みのプロセスはNFSのためのもののようです。スクショは張りませんが起動サービスとして、netfs,nfs,nfslock,rpcbind,ypbindが出てきました。NFSサーバとして利用しないので、これらを上記と同様に停止させ再起動時に起動しないようにしました。
結果
最後にnetstat -antuの結果を示します。
かなりすっきりしました。
※CLOSE_WAITになっているのは前の記事で書いたgnomeのものです。
ApacheのIPv6/IPv4の射影仕様について
Apacheを立ち上げて、待ち受けの様子をnetstatで見たところ、IPv6アドレスしか表示されていませんでした。しかしながら、外部からIPv4アドレスを指定してもちゃんとアクセスできたので、どういう仕様になっているのかを調べてみました。
状態確認
netstat -an | grep tcpの結果は以下のとおりです。
IPP(631)とssh(22)、SMTP(25)についてはIPv4,IPv6それぞれのアドレスで待ち受けています。しかしながら、Apacheが待ち受けているはずのHTTP(80)に関してはIPv6アドレスしか表示されていません。IPv4も考慮して設定したのにもかかわらずなぜ表示されていないのでしょうか?
これを調査しました。
※ちなみに0.0.0.0,::はそれぞれIPv4,IPv6において自分自身を表すネットワークアドレスで、任意の自身のアドレスで待ち受けていることを表します。127.0.0.1,::1はローカルループバックアドレスで、自分自身から自身のサービスを利用する際に指定するアドレスです。
調査
各ノードのアドレスは以下です。
・クライアントのIPv4アドレス:192.168.11.3
・WebサーバのIPv4アドレス:192.168.11.6
クアイアントからこのWebサーバにIPv4でブラウザアクセスしたところ、netstatに以下のような状態が追加されました。
今回はコネクションを3つ張ってコンテンツを得ており、それぞれIPv4アドレスを指定してもコネクションを張れていることがわかります。ブラウザでも実際のコテンテンツを取得で来ました。
ただし、netstatでのコネクションの実際のアドレスを見てみると、いわゆるIPv4-mapped IPv6アドレス/IPv4射影IPv6アドレスと呼ばれるアドレスに変換されていることがわかります。つまり、IPv4アドレスでアクセスしてもWebサーバ側のソケットではIPv6アドレスとして処理されています。IPv4射影アドレスとは、一般的に先頭80bitが0、続く16bitが1、残りがIPv4アドレスであるIPv6アドレスです。これはIPv6ノードが、IPv6をサポートしていないIPv4ノードと通信をおこなうための仕組みです。
Webサーバ内部での処理はIPv6アドレスで行われていたわけですが、実際にリンクへ流すパケットでは双方がIPv4アドレスを使っていました。
結論・考察など
射影アドレスを使っていることから、恐らくApacheはIPv6ソケットで通信を待ち受けており、IPv6に対応していないノードからIPv4でアクセスされても処理できるように射影アドレスに変換してサーバ内ではIPv6アドレスとして処理を行うようになっているようです。
つまり、netstatなどの見掛け上はIPv6でしか待ち受けていないように見えてもちゃんと内部的にはIPv4でも処理しているみたいです。(射影がnetstatの結果にどのような形で反映されているのかがなんとなく掴めました)
関連サイト
後日同様のことを調べてらっしゃるサイト様を探したところ、以下のQiita記事が見つかりました。
qiita.com大体疑問点は同じなのですが、ちゃんとnetstatのコードを読んでいらっしゃって、射影アドレスが反映されるフラグがIPV6_V6ONLYにあることまで突き止めていらっしゃい ました。記事によるとこのフラグが真だとIPv6とIPv4が分けられるようで、上記のsshやIPPはこれに当たるようです。逆に偽だと今回のように射影されて透過 的に処理できるようになるとのことでした。
また、海外でも同様の疑問をお持ちの方がいらっしゃったようです。
askubuntu.com今回の仕様はIPv6-IPv4フォールバック(IPv6でアクセスできなかったらIPv4でアクセスする)機構の特徴があること、それから、IPv4ノードとIPv6ノードでも互いに通信できるような互換のためにありなおかつそれぞれのソケットをもつ問題を解消するためにあると解説されていました。
身に覚えのない通信先とCDNについてのめも
前置き
netstatで各ポートの様子やコネクションの状態を確認していたら、見知らぬアドレスに対するハーフクローズの通信を見つけました。ずっとハーフクローズだしなんぞこれ。。。マルウェアの類だったら嫌だなぁ。。。ということでちょっと調べてみたのでメモしておきます。
また、間違っている認識や記述があれば指摘していただけると幸いです。
状態
80番ポートを使った通信をnetstatで確認したら下記のような表示になりました。
内部ホストから外部の110.232.152.33に対してCLOSE_WAITの状態を保っていました。CLOSE_WAITは、TCPの状態遷移図における状態の1つです。このホストがコネクションクローズのためのFINを受け取りACKを送ったものの、その後のFINを送信できていない状態で、これをハーフクローズと呼びます。ハーフクローズの状態があまりにも長く続いているので不審に思いました。
そして110.232.152.33とはどこぞやということで名前解決してみた結果…
回答から、deploy.akamaitechnologies.com配下のノードであることがわかりました。
通信相手についての調査結果
akamaitechnologies.comということはあのCDNを展開しているAkamaiさん…?Akamaiさんのドメインでよく知っているのはakamai.comだけど、コンテンツ配信用のドメインはそういえばなんなんだろうなぁ…などと思いつつ調べてみると、国内外問わずいろんな質問サイトに同様の質問がありました。この回答によると通信相手はやっぱりAkamaiさんのコンテンツ配信に関わるノードのようです。今回これを観測したホストはブラウジングなどクライアントとして用いていたので、その際利用したものと思われます。
AkamaiのCDNの仕組みは、オリジナルサーバを管理しているADNSのCNAMEにAkamaiのエッジサーバのドメインを登録することで、クライアントがエッジサーバ(オリジナルサーバの内容をキャッシュしたサーバ)からコンテンツを取得するようになるというものです。エッジサーバは、自身に適切なキャッシュがなければリバースプロキシとして機能し、リクエストをオリジナルサーバへ送ってキャッシュを得ます。
今回の110.232.152.33はCDNのエッジサーバってことで良いんでしょうか…?ポートスキャンをかけてみると80以外にも443が開いていました。
また、110.232.152.33についてwhoisで調べてみると以下のことがわかりました。
どうやら今回はSo-netのASにAkamaiのエッジサーバがあるっぽいですかね。実際にAS2527(So-net Entertainment Corporation)からAkamaiコンテンツが吐かれているというソースもありました*1。ここ*2でも確認しました。あれ、でもそういえばSo-netって自社でCDN持ってるんじゃなかったっけ、違ったっけ…と思いつつ調べていくと、一応、自社CDNとAkamai CDNなどによるマルチCDNといった構成を取る事業者さんもいるらしいです。その辺りも上記のJANOGにおける解説がわかりやすいです。
蛇足ですが、今まではほとんどのバックボーントラヒックはTier1を介していたのですが、それが近年では、Tier1までいかずHyperGiant(Google,Akamai)やCDNを構成するISPを介すようになった件なども上記注釈リンクにて説明されていています。トランジットへ通行料を払うよりCDNを介する方がコストも安上がりで応答性も上がったので仕方ないですね。
コネクションについて
ハーフクローズ状態がずっと続いているのが気になります。ハーフコネクションは、コネクション確立中に片方のホストが落ちた場合などに発生します。今回は、こちら側がFINを受け取った後ACKを送信してダウンしたのかな…と思ったのですが何やらホストが元気でもCLOSE_WAITとなっている模様。もしかしたらAkamaiのエッジサーバの仕様が関わっているんでしょうか?状態が維持されたままなのはkeepaliveが変に働いているせいかとも思ったのですがwiresharkでパケットをキャプチャし続けた結果keepaliveは確認できませんでした(KeepAliveが有効なものの、KeepAliveパケット送信前に何らかの理由で接続が切れている…?)。
AWSのElastic Load Balancing(ELS)では、HTTP KeepAlive時間をLBのアイドルタイムアウト時間よりも大きい値にしなければ、LBのインスタンスへの接続を確実に閉じられない=ハーフクローズなどが生じる可能性があります。このような形で、通信先のいずれかのノードにコネクションを正常に閉じられない問題があるのかもしれません。
ちなみに110.232.152.33とはOS起動後すぐにコネクションを確立し、さらに一定時間おきにCLOSE_WAIT,LAST_ACKでコネクションを閉じ、また3wayハンドシェイクを経てESTABLISHEDへ移行するといった状態遷移を繰り替えしている模様。
通信を行っているプロセス(原因)
継続的にパケットをキャプチャしていると以下のようなHTTP GETリクエストを投げていました(というか110.232.152.33宛のリクエストで確認できたのがこれだけでした)。
0.013133732 192.168.11.6 -> 110.232.152.33 HTTP 378 GET /cgi-bin/mgetmetar.pl?cccc=KBOS HTTP/1.1
恐らくgnomeの天気概況機能のためのリクエストだと思われるので、不審(マルウェアの類)ではないですが、gnome-pannelもAkamaiのサーバを利用しているんだなぁと… つまり、ブラウザ立ち上げなくてもAkamaiエッジサーバに定期的にコネクションを張っていたのはこいつのせいだったみたいです。OS起動後すぐにコネクション張っていたのも納得ですね。
まとめ
・見覚えの無い宛先IPアドレスはAkamaiのエッジサーバだった
・今回のAkamaiエッジサーバはSo-net(ISP)に設置されたもの
・gnome-pannelがエッジサーバに対してリクエストをなげていた
・恐らくAkamaiを利用する過程のいずれかのノードでハーフクローズの原因がある
大体通信先の謎等は解決しましたが、勝手にコネクション張ってハーフクローズで放置するアプリケーションは考えものですね…
FW (iptables) の設定
固定IPアドレスを割り当ててサーバを外部へ公開する前に、サーバのFW(Firewall)の見直しを行います。間違っていたら指摘していただけると幸いです、
FWの設定
iptablesについて
ここで扱う純粋なFWは、トランスポート層以下のヘッダ内容を見てパケットの処理を決めるものです。LinuxではFWのプログラムとしてIPv4用にiptables、IPv6用にip6tablesが提供されており、今回はiptablesを利用します。設定ファイルは/etc/sysconfig/iptablesになります。
iptablesでは、パケットの処理の規則をルールと呼び、処理内容(受け入れ、遮断など)をtargetと呼びます。このルールを列挙したものをチェインと呼び、このチェインをまとめたものをテーブルと呼びます。デフォルトで用意されているテーブルにはフィルタ目的のfilterやIPマスカレードのためのnatなどがあります。パケットがFWに到達したら、まず適用するテーブルを選択し、その中で該当するチェインを選択し、その中の該当するルールに沿って処理を実行します。
パケットフィルタリングで標準的に使われるのはfilterテーブルですので、今回もfileterテーブルのチェインとルールを操作して、遮断するパケットと通過させるパケットを制御していきたいと思います。filterテーブルには、流入パケットを扱うINPUTチェイン、転送パケットを扱うFORWARDチェイン、流出パケットを扱うOUTPUTチェインがあります。また、それぞれのチェインには、ルールに該当しないパケットへの処理方針を表すPolicyというものがあります。
また、ルール設定を行う上で*1様が凄くわかりやすいのでおすすめです。
設定方針
ルールに欠陥があり不用意にパケットを受け入れてしまうことを懸念してINPUTのPolicyをDROPとします。また中継器として機能させる予定はないのでFORWARDのPolicyもDROPとしています。OUTPUTのPolicyのみACCEPTとしています。また、INPUTでスキャン活動や攻撃と思われるパケットを落とします。
設定内容
設定変更後、service iptables restartで適用できます。設定したiptablesの内容を以下に記述します。
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
###############################################################
#ローカルホストI/F宛は通過
-A INPUT -i lo -j ACCEPT
###############################################################
#コネクション確立済みのパケットは通す
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
###############################################################
#Ping flood対策(icmp echo requestをhashlimitにより受信制限)
#ICMP echo requestは毎秒1個に制限
-A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
#log
-A INPUT -p icmp --icmp-type echo-request -j LOG --log-prefix "ping_flood:"
-A INPUT -p icmp --icmp-type echo-request -j DROP
##############################################################
#HTTP Flood(F5)対策(HTTPパケットをhashlimitで制限)
#送信元IPアドレスごとにパケットカウント
#個数が50を越えると10/sの制限をかける,期間は120000ms
-A INPUT -p tcp -m multiport --dports 80,443 -m state --state NEW -m hashlimit --hashlimit-name t_http_flood --hashlimit 10/s --hashlimit-burst 50 --hashlimit-mode srcip --hashlimit-htable-expire 120000 -j ACCEPT
#log
-A INPUT -p tcp -m multiport --dports 80,443 -m state --state NEW -j LOG --log-prefix "ping_flood:"
-A INPUT -p tcp -m multiport --dports 80,443 -m state --state NEW -j DROP
###############################################################
#SYN flood対策(SYNパケットをlimitにより受信制限)
#SYNパケットが1個到着したらすぐに制限(ほぼ常に制限状態)
#制限状態では、サーバ宛のSYNパケットを毎秒50個へ制限
-A INPUT -p tcp -m multiport --dports 80,443 --syn -m limit --limit 50/s --limit-burst 1 -j ACCEPT
#80,443以外のSYNはSSHを許可
#SSHログインを総当たり攻撃で突破することを先延ばしする
#300秒間に5回までのアクセス制限
-A INPUT -p tcp --syn --dport 22 -m recent --name sshattack --set
-A INPUT -p tcp --syn --dport 22 -m recent --name sshattack --rcheck --seconds 300 --hitcount 5 -j LOG --log-prefix "ssh_bruteforce:"
-A INPUT -p tcp --syn --dport 22 -m recent --name sshattack --rcheck --seconds 300 --hitcount 5 -j DROP
-A INPUT -p tcp --dport 22 -m tcp -j ACCEPT
#log
-A INPUT -p tcp --syn -j LOG --log-prefix "syn_flood:"
-A INPUT -p tcp --syn -j DROP
##############################################################
#ステルススキャン対策
#3wayハンドシェイクを完遂しない、コネクションの足がつかないスキャン
#nmapでありがちなスキャンパケットはとりあえず落としてログをとる
#該当ルールが多すぎるのでSTEALTH_SCANチェインを作成
-N STEALTH_SCAN
-A STEALTH_SCAN -j LOG --log-prefix "stealth_scan: "
-A STEALTH_SCAN -j DROP
#ステルススキャンと思われるパケットは全てステルススキャンチェインへ
-A INPUT -p tcp --tcp-flags SYN,ACK SYN,ACK -m state --state NEW -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags ALL NONE -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags ACK,FIN FIN -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags ACK,PSH PSH -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags ACK,URG URG -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags FIN,URG,PSH FIN,URG,PSH -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags ALL FIN -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags ALL ACK -j STEALTH_SCAN
-A INPUT -p tcp --tcp-flags ALL FIN,ACK -j STEALTH_SCAN
#########################################################
#DNS用
-A INPUT -p udp --sport 53 -j ACCEPT
-A INPUT -p tcp --sport 53 -j ACCEPT
COMMIT
ステルススキャン時のフラグの組み合わせを網羅するのは骨が折れるので、一部*2様のやり方を流用させていただきました(いろいろな攻撃や偵察を考慮していらっしゃるのでとても参考になりました)。
ステルススキャンは、1番目がNULLスキャン、2番目と3番目と7番目、8番目は名称はわからないものの正規の手順ではありえない組み合わせによる挙動 を見るスキャン、4番目はFINスキャン、5番目と6番目も名称不明ですがコネクション確立前には送られないはずのフラグが立ったパケットによるスキャン かと思われます。9番目以降は独自に追加したルールで、それぞれ、クリスマスツリースキャン、FINスキャン、ACKスキャン、Maimonスキャンを検知するルールです。もっと様々なスキャン方法を網羅したい場合nmapのスキャンテクニック*3などを参考にすると良さそうです。
HTTP Floodは送信元IPアドレスをスプーフィングできないのでhashlimitで送信元を取り、制限をかけるようにしています。これによって攻撃者は制限され正規ユーザは制限を受けません。
ホストのリソースを食いつぶすSYN Flood対策ですが、スプーフィング可能な攻撃なのでhashlimitを使って送信元IPアドレス単位での制限は有用ではないんじゃないかなぁと考え、limitを用いています。これらhashlimitやlimitの制限パラメータは環境に合わせた改善の余地がありそうなので適宜チューニングしていこうと思います。また、万全を期して別途SYN Cookie法も有効にする予定です。
同様にICMP Floodも容易に送信元IPアドレスを詐称できるのでlimitで制限をかけました。hashlimitをかけている方をたまに見かけますが、もしかしたらスプーフィングしていない純粋なpingを用いたfloodのみを対象にしているのかなぁと(pingコマンドは送信元IPアドレスを指定できますが、確か未割り当てのアドレスを指定すると送ってくれないので…)。
ICMP Smurf攻撃はネットワーク管理者がルータでブロードキャストアドレス宛のリクエストパケットを破棄するなどの対策を行っていれば防げるので今回はiptablesに対策を記述していません。もし記述するならicmp echo replyの流入を制限したり、踏み台となるのを防ぐためにicmp echo replyの流出を制限するルールを記述すればよいと思います。
DNS amp対策の一貫として、dnsパケットの受信制限を追加するのもよいかもしれません。
もっとも、ここまでで登場した中で帯域型攻撃として働くものはエンドホストで落としてもエンドホストに至るまでのリンク/中継器に影響が出ているので根本的な解決にはなりません。しかしやらないよりはマシなので実装しています。
sshポートへの総当たり攻撃を回避するためにsshポートへも接続回数制限を設ける方が良いと思ったのでこれもhashlimitで設定しようと思ったのですが、*4様の手法の方が明解だったのでこちらを参考に記述しました。
ログについて
dmesgコマンドで取り急ぎメッセージログを見ることはできますが、rsyslogの設定を変更しないと/var/log/下にログファイルは生成されません。そこで*5様の記述に沿ってログの設定を行いました。
すなわち、/etc/rsyslog.confのRULES配下に
kern.info /var/log/iptables.log
を追記。設定変更後は/etc/init.d/rsyslog restartで変更適用。これでログを吐かせるようにしました。
また、ログの肥大化を防ぐためにログのローテーションも行おうと思います。まだ実行しておらず後で行う予定です。
実際のログ
閉じたローカルネットワーク環境で実際にSYN Floodを観測させました。この場合、/var/log/iptables.logに以下のようなログが大量に残ります。192.168.11.5はサーバのローカルIPアドレスです。
ただ、今回のこのログ、実は他ホストからTCPポートスキャン(SYNスキャン)をかけたものです。SYNスキャンはステルススキャンの中でも最もポートスキャンとして検知するのが難しいので今回はsyn_floodでログを出すようにしています。
また、スキャン実行を行うとともにiptables.logをtail -fコマンドでリアルタイム監視していたのですが、RTTを考慮してもログが出力されるまで4,5秒ほどレイテンシがありました。
ping floodも以下のようにちゃんとログを吐いてくれました。
ping floodは以下のようにシェルスクリプトを記述して実行しました。※悪用厳禁。
#!/bin/sh COUNT=0 while [ $COUNT -lt 1000] do ping 192.168.11.5 -n 1000 -s 1400 > /dev/null & COUNT=$(( $COUNT+1 )) done
nmapを用いてFINスキャンを行った場合のログは以下のようになりました。
ログメッセージがstealth_scanだけでも、ログからフラグまで見えるのでどんなスキャンかが判別できますね。
sshポートについて
botのスキャン活動などではsshポートはよく標的になるので、他のポートへ変更するのが無難のようです。これも今後の予定です。