読者です 読者をやめる 読者になる 読者になる

Mercari Engineering Blog

メルカリのエンジニアブログです。技術情報を日々発信していきます。

Consulを利用したTLSセッションチケットの自動更新

nginx

Site Reliability Engineering Team(通称SRE)の@cubicdaiyaです。最近チーム名が変わりました。

今回はConsulを利用して複数台のnginxサーバのTLSセッションチケットを自動更新する仕組みについて紹介します。

TLSセッションチケット

TLSセッションチケットは簡単に言うとTLSのセッション情報を暗号化してクライアント側に保存することで HTTPS通信時に行われるTLSハンドシェイクの手順を省略してネットワークレイテンシを削減するための仕組みです。(詳細については一番下の参考情報を御覧ください)

似たような仕組みとしてTLSセッションキャッシュがありますが、こちらはセッション情報をサーバ側に保存します。

HTTPS通信ではTCPのハンドシェイクに加えてTLSのハンドシェイクが必要になるのでHTTP通信よりもネットワークのレイテンシが大きくなりますが、 これらの仕組みを利用することでレイテンシを最小限に抑えることができるようになります。

nginxとTLSセッションチケット

nginxでは ngx_http_ssl_module が組み込まれている状態であれば特に何もしなくても最初からTLSセッションチケットが有効になっていますが、 明示的にOn/Offを切り替えるにはssl_session_ticketsディレクティブを利用します。

ssl_session_tickets on; # 無効にする場合はoff

ただし、この場合はnginxを再起動する度にチケットキーが生成・更新されるので複数台サーバ間でチケットキーを共有するには以下のように ssl_session_ticket_keyディレクティブを利用してチケットキーをファイルとして保存する必要があります。

ssl_session_ticket_key /path/to/tls_session_ticket.key;

チケットキーは48バイトのバイナリで、openssl コマンドで生成することができます。

openssl rand 48 > tls_session_ticket.key

TLSのセッション情報のサーバ間共有

先述の通りTLSセッションキャッシュやTLSセッションチケットを利用するとHTTPS通信時のネットワークレイテンシを削減することが可能ですが、 HTTPSサーバが複数台ある場合、TLSのセッション情報をどのように共有するか、という技術的および運用上の課題があります。

例えば、nginxのTLSセッションキャッシュの仕組みだとセッション情報を共有メモリ上に保存するので複数サーバ間でセッション情報を共有することができません。 TLSセッションチケットであれば各nginxサーバ間で同一のチケットキーファイルを指定することでセッション情報を複数サーバ間で共有できますが、 仕組み上チケットキーは定期的に更新することが望ましいので、nginxの再起動に合わせて各サーバ上のチケットキーを更新する仕組みを考える必要があります。

メルカリではnginxでTLSセッションキャッシュとTLSセッションチケットを併用しつつ、Consulのイベント通知機能を利用して 複数台あるnginxサーバのチケットキーファイルの更新およびnginxのリロードを一定間隔で自動で行うようにしています。

Consulを利用したTLSセッションチケットの自動更新

Consulには管理下にあるクラスタ内の特定のノードに対してイベントを通知する機能(以下consul event)があります。 consul eventは合わせてペイロードを送ることができるのでこれにbase64エンコードしたチケットキーのバイナリデータを載せます。

$ consul event -name="ssl-session-ticket-refresh" `openssl rand 48 | base64`

また、各nginxサーバ上には以下の設定をあらかじめ用意しておきます。

{
    "watches": [
        {
            "type": "event",
            "name": "ssl-session-ticket-refresh",
            "handler": "/usr/local/bin/ssl_session_ticket_refresh"
        }
    ]
}

上記のconsul eventが実行されると各nginxサーバ上のconsul-agentを経由してハンドラ(ssl_session_ticket_refresh)が実行されるというわけです。

f:id:cubicdaiya:20151110194847j:plain

あとは起動したハンドラには標準入力でペイロードを含むJSONが送られてくるのでこれを元にチケットキーファイルの更新およびnginxのリロードを行うことができます。 また、一斉にリロードが走らないようにサーバ毎にランダムにディレイをかけるといったこともしています。

参考