ALBのSSLターミネーションとACM:現場で迷わない「暗号化の境界線」の設計術
こんにちは。現場で泥臭いパケット解析と格闘し続けてきたSREです。
クラウドインフラを設計する際、避けて通れないのが「どこで暗号化を解くか」という問いです。ALB(Application Load Balancer)のSSL/TLSターミネーションは、AWSにおけるWeb API設計の標準的な構成ですが、意外と「なぜその構成にするのか」という本質を飛ばして、とりあえずACMを紐付けて終わり、というケースをよく見かけます。
今回は、パケットがALBに到達してからバックエンドに届くまでの裏側を紐解きつつ、実務で絶対に外してはいけないポイントを整理しましょう。
—
1. なぜALBでSSLを「終端」させるのか?
結論から言うと、「計算負荷のオフロード」と「証明書管理の一元化」が最大の理由です。
TLSハンドシェイクは、公開鍵暗号方式を用いるためCPUリソースをそれなりに消費します。これをALBというマネージドな層に丸投げすることで、アプリケーションサーバー(EC2やFargate)は、本来のビジネスロジック処理に全力を注げるようになります。
通信のシーケンス:パケットの旅路
1. Client -> ALB: インターネット経由のHTTPS通信。ここでALBがACMの証明書を使ってハンドシェイクを完了します。
2. ALB -> Backend: ここが分岐点。ALBは復号された平文のHTTPパケットをバックエンドに転送します。
3. Backend -> Client: レスポンスは逆順を辿り、ALBで再び暗号化されてクライアントへ届きます。
この構成において、クライアントからは「暗号化された安全な通信」に見えても、VPC内のプライベートネットワーク上では平文で流れているという点には注意が必要です。
—
2. ACM連携のキモ:設定と注意点
AWS Certificate Manager (ACM) は、証明書の更新を自動化してくれるエンジニアの「救世主」です。ALBの設定自体はシンプルですが、実務でハマりやすいのが「セキュリティポリシー」の選択です。
推奨されるセキュリティポリシー
ALBのリスナー設定で選ぶ ELBSecurityPolicy-TLS-1-2-2017-01 などのポリシーは、クライアントがサポートすべきTLSバージョンを定義します。
ELBSecurityPolicy-2016-08: 互換性重視(古めのモバイル端末向け)ELBSecurityPolicy-TLS-1-2-2017-01: 最低限のセキュリティ基準(現代のAPI開発ならここから)
古いブラウザやクライアントを切り捨てる勇気があるなら、TLS-1-2-PFS など、Forward Secrecy(前方秘匿性)を強制するポリシーを選択してください。
—
3. 実践:ALBとバックエンドの通信を確認する
ALBがバックエンドへ転送する際、HTTPヘッダーには「実はSSL経由だった」という情報が付与されます。これを適切に拾わないと、アプリ側で無限リダイレクトループが発生することがあります。
ALBが付与する主なヘッダー
X-Forwarded-For: クライアントのIPアドレスX-Forwarded-Proto: クライアントが使用したプロトコル(httpsかhttpか)X-Forwarded-Port: クライアントが接続したポート
Flask(Python)での受け取り例
バックエンドのWebサーバーで、ALB経由の通信かどうかを判定するロジック例です。
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
# ALB経由でhttpsアクセスされたか確認
proto = request.headers.get('X-Forwarded-Proto')
if proto == 'https':
return "セキュアな通信が確立されています。"
else:
return "注意:暗号化されていない通信経路です。"
# 実際の実務では、信頼できるALBのIP範囲からのみ
# これらのヘッダーを許可する設定をミドルウェア等で実装します
—
4. トラブルシューティングの勘所
「APIが動かない!」という連絡を受けたとき、まず確認すべきは以下の3点です。
1. ACM証明書のステータス: ISSUED になっていますか? DNS検証が完了していないと、ALBはリスナーを作成できません。
2. ターゲットグループのヘルスチェック: ALBのリスナーをHTTPSにしていても、ターゲットグループ(バックエンド)のポート設定が一致していないと、ALBは「502 Bad Gateway」を返します。
3. セキュリティグループの穴: ALBからバックエンドへの通信(例: ポート80/8080)が、ターゲットグループのセキュリティグループで許可されているか確認してください。
curlでのデバッグコマンド
ALBのエンドポイントに対して、SSLの詳細を確認するコマンドです。
# 証明書の有効期限や暗号スイートを確認する
curl -Iv https://your-api-endpoint.com/ 2>&1 | grep -E "SSL|Certificate"
# 期待通りのレスポンスが返るか確認
curl -H "X-Forwarded-Proto: https" http://your-internal-backend:8080/health
—
最後に:再暗号化(HTTPS to HTTPS)の考え方
最後に、「ALBからバックエンドもHTTPSにすべきか?」という議論について。
「VPC内は閉域だから平文でOK」という考え方が主流ですが、金融や医療など、コンプライアンス要件で「エンドツーエンドの暗号化」が求められる場合は、ALBからバックエンド間もHTTPS通信を行います。
その場合、バックエンドサーバーにも証明書が必要になります(ACMの証明書はALB等のAWSリソースにしかインストールできないため、バックエンド用には別途オレオレ証明書か、内部CAが発行した証明書を用意することになります)。
結論: 特別な要件がない限り、ALBでSSLを終端してバックエンドは平文にする構成が、運用コストとパフォーマンスのバランスが最も優れています。
インフラは「繋がって当たり前」の世界ですが、その裏側にある技術的必然性を理解しているだけで、障害発生時の初動速度が劇的に変わります。ぜひ、設計図を眺める際は「パケットがどこで暗号を解いているか」を想像してみてください。
コメント