OpenStackでVIP冗長構成を作る

このエントリはOpenStack Advent Calender 2015の12/21のエントリです。今日はOpenStack運用ではまりがちなallowed-address-pairsについて書いてみようと思います。なお本エントリの内容はOpenStack Havanaを前提に記載しています。

allowed-address-pairsとは

OpenStackでは各インスタンスにポートを紐付けることにより、インスタンスは外部と通信が可能となります。そしてポートは基本的に1ポートに1つのIP、1つのMACアドレスが割り当てられています。allowed-address-pairsとはそのポートに紐付ける拡張アドレスのようなもので、例えばAというポートに10.1.1.1のIPが紐付いている場合に、Aに対してallowed-address-pairs=10.1.1.2を紐付けると、ポートAは10.1.1.110.1.1.2の通信が可能になります。具体的な用途としてはkeepalivedなどを用いてVIPを使用した冗長構成を取る場合に、VIPをallowed-address-pairsとして利用することが多いです。

設定方法

python-neutronclientで下記のコマンドを発行することでallowed-address-pairsを追加することが出来ます。

neutron port-update xxxxx-xxxx-xxxxx --allowed-address-pairs type=dict list=true ip_address=10.1.1.2

また解除する場合は空白を設定します。

neutron port-update xxxxx-xxxx-xxxxx --allowed-address-pairs ''

やっていはいけない落とし穴

一見便利に見えるこの機能ですが、SecurityGroup(SG)のremote-groupと組み合わせて使用する場合は注意が必要です。
allowed-address-pairsの実際の動きとしてはcompute-nodeiptablesにallowed-address-pairsで指定したIPアドレスのPermitルールが追加されます。この設定を一つ間違えると全てのポリシーが許可されてしまうケースが有ります。下記の図はLAN内のinternal-serverがインターネット上のサービスexample-serviceと通信する際の通信とIPヘッダを記載しています。
sozai.001
往路のパケットに関しては特に特別なことはなく、通常のNAT通信です。しかし、戻りのパケットを見てみましょう。
sozai.002
注目すべきはnat → internal serverの通信です。ここでのfrom ipはexample serviceのipとなるため、natサーバのinterfaceからexample serviceのipで通信するためには、allowed-address-pairsに0.0.0.0/0を追加する必要があります。これはexample serviceはインターネット上のサービスであり、あらゆるIPになり得るためです。またallowed-address-pairsを追加しない場合、natサーバのinterfaceからOpenStackの管理しないIP(200.200.200.200)のパケットが送出されたことになり、デフォルトのポリシーでパケットが捨てられてしまいます。

さて、natサーバに0.0.0.0/0のallowed-address-pairsが追加されたことにより、natサーバが存在するcompute-nodeには0.0.0.0/0からのip通信が許可される設定が追加されています。この状態でSecurity Groupのremote-groupを利用した場合に、なんと0.0.0.0/0のipを持つホストからの通信が許可されてしまうという状態に陥るケースが有ります。

remote-groupとは

リモートグループは許可されたソースの CIDR を動的に定義する方法です。ユーザーがリモートグループ (セキュリティグループ名) を指定します。これにより、指定されたリモートグループを使用する、ユーザーの他のインスタンスが動的にすべて選択されます。この動的な選択により、クラスターのそれぞれの新しいメンバーを許可する、個別のルールの必要性を軽減できます。

通常Security Groupは例えば22番ポートを10.10.10.0/24のアドレス帯からのみ許可するといったSecurity Group定義を行うのですが、10.10.10.0/24の部分をSecurity Groupの名前で定義出来たりします。例として2つのポリシーを挙げてみます。

Security Group名:ssh

  • ポート:22
  • 許可IP: 10.10.10.0/24

Security Group名:proxy

  • ポート:80
  • リモートグループ:ssh

この定義をした場合、proxyルールを持つインスタンスはsshルールが割り当てられたインスタンス全てにポート80番の通信を許可すると言った挙動をします。しかし、これは裏の動きは単純にsshルールが割り当てられたインスタンスのIPから80番ポートに対して許可するiptablesが書かれているだけに過ぎません。先のnatサーバに話を戻しましょう。

natサーバの管理するアドレス

  • eth0:100.100.100.100
  • eth1:10.10.10.10
  • allowed-address-pairs: 0.0.0.0/0

この状態でnatサーバにSecurity Group:sshを割り当てると、Security Group:proxyを持つインスタンスに対して、from ip100.100.100.100,10.10.10.10,0.0.0.0/0からの80番ポートへの通信が許可されてしまいます。お気づきでしょうか?0.0.0.0/0が許可されることで全てのIPからの許可ポリシーが追加されてしまいます。こういった自体を避けるためにNATサーバのようにallwed-address-pairsに0.0.0.0/0を持ちうるサーバのSecurity Groupは分離して管理すべきです。

最後に

一見便利に見えるallowed-address-pairs,Security Groupですが実際裏で動いているのはiptablesということもあり、シンプルな反面、小回りが効かないことも多々あります。そして運用であれ・・・通信できないな・・・というときは8〜9割はこの2つの設定が誤っていることが多いですよね。allowed-address-pairsは意外と日本語の情報が少なかったので今回駆け足ながらも記事にしてみました。国内でこれからOpenStackを触るエンジニアのハマる時間を1時間でも短くできれば幸いです。それでは明日は@mizumotodaさんで「OpenStackにコンテナの実行環境をネットワークごとデプロイする」です。有難うございました。