サービス概要

昨年のre:Invent 2023で発表されたアップデートにより、ALBで相互TLS(mutual TLS)認証(クライアント認証)ができるようになりました。相互TLSには、X.509v3クライアント証明書を検証するための2つのオプションがあり、そのうちの一つがトラストストア検証です。
トラストストア検証とは、ALBがTLS接続をネゴシエートするときに、クライアントに対してX.509クライアント証明書認証を実行して互いのIDを検証し、TLS接続を確立して互いの間の通信を暗号化します。通常、相互TLS検証モードといった場合はこちらのトラストストア検証を指します。

目的・やりたいこと

マイクロサービスを統合し、セキュアで柔軟なAPIマネジメントを実現する。

ユースケース:

  • 既存のオンプレミスシステムと新規クラウドサービスを統合し、一貫したAPI管理を行う
  • 複数の部門や事業部が独自に開発したサービスを、統一されたインターフェースで外部に公開したい

上記の目的を達成するため、トラストストア検証で、ALBの後段にAPI Gatewayする構成が有効かどうかを検証する。

以下要件:

  • ALBでmTLSトラストストア検証を有効、S3に設置したCA証明書、失効リストの設定を行いALBでクライアント認証行う
  • ALBでセキュリティポリシーをTLS1.3のみ有効なポリシーを当てる

API Gateway への到達性をカスタムドメイン(例:https://api.example.com)を介して提供します。カスタムドメイン名のDNSレコードは、Route 53サービスによってホストされます。デプロイに必要な公開SSL証明書を作成するために、AWS Certificate Managerを使用します。

対象となる技術

  • ALB(Application Load Balancer)
  • 相互TLS(トラストストア)検証
  • Amazon API Gateway

条件(証明書の要件)

相互TLS認証で使用される証明書について、以下をサポートしています。

  • サポートされている証明書:X.509v3
  • サポートされているパブリックキー:RSA 2K–8K または ECDSA secp256r1、secp384r1、secp521r1
  • サポートされている署名アルゴリズム:SHA256、384、512 と RSA/SHA256、384、512 と EC/SHA256、384、512ハッシュとMGF1のRSASSA-PSS

(参考)Application Load Balancer で相互 TLS の設定を開始する前に

参考URL

注意事項

概要図

作業の流れ

事前作業

1.APIエンドポイントの作成
VPC > エンドポイント > [エンドポイントを作成]

「AWSのサービス」で「com.amazonaws.ap-northeast-1.execute-api」サービスを選択

AZは今回検証のため1つだけ選択

残りはデフォルトのまま[エンドポイントの作成]
エンドポイントが作成されたら、以下の2つのDNS名を確認

nslookupでIPを見ると2つとも同じIPが返ってきたので、これをメモ

# nslookup vpce-****.execute-api.ap-northeast-1.vpce.amazonaws.com
Non-authoritative answer:
Name:   vpce-****.execute-api.ap-northeast-1.vpce.amazonaws.com
Address: 10.0.22.**

# nslookup vpce-****-ap-northeast-1a.execute-api.ap-northeast-1.vpce.amazonaws.com
Non-authoritative answer:
Name:   vpce-****-ap-northeast-1a.execute-api.ap-northeast-1.vpce.amazonaws.com
Address: 10.0.22.**

2.APIゲートウェイの作成
API Gateway > API > [APIを作成]

REST APIを[構築]
「サンプル API」を選択

エンドポイントタイプは「プライベート」にし、エンドポイントIDに1.で作成したAPIエンドポイントIDを選択、最後に[APIを作成]

3.リソースポリシーの作成
API > リソースポリシー > [ポリシーを作成]

テンプレートを選択 から[ソースVPC許可リスト]を選び、検証なのでこのように比較的緩めのポリシーを作成

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "execute-api:/*/*/*",
            "Condition": {
                "StringNotEquals": {
                    "aws:sourceVpc": "vpc-****"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "execute-api:/*/*/*"
        }
    ]
}

4.接続テスト
ポリシーを作成するとデプロイができる

インターネット経由だとWebアクセスできないので、パブリックサブネットにある踏み台WindowsからWebアクセスして接続確認

見れない方はエンドポイントのセキュリティグループを確認してみてください。自分は最初それがデフォルトのままでhttpsアクセスが許可されておらず、若干ハマりました。

5.ACM証明書の取得
自分は「nozaki2.com」ドメインをお名前.comで取得していたので、「*.nozaki2.com」でACM証明書を取得しました。

手順

API Gatewayの事前準備が終わったので、ALBを作成していきます。

1.ターゲットグループの作成
ターゲットタイプは「IPアドレス」を選択
ヘルスチェックには「HTTPS」を使用し、プロトコルバージョンとして「HTTP1」を指定

[ヘルスチェックの詳細設定]を開き、「成功コード」の値を「403」に変更

注: ALB は、VPC エンドポイントの IP アドレスに対して HTTPS リクエストを送信して API Gateway のヘルスを検証します。API Gateway は、ヘルスチェックのプローブ中に正しいドメイン名とステージの URL を提供しないため、403コード(アクセス禁止)で応答します。

事前準備1.でメモしたIPをターゲットとして追加

2.ALBの作成
通常通りALBを作成。ターゲットグループには1.で作成した「nozaki-privateapi-TG」を選択

ここでは一旦まずmTLS無しで作成して、接続確認を優先

また、念のためポリシーでTLS 1.3になっていることを確認しておきます。
(ユーザーからのエンドポイントであるALB側でTLS1.3のみで受け付けられれば、ALB→プライベートAPIはTLS1.2でよいと考えています)

Web接続テスト

ところがこの状態でWeb接続確認してみたところ、{“message”:”Forbidden”}となりました。エンドポイントのDNSやIPに直接アクセスしても同様に{“message”:”Forbidden”}となります。

手順追加

何か設定が足りていないのだろうと、以下を追加することにしました。

  • カスタムドメインの設定
  • エイリアスAレコードの設定

3.カスタムドメインの設定
APIマッピングを使用して、APIステージをカスタムドメイン名に関連づける必要があることがわかりました。デプロイメント用にカスタムドメインを作成します。ドメイン名は、ACM証明書に含まれている必要があります。

3.1. API Gatewayコンソールで、カスタムドメイン名 > [作成]

3.2. ドメイン名の詳細

  • ドメイン名:適当に入力
  • TLSの最小バージョン:デフォルトのまま
    ただし、相互TLS認証はあくまでALBにやらせるので、ここでは有効にしません。

3.3. エンドポイント設定
ここで困りました。選択できるエンドポイントタイプがリージョン or エッジ最適化しかありません。あれ?プライベート選べないの?と思いましたが、どっちを選んでもいいのです。
ACM証明書は事前作業 5.で作成したACMを選びます。

以上で[ドメイン名を作成]

3.4. APIマッピング
作成したドメイン名を選択し、[APIマッピング]タブ > [APIマッピングを設定]

「dev」ステージを選択して[保存]

4.エイリアスAレコードの設定
上記でカスタムドメイン名とAPIステージの関連付けが無事完了したので、今度はそのカスタムドメインでWebアクセスした時にALBに向けられるよう、DNSレコードを設定する必要があります。
外部からのアクセスを想定しているため、Route 53で「nozaki2.com」のパブリックホストゾーンを作成しておきます。
[レコードを作成]でこのようなエイリアスAレコードを作成

今度こそ気を取り直して再度Web接続テスト

「aaa.nozaki2.com」にWebアクセスし、無事API Gatewayに設定したPet Storeページが表示されました!

次はいよいよ相互トラストストアを設定していきます。

手順追加(相互トラストストア設定)

相互トラストストアは、トラストストアの作成が結構面倒なので、先にトラストストアを単独で作成しておきます。そのためにはクライアント証明書を作成する必要があります。

5.クライアント証明書の作成
opensslコマンドで秘密鍵(root_key.pem)、ルートCA証明書(root_cert.pem)、クライアント証明書(client_cert.pem)の順に作成していきます。生成されたルートCA証明書はトラストストアに、クライアント証明書はクライアント認証で使います。

6.トラストストアの作成
ロードバランシング > トラストストア > [トラストストアを作成]

5.で作成したルートCA証明書(root_cert.pem)をS3のどこかに上げておき、そのS3 URIパスを認証局バンドルとして指定

そのほかはデフォルトのまま、[トラストストアを作成]

※注意
ここでルートCA証明書が正しい形式で作成されていないと、[トラストストアを作成]しても、下記のような「The basic constraints extension must specify that the certificate is for a CA(基本制約では、証明書がCA用であることを指定しなければならない)」という謎のエラーが出てしまいます(ググっても出てきません。。)

ルートCA証明書の中身が以下のようなフォーマットになっていることを確認してください。自分はここでハマりました。

-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgIUQREbhxa83orBI9vac76/sr5JCvIwDQYJKoZIhvcNAQEL
〜〜〜〜〜
W71mqNugfiXQC0GuPZdCs/WillAsHhlp+A==
-----END CERTIFICATE-----

7.ALBの設定変更
再度ALBの設定に戻り、[リスナーとルール]タブ > [ルールを管理] > [ルールの編集]

「クライアント証明書の処理」で今度は相互認証を有効にし、「トラストストアで検証」を選択、トラストストアには6.で作成したトラストストア(nozaki-truststore)を選択して、[変更内容の保存]

再度Web接続テストで最終確認

ここで接続テストはWindows上で行いました。このため、クライアント証明書を.pfx形式に変換してブラウザに入れておきます。
「aaa.nozaki2.com」に再度Webアクセスすると、今度はクライアント証明書を求められるダイアログが表示されます。

正しい証明書を選択すると、無事Pet Storeのページが表示されます。

ちなみに間違った証明書を選択したり、キャンセルしたりすると、「このサイトにアクセスできません」として拒否できていることが確認できます。

以上により、ALB(トラストストア) → API Gatewayエンドポイント → API Gateway(プライベートAPI) でクライアント認証経由で通ることを確認できました。

まとめ

  • ALBでAPI Gatewayをターゲットにしたい場合は、API GatewayエンドポイントのIPアドレスを指定する
  • プライベートAPIはTLSv1.2のみサポート
  • ALB経由でAPI GatewayにWebアクセスするには、「カスタムドメインの設定」「エイリアスAレコードの設定」を行う
  • 相互認証(mTLS)を有効にするとクライアント証明書認証ができる
  • トラストストア検証では、ALB側でクライアント認証を行う
  • トラストストアを作成する場合は、ルートCA証明書の形式に注意

所要時間

2時間

ユースケース

ALBでAPI Gatewayエンドポイントをターゲットにしたい場合に、TLS 1.3+クライアント認証も行ってセキュリティを強化したいケース