nelmoの日記帳

エンジニア見習いの備忘録とかです。

AWS ELBのProxy Protocolを触ってみた

Amazon Web Serviceブログに7/31付で発表がありましたが、ELBでProxy Protocolがサポートされました。

これにより、TCP接続の負荷分散でも、接続元IPアドレスの取得が可能になったようです。

Proxy Protocol?

ELBでは、(2013/08/04現在)以下のプロトコルをサポートしています。

HTTPとHTTPSに関してはX-Forwarded-Forを見ることで接続元クライアントのIPアドレスを知ることができましたが、TCPの負荷分散ではヘッダが付与されないため、取得することができませんでした。

しかし、先日追加されたELB Proxy Protocolで、この機能を有効化した場合、TCPデータ内に接続元IPアドレス情報が付加されるようになりました。

というわけで、ELBの設定方法と接続元IPアドレスの取得方法について調べてみました。

検証

以下の手順で検証しました。(かなり簡易的な検証となっています)

  1. EC2上にインスタンスを2台作成し、各サーバにMySQLをインストール
  2. ELBを作成し、作成したインスタンスを関連づける
  3. 作成したELBに対し、Proxy Protocolを有効にする
  4. telnetを叩き、通信をEC2インスタンス上でtcpdumpして取得する

MySQLインストール

特筆することはないです。いつも通りさくっと。

$ sudo yum -y install mysql-server
$ sudo service mysqld start

ELB作成

ELB自体の作成は、Management Consoleからさくっとやってしまいます。 EC2に作成したインスタンスMySQLポートに分散するように設定しましょう。

f:id:Canelmo:20130804161444j:plain

最初の画面で設定したBalancer Nameは次の手順で利用します。

Proxy Protocolを有効にする

ここが本番です。

Proxy Protocolの有効化、Management Consoleからは行うことができません。 現状はAPI経由でのみ行うことができるようです。

ELBのDocumentationでは、旧来の(?)CLIが使われているので、私はPython版CLIで検証してみました。

まず、Policyの情報を取得します。

$ aws elb describe-load-balancer-policy-types | jq '.PolicyTypeDescriptions[] | select(.PolicyTypeName=="ProxyProtocolPolicyType")'
{
  "PolicyAttributeTypeDescriptions": [
    {
      "Cardinality": "ONE",
      "AttributeName": "ProxyProtocol",
      "AttributeType": "Boolean"
    }
  ],
  "PolicyTypeName": "ProxyProtocolPolicyType",
  "Description": "Policy that controls whether to include the IP address and port of the originating request for TCP messages. This policy operates on TCP/SSL listeners only"
}

ProxyProtocolPolicyTypeの設定値をTrueにしてあげればいいようです。 ので、次のコマンドを実行。

オプションの--load-balancer-name の部分は、さっき作成したELBにつけた名前を設定して下さい。

--policy-nameはこのpolicyの名前になるものです。今回は[proxy-protocol-policy]としています。

$ aws elb create-load-balancer-policy          \
    --policy-name proxy-protocol-policy        \
    --load-balancer-name proxy-protocol        \
    --policy-type-name ProxyProtocolPolicyType \
    --policy-attributes '[{"attribute_name":"ProxyProtocol","attribute_value":"True"}]'

{
    "ResponseMetadata": {
        "RequestId": "eadec4c1-fcc9-11e2-9925-39cfc649e8ce"
    }
}

今作成したPolicyを、既存のELB配下のインスタンス群の3306番ポートに対して適用します。

$ aws elb set-load-balancer-policies-for-backend-server \
    --load-balancer-name proxy-protocol                 \
    --policy-name proxy-protocol-policy                 \
    --instance-port 3306

{
    "ResponseMetadata": {
        "RequestId": "2dd9fbcf-fcca-11e2-9925-39cfc649e8ce"
    }
}

$ aws elb describe-load-balancers
(...前略)
        "Policies": {
            "LBCookieStickinessPolicies": [],
            "AppCookieStickinessPolicies": [],
            "OtherPolicies": [
                "proxy-protocol-policy"
            ]
        },
        "Scheme": "internet-facing",
        "VPCId": "vpc-62cb0a0a",
        "CanonicalHostedZoneNameID": "Z2YN17T5R711GT",
        "BackendServerDescriptions": [
            {
                "PolicyNames": [
                    "proxy-protocol-policy"
                ],
                "InstancePort": 3306
            }
        ],
(...後略)

これで、Proxy Protocolが有効になりました。

telnet & tcpdump

皆さん大好きtcpdumpで、接続元のIPアドレスがとれているかどうか確認してみます。

$ sudo tcpdump -w packet.dump tcp port 3306

ローカルのマシンから、telnetで接続を確認します。

$ telnet proxy-protocol-1621762185.ap-northeast-1.elb.amazonaws.com 3306

で、キャプチャしたものをWiresharkで見てみます。

f:id:Canelmo:20130804161602p:plain

モザイクで編集してしまいましたが、IPアドレスと、ポート番号(のようなもの)が2つずつあります。

モザイクの黒っぽい部分に、接続元のIPアドレスが書いてあります。白っぽいモザイクの方には、プライベート帯のIPアドレスがありました。ELBの内部アドレスでしょうか。

疑問点

ここまでやって本日は時間切れ。

残った疑問点としては、

  • 2つめのIPアドレスとポート番号の正体
  • 自前アプリケーションからの取得方法

ここらへんはまた近いうちに。