AWS VPN (1) between VPC VPN G/W and Debian Linux (racoon, setkey, quagga) with NAT

AWSのVPCにはVPN機能がついているので、利用すると社内など任意のサブネットとVPCの間を、Privateアドレスで暗号化通信できるようになります。

通常このVPC VPNは、ハードウェアルーターを用いて接続するべく、一般的なルータの設定ファイルを配布してくれる至れり尽くせりな環境なのですが、ウチは貧乏でLinuxが大好きなので、
Debian Wheezyで接続を試みるのでした。



2つの方法

Linuxでは私が最初に構想した、綺麗で完全な冗長化構成はできないことがわかり、妥協案として2つの構成案ができたため、2つのエントリに分けて書くことにします。その2つの案とは、

  • 綺麗な冗長化+NAT
  • 泥臭い冗長化+非NAT

  • で、まずは概要と、前者の構築手順を紹介し、
    後者は2つ目のエントリとします。


    全体図

    まずはVPNに必要な登場人物とネットワーク情報です。


    Customer側は人によって異なるところなので簡単に説明しておくと、
  • Customer VPNはVPC G/Wに対してVPN用通信が通ればGlobalAddress不要なので、他サーバにNATしてもらいWANに出ていく
  • Customer ClientはEC2サブネットへのRouting G/Wに、Customer VPNを指定
  • Customer ClientからEC2へ直接Ping, SSHができることを目的とする

  • Customer VPN自体の冗長化は今回は省略します。
    必要ならLVSとmonitなどの組み合わせで実現可能なはずです。


    使用するソフトウェア

    最初はOpenSWANでやろうとしたのですが、どうやっても最終形態に届きそうになかったのですぐ諦めました。

  • setkey (ipsec-tools パッケージ) で セキュリティポリシーを定め、
  • racoon でVPN接続のトンネルを掘り、
  • quagga でルーティングの動的切り替えを行なう ことにしました

  • 例によってネットワーク知識が非常に乏しいなりに、簡単に役割を説明をしてみます。
    (※それぞれの詳細はググってね!)

    setkey (ipsec-tools)


    IPsecを利用する通信を、セキュリティポリシーという名目で指定します。
    詳しくはSPAやSPDでググるとよいですが、簡潔にはsrc/dstの組の許可という感じです。

    Tunnelを掘るためにも必要ですし、VPN確立後に通るClient間のFORWARDパケットも関係します。

    racoon


    VPC G/WとTunnelの情報、鍵、暗号化方式などを指定してVPN接続を確立します。OpenVPNと違って、Tunnel Addressは自動的に割り当ててくれないので、eth0 などのaliasとして自分で割り当てる必要があります。

    setkeyとracoonの設定が正しければ、両方を有効にした時点で
    VPC側の2つのTunnel AddressにPingを飛ばすことができます。

    quagga


    routeやip rで見える、通常のルーティングを動的に設定します。目的は、EC2サブネットへのG/Wを2つのTunnelどちらかに設定することで、両方とも生きていればどちらかを選び、利用中のTunnelが落ちれば、自動的にもう片方に切り替えてくれます。


    VPC Management Consoleでやっておくこと

    VPCでVPN付きPrivateNetworkを作成して、そこにEC2を作成する、までは済んでいるとします。それ以降で注意すべき点がいくつかあるので、上手くいかない時はこの辺を点検してみてください。

    VPNのCustomerGateway

    このCustomerGatewayとは、VPC G/Wから見たCustomer側のSourceAddressになります。上の全体図で言えばNATしてWANに出ていく 1.2.3.4 になります。CustomerVPNサーバがGlobalAddressを持っていれば、それになります。

    最初にracoonがTunnelを掘る時、VPC G/W に UDP:500 とかで繋ぐのですが、CustomerGatewayに登録したSourceAddressでないと、そのレスポンスを返してくれないようになっています。

    VPN Routing

    quaggaにより動的ルーティングにするので、staticではなく
    Use dynamic routing (requires BGP) を選択してください。

    staticにしておくと、ダウンロードする設定ファイルにBGP関連が載りません。

    Security Group

    接続したいEC2インスタンスが所属するSecurityGroupを編集し、
    INPUTのICMP, SSH辺りを通しておいてください。
    最初は src 0.0.0.0/0 にしておいて、ひと通り成功してから制限すればよいです。

    VPN設定のダウンロード

    VPN Connections => Download Configuration で
    Genericの設定をダウンロードしておきます。
    この中の設定値を用いて、各種設定をしていきます。
    (※後述する構築スクリプトに使うことで全自動で構築できます)


    Customerの環境でやっておくこと

    CustomerVPNからVPC G/Wに対して、以下の通信が通れる必要があります。
  • UDP : 500
  • TCP : 500 (たまに使われる)
  • ESP

  • CustomerVPNがGlobalAddressを持っていればOUTPUTの許可になりますが、今回は他サーバにFORWARDしてもらってるので、途中のG/Wでこのように許可しました。


    CustomerVPNの構築

    では1つ目である、綺麗な冗長化+VPNサーバでのNAT 構成でいきます。
    各設定の設定名:値は、ダウンロードした Generic設定を参照してください。

    sysctlの設定

    他クライアントのG/Wとなるので、FORWARDを許可しておきます。

    /etc/sysctl.d/vpn.conf

    反映します。

    Clientのルーティング異常対策

    send_redirects を 0 に設定している理由は、1 のままだとquaggaによりルーティングが書き換えられた時に、Clientのルーティングキャッシュが勝手に書き換わって通信できなくなることがあり、それを防ぐためです。例えばこんな感じ。

    パッケージのインストール

    入れます。

    racoonのログ設定

    ログディレクトリを作成します。

    デーモンにオプションをくっつけます。

    /etc/default/racoon

    ログローテートの設定をしておきます。(めんどくて copytruncate)

    /etc/logrotate.d/racoon

    鍵の設定

    VPC G/WのアドレスとPre-Shared Key の組を2つ書いておきます。

    /etc/racoon/aws-vpc.txt

    racoonの設定

    racoonの設定は色々あるように見えて、相手がVPCである以上はほぼ固定です。
    リージョンが変わると、G/WやTunnelSubnetが若干変わるみたいです。

    /etc/racoon/racoon.conf

    ipsec-tools の設定

    ipsec-tools はデーモンはなく、initスクリプトで setkey を実行しているだけです。
    Tunnel同士の通信と、CustomerVPN<=>EC2間の、2Tunnel分を許可しています。

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

    quaggaの設定

    色々あるけど bgpd と zebra さんを有効にします。

    /etc/quagga/daemons

    Quaggaの前身がZebraだとかかんとか。で基本設定になります。

    /etc/quagga/zebra.conf

    BGPの設定をします。

  • ! はコメントアウトです
  • router bgp, neighbor, remtote-as はGeneric設定の BGP Configuration Options 近辺に書いてあります。
  • ここで指定した network のセグメントは、VPC ConsoleにおけるRoute Tablesに追加されます
  • networkのTunnelセグメントは必須ですが、Customerセグメントは非NATの場合のみ必要です

  • /etc/quagga/bgpd.conf

    Tunnelアドレスの設定

    Tunnelアドレスは自主的にエイリアスとして設定します。

    OS起動時にも設定されるようにします。
    OpenStackのVMなのでDHCPですが気にしないでください。

    /etc/network/interfaces

    VPN開始

    まずsetkey, racoonを開始します。
    起動後はTunnelの接続チェックします。

    Tunnelが通れば、quaggaを起動してEC2へのルーティングを確保します。
    少し時間がかかる場合がありますが、成功すればCustomerVPNサーバからEC2へPingが飛ぶはずです。


    from Client to EC2 with NAT

    ここからさらに、Customer ClientからEC2へ接続できるようにします。

    CustomerVPNのiptables

    G/WとなるのでFORWARDを許可し、NATして出て行くようにします。
    これにより、EC2側ではSrcが 169.254.252.2 or 169.254.252.6 に見えます。

    Customer Clientのルーティング

    EC2ゆきの接続を、VPNサーバに転送をお願いするように設定します。起動時にも有効にする場合は /etc/network/if-up.d/ のどこかに書いておいてください。

    これで、ClientからEC2にPingできるようになります。
    VPNやEC2上で tcpdump -i any -n icmp とか出しておくとよいです。


    NAT構成の構築スクリプト

    今回のNAT型の構築はスクリプトにしてあります。VPC Consoleから落としたGenericのVPN Configurationを指定して実行するだけで、VPC VPNとの接続ができます。

  • Gist : GedowFather / aws_vpn_between_vpcgw_and_debian_with_nat.sh
  • ※1 iptablesの部分は各環境に合わせて変更してください
  • ※2 Clientのルーティング設定は別途行ってください

  • こんな感じです。


    これでPrivate上のEC2にSSHするという目的は達成していますが、NATするとEC2側から見た時のSrcアドレスがCustomerVPNのTunnelアドレスになってよろしくないので、ここからNATを外して利用する方法を次のエントリとします。