🔏

無料で Cloudflare と API Gateway で mTLS する方法(Cloudflare で独自の証明書で mTLS する)

に公開

はじめに

AWS Lambda でサクッと書いて API Gateway で公開したいけど DDoS 対策で Cloudflare を使いたいとき、ありませんか?
その際に気をつけないといけないことや実際にする方法をご紹介できればと思います。

なお、この記事は jsys 2025 アドベントカレンダー6日目の記事です。(公開日はご覧いただきませんよう、お願い申し上げます!!)
jsysと全く関係ない内容でごめんなさい🙏

なにが問題なのか、mTLS とは何なのか

ご存知の方はこのセクションは読み飛ばしていただいて構いません。

そもそも Cloudflare は、ユーザーからのリクエストを受けると、代わりにサービスをホストしているサーバー(オリジンサーバー)にアクセスして、その結果をユーザーに返しています。その間にセキュリティのルールを適用したり、その前に DDoS 等はオリジンサーバーにアクセスせずに拒否したりしています。
ということは、Cloudflare とオリジンサーバーとの間にも通信があるということです。
もしオリジンサーバーが Cloudflare 以外からの通信も許可していると、攻撃者にオリジンサーバーの IP アドレスがバレてしまうと、Cloudflare を経由せずに直接オリジンサーバーへアクセスして WAF などのセキュリティルールを回避できてしまいます。

ここで、TLS ハンドシェイクの仕組み、特に相互認証(mTLS)と呼ばれるプロセスを活用します。
一般的な TLS 通信では、サーバーだけが証明書を提示しますが、mTLS ではお互い証明書を見せ合って厳格にチェックを行います。

Cloudflare には、オリジンサーバーとの間で mTLS をさせる Authenticated Origin Pulls という機能があります。

よく紹介されている方法

そこで調べてみたのですが、よく出てくるのが Cloudflare のクライアント証明書を AWS 側のトラストストアに登録する方法です。
Cloudflare が標準で使用するクライアント証明書は、すべての Cloudflare ユーザーで共通の CA から発行されています。そのため、攻撃者が自身の Cloudflare アカウント経由でリクエストを送るように設定すれば、AWS 側の mTLS 検証をすり抜けることが可能です。

解決策

なので、独自の証明書を作って、Cloudflare がオリジンサーバーにリクエストする時にはその証明書を使ってもらう必要があります。

しかし!!Cloudflare のダッシュボードから作成しようとすると、ルート証明書をダウンロードできません。(Cloudflare のルート証明書とこれで生成された証明書を使えば、全てを含む証明書が作れる気もしますが、、試してないのでわからないです。)

なので、API を使用して設定する必要があります。

準備

その前に独自の証明書を作成しておきましょう。

# ルートCAの秘密鍵を作成
openssl genrsa -aes256 -out rootca.key 4096

# ルートCAの証明書を作成(これを AWS 側のトラストストアに入れる)
## common name にドメイン(ホストじゃなくていい)を入れる
## 例:example.com
openssl req -x509 -new -nodes \
  -key rootca.key \
  -sha256 -days 1826 \
  -out rootca.crt

# Cloudflare(クライアント)の証明書用CSRと秘密鍵を作成
openssl req -new -nodes \
  -out cf-client.csr \
  -newkey rsa:4096 \
  -keyout cf-client.key

# 証明書作成時のオプション用ファイルを作成しておく
cat > cert.v3.ext <<EOF
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
EOF

# 最後にルートCAで署名したCloudflare(クライアント)の証明書を作成
openssl x509 -req \
  -in cf-client.csr \
  -CA rootca.crt -CAkey rootca.key -CAcreateserial \
  -out cf-client.crt \
  -days 730 -sha256 \
  -extfile cert.v3.ext

AWS 側を設定する

次に、AWS API Gateway 側に設定を入れていきます。通常のルーティングなどは済ませておいてください。
また、mTLS をするためには、ドメインの所有権を証明するためにACM(Amazon Certificate Manager)側で証明書を作成する必要があります。
ダッシュボードに移動して、使用したいホスト名で「パブリック証明書」を作成してください。

あと Cloudflare が出してきた証明書が正しいかを確かめるために、ルート証明書を持っておく必要があります。AWS では S3 に上げておく必要があるので、rootca.crt を同一リージョンの S3 にあげておいてください。

ここまでできたら、最後に API Gateway に戻ってカスタムドメインの設定をします。
「相互 TLS 認証」をオンにして、「トラストストア URI」に S3 の s3アドレス、ACM 証明書で先ほど作成した証明書を選択します。

https://xxx.execute-api.ap-northeast-1.amazonaws.com/... といった(カスタムドメインでない)デフォルトの URL などは無効にしてください。

Cloudflare 側を設定する

問題の Cloudflare です。(少なくともホストごとの Authenticated Origin Pulls は)API 経由で設定する必要があります。
そのため、次の情報が必要です:

  • ユーザー API トークン(ゾーン限定 / アカウント API トークンでもいいと思います)
  • ゾーンID

そして、次の shell スクリプトを実行して証明書を Cloudflare にアップロードします。

CF_ZONE_ID="xxxxxxxxxxxxxxxxxxxx"
CF_API_TOKEN="CF_API_TOKEN"

CERT_JSON=$(python3 - <<'EOF'
import json
cert = open("cf-client.crt").read()
key  = open("cf-client.key").read()
print(json.dumps({
  "certificate": cert,
  "private_key": key,
  "bundle_method": "ubiquitous"
}))
EOF
)

curl -X POST \
  "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/origin_tls_client_auth/hostnames/certificates" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  --data "$CERT_JSON"

結果の json から cert_id を取得し、ホストごとの Authenticated Origin Pulls を設定します。

CF_ZONE_ID="xxxxxxxxxxxxxxxxxxxx"
CF_API_TOKEN="CF_API_TOKEN"
CERT_ID="【先ほど取得したクライアント証明書のID】"
HOSTNAME="api.example.com"

curl -X PUT \
  "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/origin_tls_client_auth/hostnames" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  --data "{
    \"config\": [
      {
        \"hostname\": \"${HOSTNAME}\",
        \"cert_id\": \"${CERT_ID}\",
        \"enabled\": true
      }
    ]
  }"

success が帰ってきたら成功です!

まとめ

地味に日本語のドキュメントがなく、詰まりがちな部分だったので記事にしてみました。素敵なアプリを公開するきっかけになったらと思います。

筑波大学学園祭実行委員会では、公式ウェブサイト・独自ウェブアプリケーションの作成、生配信システムの構築、ファイバー敷設、映像制作、ライブ撮影まで幅広く活躍するメンバーを大募集中です!
興味のある筑波大生は info@sohosai.com までご連絡ください!!

参考にしたウェブサイト

筑波大学学園祭実行委員会

Discussion