第4回:Eucalyptusのセキュリティ機能
■ネットワークセキュリティを支える基礎技術
今回は、Eucalyptusのセキュリティ機能に関連する基礎技術を見ていきます。前回、iptablesのパケットフィルタリングを利用したファイアウォール・ルールの設定について紹介しました。まずは、その基礎となるiptablesの仕組みについて補足説明を行います。
そして、「ユーカリプタス入門」第2回で、VMインスタンスにSSH接続する際に使用した鍵ペアや「ユーカリプタス入門」第7回のAPI認証で登場した、X.509証明書などの認証技術の基礎を解説します。
■iptablesの全体像
iptablesの設定を行う際は、テーブルとチェーンを指定して設定を行います。iptablesは、大きくは、パケットフィルタリング機能とNAT機能の2種類の機能を持ちます。パケットフィルタリングに関する設定を行うテーブルがfilterテーブルで、NATに関する設定を行うテーブルがnatテーブルです。
もう一つのチェーンは、パケットがサーバー内部を通過していくどの段階でiptablesによる処理を行うかを指定するものです。たとえば、Linuxサーバーを2つ以上のネットワークを相互接続するルータとして使用する場合、Linuxサーバーを通過していくパケットは、【図4-1】のように「PREROUTING」「FORWARD」「POSTROUTING」の順番にチェーンを通過していきます。
それぞれのチェーンの違いを理解するのは少し難しいのですが、実用上は、DNAT処理とSNAT処理は、それぞれPREROUTINGチェーンとPOSTROUTINGチェーンで実施して、パケットフィルタリング処理はFORWARDチェーンで実施するということを覚えておけば十分です。
【図4-1】パケット転送に使用するチェーンとテーブル |
今回はクラスタコントローラがノードコントローラにパケットを転送する際の処理について考えていますが、サーバー上のアプリケーション自身がネットワーク通信する際のパケットフィルタリングを行う場合もあります。この場合、アプリケーションが送信するパケットのフィルタリングはOUTPUTチェーンを用いて、受信するパケットのフィルタリングはINPUTチェーンを用います【図4-2】。
【図4-2】パケット送受信に使用するチェーンとテーブル |
それでは、前回に示したクラスタコントローラ上のiptablesの設定を再確認します。次は、natテーブルのPREROUTINGチェーンに定義されたDNATの設定です。-tオプションと-Lオプションで、それぞれテーブルとチェーンを指定して設定内容を表示しています。-nオプションは、IPアドレスやポート番号の名前解決を行わずに数値のまま表示する指定です。
# iptables -t nat -nL PREROUTING Chain PREROUTING (policy ACCEPT) target prot opt source destination DNAT tcp -- 10.1.0.0/16 169.254.169.254 tcp dpt:80 to:169.254.169.254:8773 DNAT all -- 0.0.0.0/0 192.168.32.200 to:10.1.2.2 DNAT all -- 0.0.0.0/0 192.168.32.201 to:10.1.2.3 |
行頭にDNATと示された設定が3つありますが、2つ目と3つ目がパブリックネットワークからVMインスタンスに接続する際のDNAT処理です。第3回の【図3-5】と合わせて見てください。2つ目を見ると、宛先アドレス(destination)がVMインスタンス#1のパブリックIPアドレス(192.168.32.200)のパケットを受けとると、対応するプライベートIPアドレス(10.1.2.2)に変換するという処理であることが分かります。
一方、SNATの設定は、natテーブルのPOSTROUTINGチェーンに定義されます。行頭にSNATと示された設定が該当します。
# iptables -t nat -nL POSTROUTING Chain POSTROUTING (policy ACCEPT) target prot opt source destination SNAT all -- 10.1.2.2 !10.1.0.0/16 to:192.168.32.200 SNAT all -- 10.1.2.3 !10.1.0.0/16 to:192.168.32.201 MASQUERADE all -- !127.0.0.0/8 !10.1.0.0/16 |
例えば、1つめのSNATの定義を見ると、送信元アドレス(source)がVMインスタンス#1のプライベートIPアドレス(10.1.2.2)であった場合に、対応するパブリックIPアドレス(192.168.32.200)に変換する処理であることがわかります。
ただし、この処理が必要になるのは宛先がパブリックネットワークの場合だけです。プライベートネットワーク内部の通信は、プライベートIPアドレスのままで行う必要があります。今回の構成では、ネットワークアドレス10.1.0.0/16の範囲がプライベートネットワークですので、宛先アドレス(destination)がそれ以外(!10.1.0.0/16)という条件が付けられています。
続いて、フィルタリングルールを見ます。これはfilterテーブルのFORWARDチェーンに定義すると説明しました。次のように-tオプションを省略すると、デフォルトでfilterテーブルが指定されたことになります。
# iptables -nL FORWARD Chain FORWARD (policy DROP) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate ESTABLISHED ACCEPT all -- 0.0.0.0/0 !10.1.0.0/16 ACCEPT all -- 10.1.2.0/27 10.1.2.0/27 admin-default all -- 0.0.0.0/0 0.0.0.0/0 |
コマンド出力の一行目に「policy DROP」と示されています。これは、この後のACCEPTルールに適合しなかったパケットはすべて転送を拒否するという意味です。それでは、どのようなパケットが転送を許可されるのでしょうか? ここには3つのACCEPTルールがあります。
1つ目には「ctstate ESTABLISHED」という条件があります。これは、SSH接続など一連の通信のパケットにおける、2個め以降のパケットという意味です。最初のパケットが転送を許可されると、同じ通信に属する2個め以降のパケットはすべて転送が許可されます。VMインスタンスとやりとりされる大部分のパケットは、この条件に適合しますので、最初にこれを定義することで条件判断の処理時間が短縮されます。
2つ目のACCEPTは、宛先(destination)がプライベートネットワーク以外(!10.1.0.0/16)という事ですので、これはVMインスタンス宛ではなくて、VMインスタンスからパブリックネットワークに向けた通信ということになります。
3つ目のACCEPTは、同じセキュリティグループに属するVMインスタンス同士の通信です。Eucalyptusはセキュリティグループごとに異なるサブネットを用意しますが、ここでは、adminユーザのdefaultグループに対して、10.1.2.0/27というサブネットが割り当てられています。
さて、ここまでは、セキュリティグループに依存しない一般的なフィルタリングルールです。この他に、セキュリティグループごとに特定のポートに向けた通信を許可することができました。上のコマンド出力の最後にある「admin-default」という定義がこれにあたります。
これは、別途定義されたadmin-defaultチェーンの設定を適用するという意味です。admin-defaultという「サブルーチン」を呼び出すと考えると分かりやすいかも知れません。このようにサブルーチン的に呼び出すチェーンをユーザ定義チェーンと言います。実際のadmin-defaultチェーンの内容は次の通りです。宛先(destination)がこのセキュリティグループに対応するサブネット(10.1.2.0/27)の場合に、TCP22番ポートへの接続を許可しています。
# iptables -nL admin-default Chain admin-default (1 references) target prot opt source destination ACCEPT tcp -- 0.0.0.0/0 10.1.2.0/27 tcp dpt:22 |
このように、ユーザとセキュリティグループの組ごとにユーザ定義チェーンを用意することで、それぞれに独立したフィルタリングルールを実現していることになります。Eucalyptus環境では、これらの設定はすべて自動的に行われます。
一般のサーバー環境におけるiptablesの具体的な設定手順については、筆者の書籍「プロのためのLinuxシステム・ネットワーク管理技術」などを参考にしてください。
■暗号鍵ペアによるユーザ認証の基礎
「ユーカリプタス入門」第2回で紹介されている一般ユーザの作成手順を確認すると、ユーザ作成後にeuca2-<ユーザID>-x509.zipというファイルをEucalyptus管理画面から入手しています。
これは、euca2oolsを利用してクライアントからEucalyptus環境を操作する際に、本物のユーザであることを証明するための証明書になります。もう少し正確には、「ユーカリプタス入門」第7回に説明があるように、euca2oolsがSOAP APIを利用してEucalyptusにリクエストを送信する際に利用されます。
それでは、この証明書はどのようにしてユーザの身元を保証するのでしょうか? 証明書ファイルを直接、クライアントからネットワーク経由でEucalyptusの管理サーバー(クラウドコントローラ)に送るわけではありません。そんな事をすれば、ネットワークを流れるパケットを盗聴されて、簡単に証明書が他人の手に渡ってしまいます。
ネットワーク経由で身元を証明する際によく利用されるのが、公開鍵と秘密鍵と呼ばれる暗号鍵のペアを利用した手法です。一般に、メッセージを暗号化する方式には、暗号化と復号化に同じ鍵を用いる対象鍵方式と、暗号化に用いる鍵と復号化に用いる鍵が異なる非対象鍵方式があります。非対象鍵方式の暗号化では、例えば、秘密鍵で暗号化して、公開鍵で復号化を行います。ある秘密鍵で暗号化したメッセージは、対応する公開鍵のみで復号化が可能です。逆に、ある公開鍵で復号化できる暗号を作成するには、必ず、対応する秘密鍵を使用する必要があります。
これとは反対に、公開鍵で暗号化したものを秘密鍵で復号化する場合もあります。この後の例でわかるように、公開鍵、秘密鍵という名称は、その鍵を本人だけが所有するのか、他人に配布するのかという使用方法によって決まります。
Eucalyptusのクライアント認証の場合は、管理サーバーからダウンロードした証明書には、暗号化用の秘密鍵が含まれています。一方、それに対応する公開鍵は、管理サーバーのデータベース内に保管されます。
例えば、クライアントが管理サーバーにリクエストを送信する際に、リクエストメッセージの特定部分のハッシュ値を計算して、その値を秘密鍵で暗号化したものをリクエストメッセージと一緒に送信します【図4-3】。管理サーバー側では、データベースに保存された、対応する公開鍵で復号化します。先に説明した様に、この公開鍵で復号可能な暗号を作成できるのは、対応する秘密鍵を持った本物のユーザだけです。これでリクエストメッセージの送信者は、正しい証明書を持った本物のユーザであることが確認できました。
【図4-3】ユーザ証明書による認証処理 |
この方法には、リクエストメッセージの改竄を防ぐという効果もあります。ネットワークを流れるリクエストメッセージの一部分が誰かによってこっそりと書き換えられたします。この時、管理サーバーが受け取ったリクエストメッセージのハッシュ値をクライアントと同じ方法で再計算して、先ほど公開鍵で復号化したハッシュ値と比較すると、ハッシュ値が異なることになります。
これによって、管理サーバーはこのリクエストメッセージは途中で改竄された恐れがある事を検知します。クライアントから送られてくるハッシュ値は暗号化されているので、これをリクエストメッセージに合わせて改竄することはできない点に注意してください。
以上が、EucalyptusのSOAP API利用時におけるユーザ認証の基本的な仕組みになります。証明書ファイルの名前からわかるように、正確には、X.509証明書と呼ばれる形式にしたがった証明書が利用されています。
X.509証明書では、管理サーバーに保存されている公開鍵自体が偽造されたものではないことを保証する仕組みも取り入れられています。例えば、Eucalyptus環境とAmazon EC2の両方を同じユーザ証明書(秘密鍵)で利用したい場合、Eucalyptusの管理サーバーとAmazonの両方に同じ公開鍵を登録する必要があります。
先ほどの手順では、管理サーバー上で鍵ペアを作成していますので、公開鍵が偽造される可能性は少ないのですが、利用者から提供された公開鍵を受け入れて登録するような場合は、その公開鍵が偽造品でないことを確認する必要があります。これは、VeriSignなどの第三者機関の電子署名が入った公開鍵を登録することで実現しています。
■SSH認証の鍵ペアの役割
Eucalyptusを利用する際は、euca2oolsを使用するための証明書に加えて、VMインスタンスにSSH接続するための鍵ペアも必要です。SSH接続時の鍵ペアによるユーザ認証も基本的な原理は同じです。
euca-add-keypairコマンドで鍵ペアを作成すると、コマンドを実行したユーザの手元には秘密鍵のファイルが作成されます。この時、Eucalyptus管理サーバーのデータベースには、これに対応する公開鍵が保存されます。
euca-run-instancesコマンドでVMインスタンスを起動する際に、使用する鍵ペアを指定すると、ノードコントローラは管理サーバーから公開鍵を取得して、起動したVMインスタンスに公開鍵の登録を行います。具体的な登録の仕組みについては、第2回に説明したとおりです。
【図4-4】は、実際にSSH接続を行う際のユーザ認証の仕組みです。SSHクライアントがVMインスタンス上のSSHデーモンに接続を要求すると、SSHデーモンは、適当に作成した乱数を登録された公開鍵で暗号化して、クライアントに送付します。
クライアントは対応する秘密鍵で乱数を復号化して、そのハッシュ値をSSHデーモンに返送します。SSHデーモンは最初に送った乱数のハッシュ値を自分でも計算して、クライアントから返送されたハッシュ値と一致することを確認した後に、接続を許可します。公開鍵で暗号化された乱数を復号化してハッシュ値を計算できるのは、対応する秘密鍵を持っているユーザだけですので、これでユーザの確認が行われたことになるわけです。
この仕組みから、秘密鍵と公開鍵の名前の由来がわかります。秘密鍵は、その名の通りユーザ本人だけが所有するべきもので、他人の手に渡らないよう管理する必要があります。もう一方の公開鍵は、その秘密鍵を使って接続したいサーバーに配布する必要がありますので、逆に、世の中に公開しておく形になります。
【図4-4】鍵ペアによる SSH ユーザ認証 |
ところで、SSH接続の際は、接続元のユーザが本物であることを確認するユーザ認証以外に、接続先のサーバーが本物であることを確認するサーバー認証も行われる事はご存知でしょうか? これは、悪意のある第三者が偽装したサーバーに誤って接続することを防ぐためのものです。
SSHデーモンが起動しているサーバーには、サーバー証明書が保存されていますが、その中身は、サーバーに固有の公開鍵と秘密鍵のペアになります。SSHクライアントが初めてそのサーバーに接続すると、サーバー証明書の公開鍵を受け取って、クライアント側のローカルディスクに保存します。次に、クライアントは適当に発生した乱数をこの公開鍵で暗号化して、SSHデーモンに送付します。
この後の流れは、先ほどのユーザ認証に対して、クライアントとサーバーの役割が入れ代わる事以外は、まったく同じです。今の場合は、SSHデーモン側が秘密鍵を用いて乱数を復号化できることから、正しい秘密鍵を持った本物のサーバーであることをクライアントが確認することになります。
なお、Eucalyptusの環境では、VMインスタンスの起動・停止を繰り返しますので、以前に接続したことのあるVMインスタンスと同じIPアドレスを持った、新しいVMインスタンスに接続することがあります。この場合、新しいVMインスタンスのSSHデーモンから受け取るサーバー証明書の公開鍵は、前回、同じIPアドレスのVMインスタンスから受け取った公開鍵とは異なります。
このような場合、SSHクライアントは、サーバー証明書が偽造された可能性があると判断して、接続を拒否することがあります。Linux標準のOpenSSHクライアントを使用している場合は、~/.ssh/known_hostsに登録された古い公開鍵を削除すると、新しいVMインスタンスの公開鍵を改めて受け入れます。
あるいは、「ユーカリプタス入門」第2回のSSH接続手順にあるように、SSHコマンドに「-o StrictHostKeyChecking=no」というオプションを指定しても構いません。これは、前回と異なる公開鍵を受け取っても接続を拒否しないというオプションです。
■さいごに
iptablesはさまざまな設定が可能ですが、全体像を理解して適切な設定を行うのはなかなか難しいものです。Eucalyptusの環境では、基本的には、ユーザ自身が直接に設定する必要はありませんが、ネットワーク通信がうまくいかないなどの問題が起きた場合は、設定を確認する必要があります。クラウドネットワークを支える基本技術の1つとして、ぜひマスターしておいてください。
もう一つの認証技術も同様です。証明書の取得方法や鍵ペアの作成方法などの手順を知っていれば、Eucalyptusのクラウドを利用することはできます。しかしながら、安全なクラウドを構築して、運用管理する上では、そもそもなぜその手順で認証ができるのか、認証がうまくいかない場合はどこを確認するべきかなど、一歩踏み込んだ理解が必要です。Eucalyptusを教材として、一歩進んだ、クラウド時代のインフラエンジニアを目指していきましょう。