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로 실제 콘텐츠를 읽었습니다. 신뢰할 수 있는 발급자, 일치하는 호스트 이름, 뷰어 암호화 및 비공개 오리진 접근을 구별했습니다. 평문 요청은 거부되었으며 신뢰하지 않는 발급자와 잘못된 이름 검증은 실패했습니다. 자신의 배포 리소스와 단원 인증서만 삭제하고 무관한 참조를 유지했습니다.