AWS VPN (2) between VPC VPN G/W and Debian Linux (racoon, setkey, quagga) with monit for Non-NAT

前回、VPCとDebianの間で構築したIPsec VPNを、冗長化をなんとか保ったままNATをやめる方法を提案します。

あえて”提案”と書いたのは、実際自分はNATしたくないためこちらで利用しますが、こ汚い設計になるため自信もって推奨できないからです。



NATを外すための設定

前回の続きなので、まずVPN上のNATを無効にします。

次に、ipsec-toolsに設定を加えます。
元のTunnel用設定は確定しているので、ファイルを分けておきます。

Customer Subnet <=> EC2 Subnetを許可します。

/etc/ipsec-tools.d/20-vpc-private.conf

そして、QuaggaにCustomerSubnetを書くことで、EC2側のルーティングに追加し、EC2 => Customerに返ってこれるようにします。

/etc/quagga/bgpd.conf

そして setkey と quagga を再起動します。

これで、Customer ClientからPingを打った時に、EC2側で tcpdump icmp していると、今までsrcが 169.254.252.2 or 169.254.252.6 だったのが 172.30.4.20 になり、返りのルートも vgw となっているため見事往復できることになります。もちろん、VPNからの通信も変わりなくできる状態です。


NATなしでは正当な冗長化ができない理由

変更した設定には1つ不備に見える部分があります。setkeyです。
Tunnel 1つ分の設定しかないので本来こう、したいところです…

/etc/ipsec-tools.d/20-vpc-private.conf

…が、ダメッ… です。左側のSubnetのSrc/Dstになっている部分は、同じ条件で複数設定できず後者がエラーになるためです。そうなると当然、フェイルオーバーした時に許可していない方のトンネルを使おうとすると、通信ができなくなる、という単純な罠が待ち受けています。

この問題、AWS業界では2011年頃には既に知られていて、SPD の設定を動的に、もしくは複数設定できない、という理由からLinuxで冗長化するのは不可能だと結論が出ていて今なお変わらないようです。

対応案としては、2つ見つかりました。

  • どちらかのTunnel分しかspdaddしない(=冗長化は捨てる)
  • どちらか片方のTunnelしか利用しないと割り切る

  • 完全にイケてねー状態です。
    で、それぞれの対応案を工夫する形で外道式を考えてみることにしました。


    片方しかspdaddできない問題を解決する

    Private間用のsetkeyの設定を、initスクリプトが実行しない形で用意します。
    中身は、設定中のTunnelを消してもう片方のTunnelを追加するだけのものです。

    /etc/ipsec-tools.d/20-vpc-private.standby1

    /etc/ipsec-tools.d/20-vpc-private.standby2

    そして各TunnelへのPingを監視しておいて、到達しなくなったら自動的に切り替えるという作戦です。

    一見上手くいきそうだったのですが、問題が1つありました。
    Customer => EC2 と EC2 => Customer の通信において、
    Customer => EC2 は Quagga が、
    EC2 => Customer は VPC VPN G/W が
    どちらのTunnelを使うか決定しており、その組み合わせは4種類あることになります。
    往復が同じTunnelの場合もあるし、異なる場合もありどちらも動きます。

    これ、Quaggaの選択状況は把握できるのですが、VPC側の選択状況は取得できません。
    Tunnelを2本とも利用できる状態において、setkeyとquaggaの設定を、VPC側が選択しているTunnelと同じにしなくてはいけないのですが、色々調べてみても唯一の切り替え判断は Client <=> EC2 の疎通ができているか、なので、それをVPNサーバで行うわけにもいきません。

    何度も切り替えたり、iptablesで遮断してTunnelDownを偽装してみると全然思い通りに動かなく、setkey切り替え作戦は失敗に終わりました。


    片系のTunnelしか利用せず丸ごと切り替える

    経路に問題があるなら片方しか選べないようにすればえぇんや!
    と半ばキレ気味に、racoon, setkey, quagga(bgpd) の設定を2つ用意し、monit監視でTunnelダウン検知時に設定ファイルごと切り替えて再起動する、という荒技を行うことにしました。

    こんな感じです。

    racoon

    VPN1の場合はこの設定。VPN2はG/WとTunnelSegmentを変えるだけです。
    ここで注意すべきは、鍵ファイルは分けずに2件書いて共通して使う必要があるということです。

    /etc/racoon/racoon.conf

    ipsec-tools

    Tunnel部分は共通設定となります。

    /etc/ipsec-tools.d/10-vpc-tunnels.conf

    PrivateSubnet間の設定は分けておきます。これはVPN1用です。

    /etc/ipsec-tools.d/20-vpc-private.conf

    quagga

    /etc/quagga/zebra.conf に変更はありません。
    BGPのVPN1用はこうです。VPN2は networkのTunnelSubnetと、neighborのTunnelAddressを変えるだけです。

    /etc/quagga/bgpd.conf

    monit

    そして監視用にmonitを入れます。

    監視設定はヘルスチェックスクリプトと、Tunnel切り替えスクリプトを使います。

    /etc/monit/conf.d/aws-vpc.cfg

    monit用ヘルスチェックスクリプト

    現行片VPNの、VPC側のTunnelAddressにPingが飛ばない場合、エラーと判定します。

    /usr/local/bin/aws-vpc-healthcheck.sh

    monit用Tunnel切り替えスクリプト

    実行すると、現行片VPNではないもう1つのVPNの設定に切り替えてrestartします。

    /usr/local/bin/aws-vpc-switch.sh

    スクリプトを見たらわかりますが、VPN1,2それぞれの上書き用設定は、ここでは /usr/local/etc/aws-vpc/vpn1, 2 に置いておき、切替時に本来のパスへ cp しているだけになります。

    用意できたら、1回切り替えスクリプトを実行して、monit を起動しておけば、ひたすら片肺飛行を繰り返してくれる、なかなかグロい構成になっています。

    問題点

    この設定には1つ問題があります。

    本来すべき2Tunnelの設定をしていないせいか、切替時になかなかracoonがTunnel確立をできず、長いと1分以上かかる場合もあります。それゆえに、監視間隔時間の調整が大切になります。

    そしてこの切替時間は、非NAT化のために犠牲にできる長さなのか、という話になります。それについては用途の問題で、仮にSSH接続だけなら問題ない範囲と捉えますし、重要なレプリケーションなどをするならNAT型にする、というかおとなしくハードウェアにした方がよいと思います。


    完全切替型の構築スクリプト

    お世辞にも綺麗な構成とは言えませんが、それでも構築スクリプトを置いておきます。

  • Gist : GedowFather / aws_vpn_between_vpcgw_and_debian_with_monit_for_non_nat.sh



  • せっかく調べたから書きましたが、いやー汚いですねコレ。
    もしかしたら microインスタンスでOpenVPNの方がマシかもしれません。

    相変わらず自分のネットワーク系のセンスの無さというか、撤退や妥協が下手というか、身にしみた案件でした。