HTTPS でコンテンツのエンドポイントを保護する

AWSBeginner
オンラインで実践に進む

はじめに

エンドポイントは動作していますが、利用者はサーバーの身元を検証する暗号化接続で内容を読むべきです。用意された ACM 証明書を CloudFront の代替ドメイン名に関連付け、HTTPS を必須にし、実際のページと証明書の信頼・名前をテストします。

先に非公開 S3 配信ラボを完了してください。この独立 VM には新しい非公開オリジン接続、content.labex-n04.test 用のインポート済み証明書、公開の信頼アンカー practice-ca.pem が用意されています。証明書はまだディストリビューションに関連付けられていません。上部の AWS View で状態を確認し、下部の Terminal を使います。個人の AWS アカウントや購入したドメインは不要です。

練習証明書は用意されたプライベート認証局が署名しています。公開 CA ファイルを単一コマンドで信頼し、システムの信頼ストアは変更しません。公開 CloudFront サイトには公開で信頼される証明書が必要です。ここでは関連付けと実際の TLS 検証を練習し、公開ドメイン登録や証明書発行は行いません。

認定試験の学習ポイント

認定 試験タスク 実践内容
Cloud Practitioner (CLF-C02) タスク 2.2 HTTPS による転送中の暗号化を理解し、非公開オリジンのアクセスポリシーと区別します。
Solutions Architect – Associate (SAA-C03) タスク 1.3 ACM 証明書を関連付け、TLS の信頼、ホスト名一致、暗号化コンテンツへのアクセスを検証します。

ラボの概要

概念図:ビューアーは TLS で証明書の名前と発行者を検証し、非公開オリジンのディストリビューションを通して内容を受信する。

証明書を関連付けて HTTPS を必須にする

このステップでは、用意された証明書と代替名をディストリビューションに接続し、暗号化されたビューアーリクエストを必須にします。

TLS は接続を暗号化し、クライアントがサーバーの身元を検証できるようにします。証明書には名前と公開鍵があり、認証局 CA が署名します。ACM(AWS Certificate Manager)は AWS サービスの証明書を管理します。ACM に証明書があっても、自動的にディストリビューションへ関連付けられるわけではありません。

用意されたリソースを特定します。$(...) は出力をシェル変数へ保存し、--query は一致するリソースを選び、--output text は ID を引数として使える形にします。Terminal を開いたままにします。

cd /home/labex/project
HOST_NAME=content.labex-n04.test
DIST_ID=$(aws cloudfront list-distributions \
  --query "DistributionList.Items[?Comment=='labex-n04:private-content'].Id | [0]" \
  --output text)
CERT_ARN=$(aws acm list-certificates \
  --query "CertificateSummaryList[?DomainName=='content.labex-n04.test'].CertificateArn | [0]" \
  --output text)
aws acm describe-certificate \
  --certificate-arn "$CERT_ARN" \
  --query 'Certificate.{Name:DomainName,Names:SubjectAlternativeNames,Status:Status}'

ISSUED と、Names 内の練習名を確認します。ARN はアカウントとリージョンを識別します。CloudFront の ACM ビューアー証明書は us-east-1 にある必要があり、用意された ARN はこのリージョンを使います。証明書は関連付ける代替名を含む必要があります。

現在の設定と ETag を読みます。ETag は更新に必要なバージョントークンです。> は出力をファイルに保存します。

aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query DistributionConfig > current-config.json
ETAG=$(aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query ETag \
  --output text)

代替ドメイン名(alias)により、ディストリビューションがコンテンツ名を認識します。SNI(Server Name Indication)は TLS 接続時に名前を送り、証明書を選択させます。ビューアープロトコルポリシーは HTTP と HTTPS を制御し、S3 オリジン権限とは別です。

jq は他の設定を保ちながら JSON を編集します。--arg は取得値を JSON 文字列として渡します。引用符内の | は、代替名、カスタム証明書、https-only の三つの変更を順に適用します。選択するセキュリティポリシーは TLS 1.2 以降を要求します。

jq --arg host "$HOST_NAME" --arg cert "$CERT_ARN"   '.Aliases = {Quantity: 1, Items: [$host]} |
   .ViewerCertificate = {CloudFrontDefaultCertificate: false,
     ACMCertificateArn: $cert, SSLSupportMethod: "sni-only",
     MinimumProtocolVersion: "TLSv1.2_2021"} |
   .DefaultCacheBehavior.ViewerProtocolPolicy = "https-only"'   current-config.json > https-config.json
aws cloudfront update-distribution \
  --id "$DIST_ID" \
  --if-match "$ETAG" \
  --distribution-config file://https-config.json
aws cloudfront wait distribution-deployed \
  --id "$DIST_ID"

接続をテストする前に保存された設定を確認します。

aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query 'DistributionConfig.{Names:Aliases.Items,Certificate:ViewerCertificate.ACMCertificateArn,Protocol:DefaultCacheBehavior.ViewerProtocolPolicy}'

正確な練習名、証明書 ARN、https-only が表示されるはずです。AWS View に代替名と関連付けられた ACM 証明書が表示されます。ネイティブ設定は準備できました。次のステップで実際の暗号化リクエストを証明します。関連付けの検証を実行します。

AWS View の例:ディストリビューションには練習用代替名、ACM ビューアー証明書、https-only ポリシーがあり、証明書は ISSUED。

暗号化コンテンツと証明書を検証する

このステップでは、信頼できる HTTPS でページを読み、平文アクセスの拒否を観察し、証明書の信頼とホスト名一致を区別します。

curl --cacert practice-ca.pem はこのコマンドだけで公開 CA を信頼します。--resolve は正確な名前とポートをこの VM に接続し、その名前を SNI と証明書検証に使います。公開ドメイン登録やシステム DNS 変更はしません。用意された HTTPS エンドポイントはポート 8443 です。

curl --fail --include --noproxy '*' --cacert practice-ca.pem   --resolve "${HOST_NAME}:8443:127.0.0.1"   "https://${HOST_NAME}:8443/index.html"

HTTP 200 と Release one を確認します。クライアントは実際の TLS 接続を確立し、信頼された発行者と名前を検証して実際の S3 ページを受信しました。保存済み ARN 以上の動作を証明しています。

平文ビューアーと匿名の直接オリジンアクセスをテストします。

curl --noproxy '*' --output /dev/null   --resolve "${HOST_NAME}:8082:127.0.0.1"   --write-out 'Plaintext viewer: HTTP %{http_code}\n'   "http://${HOST_NAME}:8082/index.html"
curl --noproxy '*' --output /dev/null   --write-out 'Anonymous origin: HTTP %{http_code}\n'   http://127.0.0.1:5000/labex-n04-content/index.html

両方 HTTP 403 でなければなりません。HTTPS の必須化は S3 バケットを公開しません。これは別々の保護です。

CA ファイルを省略して信頼をテストします。このコマンドは意図的に失敗するはずです。

curl --fail --noproxy '*'   --resolve "${HOST_NAME}:8443:127.0.0.1"   "https://${HOST_NAME}:8443/index.html"

通常は curl エラー 60 などの証明書検証エラーになります。デフォルトの信頼ストアはこのプライベート CA を信頼しません。想定された失敗後は続けてください。成功したリクエストは正しい信頼アンカーを選びました。証明書検証を無効にすると、練習している保護を失います。

openssl s_client は SNI 選択名と検証名を分けられます。正しい SNI を送り、CA を信頼しますが、意図的に誤ったホスト名を検証させます。-verify_return_error は検証失敗で停止し、< /dev/null は対話入力を避けます。

openssl s_client -connect 127.0.0.1:8443   -servername "$HOST_NAME" -CAfile practice-ca.pem   -verify_hostname wrong.labex-n04.test   -verify_return_error < /dev/null

hostname mismatch と検証失敗を確認します。発行者の信頼だけでは不十分で、証明書は要求した身元も含む必要があります。AWS View に実際の暗号化結果と平文拒否が残ります。HTTPS の検証を実行します。

Terminal の例:誤った検証名は hostname mismatch となるが、練習 SNI 名は正しい証明書を選択する。

AWS View の例:最新の実際の HTTPS ページリクエストは HTTP 200、以前の平文アクセスは拒否。

自分の配信リソースだけを削除する

このステップでは、まずディストリビューションを無効にして削除し、次に OAC、S3 内容、用意された証明書を削除します。参照バケットは維持します。

CloudFront は ETag をバージョントークンに使います。推測せず現在の設定と ETag を取得します。最初にオリジンにつながる正確な OAC を取得します。

OAC_ID=$(aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query 'DistributionConfig.Origins.Items[0].OriginAccessControlId' \
  --output text)

設定を保存し、ETag を取得します。

aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query DistributionConfig \
  --output json > distribution-current.json
DIST_ETAG=$(aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query ETag \
  --output text)

現在の設定でディストリビューションの Enabled は true の唯一の同名属性です。通常の sed 置換で無効なコピーを作り、オリジン設定を維持します。

sed 's/"Enabled": true/"Enabled": false/' distribution-current.json > distribution-disabled.json
aws cloudfront update-distribution \
  --id "$DIST_ID" \
  --if-match "$DIST_ETAG" \
  --distribution-config file://distribution-disabled.json

無効な設定のデプロイを待ちます。AWS での変更反映には時間がかかることがあります。ここでは世界全体のデプロイ時間は測定しません。

aws cloudfront wait distribution-deployed \
  --id "$DIST_ID"

更新すると ETag が変わります。無効なディストリビューションを削除する前に最新トークンを読みます。

DIST_ETAG=$(aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query ETag \
  --output text)
aws cloudfront delete-distribution \
  --id "$DIST_ID" \
  --if-match "$DIST_ETAG"

OAC 自身の ETag で削除します。ディストリビューション ID と OAC ID は別のリソースです。

OAC_ETAG=$(aws cloudfront get-origin-access-control \
  --id "$OAC_ID" \
  --query ETag \
  --output text)
aws cloudfront delete-origin-access-control \
  --id "$OAC_ID" \
  --if-match "$OAC_ETAG"

このラボのコンテンツオブジェクトとバケットだけを削除します。

aws s3api delete-object \
  --bucket labex-n04-content \
  --key index.html
aws s3api delete-bucket \
  --bucket labex-n04-content
aws s3api list-buckets \
  --query 'Buckets[].Name'

ディストリビューション削除後、先ほど選んだ ARN で用意された証明書だけを削除します。ACM リソースを削除しますが、公開 CA ファイルは独立したローカル素材として残して構いません。

aws acm delete-certificate \
  --certificate-arn "$CERT_ARN"
aws acm list-certificates \
  --query 'CertificateSummaryList[].CertificateArn'

単元証明書がなくなり、参照バケットが残り、コンテンツバケットがないはずです。AWS View にはコンテンツディストリビューションがなく、参照だけが表示されます。クリーンアップ検証を実行します。API の失敗は削除の証拠になりません。

AWS View の例:単元のディストリビューションと証明書は削除され、参照だけが残る。過去のリクエスト履歴は引き続き表示される。

まとめ

ACM 証明書と代替名を CloudFront に関連付け、HTTPS を必須にし、検証済み TLS で実際の内容を読みました。発行者の信頼、ホスト名一致、ビューアー暗号化、非公開オリジンアクセスを区別しました。平文は拒否され、信頼されない発行者や誤った名前の検証は失敗しました。自分の配信リソースと単元証明書だけを削除し、無関係な参照を維持しました。