2013年12月17日火曜日

Neutron VPNaaS を使ってみる ~ 意味もないけど、とりあえず back-to-back で IPsec VPN

OpenStack Neutron に Havana リリースで VPNaaS が追加されました。今回のリリースでは IPsec による L3 VPN のみが実装されています。Icehouse では SSL-VPN や BGP/MPLS による VPN なども計画されています。devstack を使って VPNaaS を動かして、どんなことが行われているのか見てみたいと思います。

VPNaaS だけでなく LBaaS/FWaaS を含めた Network Service の考え方は OpenStack Neutron の紹介資料 (Havana 版) の後ろの方で説明しているので、こちらもご参照ください。

構成

2か所のサイトを IPsec VPN で接続する例を考えます。


ここでは、一つの OpenStack で 2つのネットワークをそれぞれ別のサイトとみなして、VPNaaS を使って back-to-back で VPN を作成して接続します。上の図の site1, site2 はそれぞれ net1, net2 が対応します。router1, router2 にそれぞれ相手のネットワークに向けた経路情報を明示的に設定しない限り、net1 と net2 間は通信できないので、VPN 接続の確認として使うことができます。


devstack の実行

devstack の stable/havana branch を取得して、localrc を作成して、stack.sh を実行します。 Ubuntu 12.04.3 LTS を使っています。
git clone https://github.com/openstack-dev/devstack.git
cd devstack
git checkout stable/havana
wget -O localrc https://gist.github.com/amotoki/6520741/raw/6856653c57b67441c99aba057f310801da4d03ae/localrc-havana-allinone
./stack.sh
今回使った localrc は https://gist.github.com/amotoki/6520741 にありますので、参考にしてください。
Ceilometer, Heat, Swift も有効にしていますが、無効にしても構いません。
余談ですが、Ubuntu 12.04 では mongodb のバージョンが古いので、Ceilometer を動かすには 2.4.6 などの新しいバージョンの mongodb をインストールする必要があります。 Havana の Ubuntu Cloud Archives から mongodb をインストールするのが一番簡単だと思います。

VPNaaS 用の Neutron の設定

devstack では、設定ファイルの作成は devstack の実行中に行われますが、何が行われているのかを見ておきます。

まず /etc/neutron/neutron.conf です。 VPNaaS を有効にするには service_pliugins に VPN プラグイン neutron.services.vpn.plugin.VPNDriverPlugin を指定します。この例では、他に L3, LBaaS, FWaaS の reference implementation が有効になっています。大きな流れとしては OpenStack Neutron の紹介資料 (Havana 版) の資料 (p.19) で紹介しているように、Neutron では機能単位のプラグイン (Service Plugin) を用意する方向です。VPNaaS もこの方向で実装されています。
service_plugins = neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,neutron.services.loadbalancer.plugin.LoadBalancerPlugin,neutron.services.vpn.plugin.VPNDriverPlugin,neutron.services.firewall.fwaas_plugin.FirewallPlugin
extension list を確認すると、vpnaas が有効になっていることが分かります。
$ neutron ext-list
+-----------------------+-----------------------------------------------+
| alias                 | name                                          |
+-----------------------+-----------------------------------------------+
| service-type          | Neutron Service Type Management               |
| fwaas                 | Firewall service                              |
| security-group        | security-group                                |
| l3_agent_scheduler    | L3 Agent Scheduler                            |
| lbaas_agent_scheduler | Loadbalancer Agent Scheduler                  |
| ext-gw-mode           | Neutron L3 Configurable external gateway mode |
| binding               | Port Binding                                  |
| quotas                | Quota management support                      |
| agent                 | agent                                         |
| router                | Neutron L3 Router                             |
| dhcp_agent_scheduler  | DHCP Agent Scheduler                          |
| external-net          | Neutron external network                      |
| multi-provider        | Multi Provider Network                        |
| allowed-address-pairs | Allowed Address Pairs                         |
| vpnaas                | VPN service                                   |
| extra_dhcp_opt        | Neutron Extra DHCP opts                       |
| provider              | Provider Network                              |
| lbaas                 | LoadBalancing service                         |
| extraroute            | Neutron Extra Route                           |
+-----------------------+-----------------------------------------------+
IPsec の実装は Openswan https://www.openswan.org/ を使用しています。
devstack 実行後には openswan パッケージがインストールされています。現状は openswan ベースの IPsec VPN だけが Neutron に入っている唯一の VPNaaS の実装です。
)$ COLS=80 dpkg -l openswan
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                Version             Description
+++-===================-===================-======================================================
ii  openswan            1:2.6.37-1          Internet Key Exchange daemon
VPN に対応した L3 agent である neutron-vpn-agent が openswan を呼び出して VPN 接続を行います。 VPNaaS を使う場合には l3-agent の代わりに vpn-agent を使用します。 vpn-agent は l3-agent に VPN 用の機能が追加されたもので、通常の l3-agent としてもふるまいます。
実行されているサービスプロセスを確認すると vpn-agent が起動していることが分かります。 FWaaS も同時に有効にしているので、fwaas.ini も引数に指定されています。現状は VPNaaS に関連する設定項目はありません。
$ ps auxw | grep vpn-agent
1000      7786  0.0  0.0   9388   932 pts/90   S+   21:28   0:00 grep --color=auto vpn-agent
1000     17627  0.2  0.9 112980 39164 pts/14   S+   20:58   0:05 /usr/bin/python /usr/local/bin/neutron-vpn-agent --config-file /etc/neutron/neutron.conf --config-file=/etc/neutron/l3_agent.ini --config-file /etc/neutron/fwaas_driver.ini

ネットワークの準備

site1 側のネットワーク、ルーターは devstack の実行時にデフォルトで作成されているので、2つ目のサイトに相当するネットワーク、ルーターを作成します。ここではコマンドラインで作成していますが、ダッシュボードから作成した方が楽だと思います。(user_name: demo, tenant_name: demo で操作します)。さらに、それぞれのネットワークに接続した VM も起動しておきます。
$ neutron net-create net2
$ neutron subnet-create --name subnet2 net2 10.3.3.0/24
$ neutron router-create router2
$ neutron router-interface-add router2 subnet2
$ neutron router-gateway-set router2 ext_net
$ nova boot --key-name mykey --image tty-quantum --flavor m1.tiny --nic net-id= vm1
$ nova boot --key-name mykey --image tty-quantum --flavor m1.tiny --nic net-id= vm2
この状態ではこのようになります。


自分の例では以下のようなアドレスになりました。
  • Site1
    • VM1 : 10.0.0.3
    • router1 (net1側) : 10.0.0.1
  • Site1
    • VM2 : 10.3.3.2
    • router2 (net2側) : 10.3.3.1

この段階で異なるネットワーク間で ping が通らないことを確認しておきます。
  • VM1 -> router1(net1側) : OK
  • VM1 -> router2(net2側) : NG
  • VM1 -> VM2 : NG
  • VM2 -> router2(net2側) : OK
  • VM2 -> router1(net1側) : NG
  • VM2 -> VM1 : NG
セキュリティグループで ICMP と SSH を開けた上で確認します。
neutron security-group-rule-create --protocol tcp --port-range-min 22 --port-range-max 22 --remote-ip-prefix 0.0.0.0/0 default
neutron security-group-rule-create --protocol icmp --remote-ip-prefix 0.0.0.0/0 default

VPN の作成


VPN の作成は、それぞれのサイトで行います。

最初に、VPN service はルーターとサブネットを指定して作成します。VPN service は、VPN 接続を受け付けるサイト側で、VPN 接続をどのルーターで終端し、どのサブネットに関連付けるかを指定するものです。また、IPsec 接続の各種ポリシーはあらかじめ用意した IPsec Policy, IKE Policy で作成しておきます。この段階では VPN service の status は Pending Create で問題ありません。

Site1 側
neutron vpn-ipsecpolicy-create ipsec-policy1
neutron vpn-ikepolicy-create ike-policy1
neutron vpn-service-create --name vpn1 router1 private-subnet
Site2 側
neutron vpn-ipsecpolicy-create ipsec-policy2
neutron vpn-ikepolicy-create ike-policy2
neutron vpn-service-create --name vpn2 router2 subnet2

次に、IPsec 接続を VPN service を指定して作成します。

IPsec 接続を作成するには、接続相手側の VPN ルータの外側の IP アドレスが必要です。以下のコマンドを実行することで、それぞれのルーターの外側の IP アドレスが以下のように分かります。
  • router1: 172.24.4.226
  • router2: 172.24.4.227
現在の Neutron API ではルーターの外側の IP アドレスは管理者権限でしか入手することができません。そのため、通常のユーザーが OpenStack どうしを VPNaaS で接続することはできません。これでは不便なので、ルーターの外部の IP アドレス情報を入手できるようにすることが検討されています。
$ OS_USERNAME=admin OS_TENANT_NAME=admin neutron router-port-list router1 --device_owner network:router_gateway -c fixed_ips
+-------------------------------------------------------------------------------------+
| fixed_ips                                                                           |
+-------------------------------------------------------------------------------------+
| {"subnet_id": "711a97f6-b6c0-425c-b17b-618904324f95", "ip_address": "172.24.4.226"} |
+-------------------------------------------------------------------------------------+
$ OS_USERNAME=admin OS_TENANT_NAME=admin neutron router-port-list router2 --device_owner network:router_gateway -c fixed_ips
+-------------------------------------------------------------------------------------+
| fixed_ips                                                                           |
+-------------------------------------------------------------------------------------+
| {"subnet_id": "711a97f6-b6c0-425c-b17b-618904324f95", "ip_address": "172.24.4.227"} |
+-------------------------------------------------------------------------------------+

この情報をもとに IPsec 接続を作成します。

まず Site1 側を作成します。 peer-address には接続相手の VPN ルーターの外側の IP アドレス (ここでは router2 の外側のアドレス) 172.24.4.227 を、peer-cidr は接続相手のサブネットアドレス 10.3.3.0/24 を指定します。 peer-id の指定内容は VPN ルーターの構成により変わりますが、vpn-agent ベースのルーターの場合は peer-address と同じアドレスを指定します。
$ neutron ipsec-site-connection-create --vpnservice-id vpn1 \
    --ikepolicy-id ike-policy1 --ipsecpolicy-id ipsec-policy1 \
    --peer-address 172.24.4.227 --peer-id 172.24.4.227 \
    --peer-cidr 10.3.3.0/24 --psk secret1 --name conn1
$ neutron ipsec-site-connection-list -c name -c peer_address -c peer_cidrs -c status
+-------+--------------+---------------+--------+
| name  | peer_address | peer_cidrs    | status |
+-------+--------------+---------------+--------+
| conn1 | 172.24.4.227 | "10.3.3.0/24" | DOWN   |
+-------+--------------+---------------+--------+
この段階では Site1 側の IPsec 接続 conn1 のステータスは、まだ相手と接続されていないので、DOWN となります。

次に、Site2 側の IPsec 接続を作成します。
$ neutron ipsec-site-connection-create --vpnservice-id vpn2 \
    --ikepolicy-id ike-policy2 --ipsecpolicy-id ipsec-policy2 \
    --peer-address 172.24.4.226 --peer-id 172.24.4.226 \
    --peer-cidr 10.0.0.0/24 --psk secret1 --name conn2
$ neutron ipsec-site-connection-list -c name -c peer_address -c peer_cidrs -c status
+-------+--------------+---------------+--------+
| name  | peer_address | peer_cidrs    | status |
+-------+--------------+---------------+--------+
| conn1 | 172.24.4.227 | "10.3.3.0/24" | ACTIVE |
| conn2 | 172.24.4.226 | "10.0.0.0/24" | ACTIVE |
+-------+--------------+---------------+--------+
相手と正常に接続されていれば、両方のサイトの IPsec 接続でステータスが ACTIVE になります。

導通も確認してみましょう。VM1 にログインして、VM2 に対して ping を実行します。
# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr FA:16:3E:8A:39:FE
          inet addr:10.0.0.3  Bcast:10.0.0.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1185 errors:0 dropped:0 overruns:0 frame:0
          TX packets:821 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:126234 (123.2 KiB)  TX bytes:144124 (140.7 KiB)

# ping -c 5 10.3.3.2
PING 10.3.3.2 (10.3.3.2): 56 data bytes
64 bytes from 10.3.3.2: seq=0 ttl=62 time=6.093 ms
64 bytes from 10.3.3.2: seq=1 ttl=62 time=1.803 ms
64 bytes from 10.3.3.2: seq=2 ttl=62 time=1.886 ms
64 bytes from 10.3.3.2: seq=3 ttl=62 time=1.771 ms
64 bytes from 10.3.3.2: seq=4 ttl=62 time=1.399 ms

--- 10.3.3.2 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 1.399/2.590/6.093 ms

デバッグ情報

上では手順と動作確認を見てきましたが、デバッグ情報がどこにあるかを紹介しておきます。

  • neutron-vpn-agent のログ : /opt/stack/logs/screen-q-vpn.log (devstack の場合)
  • neutron-vpn-agent が作成する openswan の情報 : /opt/stack/data/neutron/ipsec/<router-id>
    • <router-id> は VPN service が関連付けられたルーターの ID
    • 通常のインストールであれば /var/lib/neutron などになります。
  • network namespace 内の ipsec 設定の情報
    • ip netns qrouter-ROUTER_ID ip -s xfrm state
    • ip netns qrouter-ROUTER_ID ip -s xfrm policy
    $ sudo netns exec qrouter-b78b3faf-e8d1-45dd-b185-02f65415b389 ip -s xfrm state
    src 172.24.4.227 dst 172.24.4.226
            proto esp spi 0x910992d8(2433323736) reqid 16385(0x00004001) mode tunnel
            replay-window 32 seq 0x00000000 flag af-unspec (0x00100000)
            auth-trunc hmac(sha1) 0x34f52f8ecdc75a4c3a4940e578b3f8a6a50e8147 (160 bits) 96
            enc cbc(aes) 0xc6091efe44c2a152ac5c77d6e6672f05 (128 bits)
            lifetime config:
              limit: soft (INF)(bytes), hard (INF)(bytes)
              limit: soft (INF)(packets), hard (INF)(packets)
              expire add: soft 0(sec), hard 0(sec)
              expire use: soft 0(sec), hard 0(sec)
            lifetime current:
              1512(bytes), 18(packets)
              add 2013-12-16 01:40:17 use 2013-12-16 01:46:21
            stats:
              replay-window 0 replay 0 failed 0
    src 172.24.4.226 dst 172.24.4.227
            proto esp spi 0xadd229c7(2916231623) reqid 16385(0x00004001) mode tunnel
            replay-window 32 seq 0x00000000 flag af-unspec (0x00100000)
            auth-trunc hmac(sha1) 0x8a0f54cd12bd528be9b7f7feeb6a901276d7264a (160 bits) 96
            enc cbc(aes) 0x4f7ffa695054c7408dae2f99ceb0c241 (128 bits)
            lifetime config:
              limit: soft (INF)(bytes), hard (INF)(bytes)
              limit: soft (INF)(packets), hard (INF)(packets)
              expire add: soft 0(sec), hard 0(sec)
              expire use: soft 0(sec), hard 0(sec)
            lifetime current:
              1512(bytes), 18(packets)
              add 2013-12-16 01:40:17 use 2013-12-16 01:46:21
            stats:
              replay-window 0 replay 0 failed 0
    

後始末

現時点では unstack.sh を行っても openswan のプロセスが削除されません。このプロセスを停止しないと、対応する network namespace を削除できません。 openswan が作成するプロセス pluto の PID が /opt/stack/data/neutron/ipsec/*/var/run/pluto.pid にあるので、この情報をもとに関連プロセスを削除します。
$ sudo kill `cat /opt/stack/data/neutron/ipsec/*/var/run/pluto.pid$`
$ for ns in `ip netns | grep -E '(qrouter|qdhcp)-'`; do sudo ip netns delete $ns; done