VPC 엔드포인트로 S3에 비공개 연결하기

AWSBeginner
지금 연습하기

소개

프라이빗 배송 애플리케이션은 NAT를 통해 S3 매니페스트를 읽습니다. 일반 Internet 접근 없이 작동하는 서비스 전용 경로가 필요합니다. S3 게이트웨이 엔드포인트를 만들고 실제 요청을 비교하여 잘못된 테이블 연결을 진단한 다음 제공된 네트워크를 복원합니다.

먼저 Give a Private Subnet Outbound Access를 완료하세요. 새로운 환경은 자체 애플리케이션, S3 객체, 퍼블릭 및 프라이빗 서브넷, NAT 게이트웨이와 보안 규칙을 제공합니다. CLI는 설정되어 있습니다. 제공 리소스와 참조 네트워크를 보존하고 자신의 엔드포인트만 생성·삭제하세요. 임시로 삭제한 프라이빗 NAT 기본 경로는 복원합니다.

자격증 관련성

다음 시험 주제에 대한 기초 실습입니다.

프라이빗 애플리케이션용 S3 게이트웨이 엔드포인트 만들기

현재 NAT 경로를 살펴보고 S3 주소 범위를 확인한 뒤 프라이빗 테이블에 엔드포인트를 만듭니다.

Terminal에서 명령을 실행하고 옆에 AWS View를 여세요. 애플리케이션 주소는 10.20.2.10입니다. 제공 버킷 parcel-delivery-storage의 message.txt에는 Parcel manifest ready가 들어 있습니다.

cd /home/labex/project

Name 태그로 VPC를 선택합니다. 필터는 리소스를 선택하고 쿼리는 ID를 추출하며 $(...)는 변수에 저장합니다.

VPC_ID=$(aws ec2 describe-vpcs \
  --filters Name=tag:Name,Values=application-network \
  --query 'Vpcs[0].VpcId' \
  --output text)

프라이빗 및 퍼블릭 테이블 ID를 저장합니다. 서브넷 연결은 이미 올바릅니다.

PRIVATE_RT_ID=$(aws ec2 describe-route-tables \
  --filters "Name=vpc-id,Values=$VPC_ID" Name=tag:Name,Values=private-routes \
  --query 'RouteTables[0].RouteTableId' \
  --output text)
PUBLIC_RT_ID=$(aws ec2 describe-route-tables \
  --filters "Name=vpc-id,Values=$VPC_ID" Name=tag:Name,Values=public-routes \
  --query 'RouteTables[0].RouteTableId' \
  --output text)

경로와 연결을 읽습니다.

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" "$PUBLIC_RT_ID" \
  --query 'RouteTables[].{ID:RouteTableId,Routes:Routes,Associations:Associations}' \
  --output json

프라이빗 0.0.0.0/0는 NAT, 퍼블릭 기본 경로는 Internet 게이트웨이를 가리킵니다. 두 테이블 모두 local을 유지합니다. 정리 시 복원할 NAT ID를 저장합니다.

NAT_ID=$(aws ec2 describe-nat-gateways \
  --filter "Name=vpc-id,Values=$VPC_ID" Name=state,Values=available \
  --query 'NatGateways[0].NatGatewayId' \
  --output text)

실제 요청과 비교할 주소를 읽습니다.

aws ec2 describe-nat-gateways \
  --nat-gateway-ids "$NAT_ID" \
  --query 'NatGateways[].{State:State,Addresses:NatGatewayAddresses}' \
  --output json

AWS View에서 Read storage object를 누르면 Success와 매니페스트가 나옵니다. Source address는 NAT 퍼블릭 주소입니다. Request outbound service도 같은 주소로 성공합니다. 마지막에 이 초기 동작을 복원합니다.

AWS 관리형 접두사 목록은 리전 내 서비스의 네트워크 범위를 묶습니다. S3 게이트웨이 엔드포인트는 연결된 테이블에 목록 경로를 추가하며, 애플리케이션에 퍼블릭 주소나 일반 Internet 접근을 제공하지 않습니다. S3 목록과 IPv4 범위를 읽습니다.

PREFIX_ID=$(aws ec2 describe-prefix-lists \
  --filters Name=prefix-list-name,Values=com.amazonaws.us-east-1.s3 \
  --query 'PrefixLists[0].PrefixListId' \
  --output text)
aws ec2 get-managed-prefix-list-entries \
  --prefix-list-id "$PREFIX_ID" \
  --query 'Entries[].Cidr' \
  --output json

us-east-1 S3의 엔드포인트를 프라이빗 테이블에만 연결합니다. --vpc-endpoint-type Gateway는 경로 기반 유형, --service-name은 리전 서비스, --route-table-ids는 애플리케이션 테이블을 지정합니다. 태그로 자신의 실습 리소스를 식별합니다.

ENDPOINT_ID=$(aws ec2 create-vpc-endpoint \
  --vpc-id "$VPC_ID" \
  --vpc-endpoint-type Gateway \
  --service-name com.amazonaws.us-east-1.s3 \
  --route-table-ids "$PRIVATE_RT_ID" \
  --tag-specifications 'ResourceType=vpc-endpoint,Tags=[{Key=Name,Value=parcel-s3-endpoint},{Key=Project,Value=parcel}]' \
  --query 'VpcEndpoint.VpcEndpointId' \
  --output text)

상태와 연결을 확인합니다.

aws ec2 describe-vpc-endpoints \
  --vpc-endpoint-ids "$ENDPOINT_ID" \
  --query 'VpcEndpoints[].{ID:VpcEndpointId,State:State,Type:VpcEndpointType,Service:ServiceName,Tables:RouteTableIds}' \
  --output json

State가 available이면 진행합니다. 아직 준비되지 않았다면 몇 초 기다렸다가 같은 쿼리를 반복하세요. 유형은 Gateway, 서비스는 S3이며 프라이빗 테이블만 연결되어야 합니다. 변수 유지를 위해 Terminal을 열어 두세요.

다시 Read storage object를 누르면 내용은 같지만 출발지 주소는 10.20.2.10입니다. S3 접두사 경로가 NAT 기본 경로보다 구체적이어서 S3는 엔드포인트를, 일반 아웃바운드는 NAT를 사용합니다.

NAT 기본 경로 없이 S3 접근 유지하기

일반 아웃바운드 경로를 제거하고 서비스 전용 경로를 확인합니다.

프라이빗 NAT 기본 경로만 삭제합니다. NAT 본체, 주소, 퍼블릭 경로와 엔드포인트를 보존합니다.

aws ec2 delete-route --route-table-id "$PRIVATE_RT_ID" --destination-cidr-block 0.0.0.0/0

삭제 성공 시 출력은 없습니다. 경로를 읽습니다.

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" \
  --query 'RouteTables[].Routes' \
  --output json

local과 S3 DestinationPrefixListId는 남고 기본 경로는 없어집니다. 서비스 GatewayId는 엔드포인트 ID이며 상태는 active입니다. 이 경로는 엔드포인트 연결로 관리하므로 일반 경로 명령으로 수정하거나 삭제하지 마세요.

새로운 Read storage object는 Parcel manifest ready와 10.20.2.10을 반환합니다. Request outbound service는 실패합니다. 198.51.100.20:9000은 S3 목록 밖이고 기본 경로가 없기 때문입니다. Request private application from outside도 실패합니다. 엔드포인트는 S3의 프라이빗 연결만 제공합니다.

NAT 기본 경로 없이 S3를 읽는 프라이빗 애플리케이션

예시에서 프라이빗 테이블은 local과 S3 접두사만 포함하며 0.0.0.0/0는 없습니다. 실제 응답에는 원본 매니페스트와 출발지 10.20.2.10이 있습니다. NAT는 사용 가능하지만 이 S3 경로에는 참여하지 않습니다. 생성 ID와 주소는 다를 수 있습니다.

잘못된 테이블 연결 관찰하기

엔드포인트를 애플리케이션 테이블에서 옮겨, 사용 가능한 엔드포인트도 도달 불가능할 수 있는 이유를 확인합니다.

엔드포인트–테이블 연결과 서브넷–테이블 연결은 별개입니다. 첫 번째만 변경합니다.

aws ec2 modify-vpc-endpoint \
  --vpc-endpoint-id "$ENDPOINT_ID" \
  --add-route-table-ids "$PUBLIC_RT_ID" \
  --remove-route-table-ids "$PRIVATE_RT_ID"

Return: true가 나옵니다. 엔드포인트를 읽습니다.

aws ec2 describe-vpc-endpoints \
  --vpc-endpoint-ids "$ENDPOINT_ID" \
  --query 'VpcEndpoints[].{State:State,Tables:RouteTableIds}' \
  --output json

상태는 available이지만 퍼블릭 테이블만 나옵니다. 두 테이블을 읽습니다.

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" "$PUBLIC_RT_ID" \
  --query 'RouteTables[].{ID:RouteTableId,Routes:Routes,Associations:Associations}' \
  --output json

자동 S3 경로가 퍼블릭으로 옮겨지고 프라이빗에는 local만 남습니다. 기존 서브넷 연결은 바뀌지 않습니다. 같은 VPC라고 해서 모든 서브넷이 동일한 엔드포인트 경로를 쓰는 것은 아닙니다.

AWS View가 변경을 표시하면 Read storage object를 누릅니다. 새 요청은 Connection failed와 Storage unavailable을 반환합니다. 애플리케이션 테이블에 S3 경로가 없기 때문입니다. Request outbound service도 실패합니다. 이전 성공은 과거 결과입니다. 이 단계의 검사를 위해 오류를 유지하고 다음 단계에서 수정합니다.

프라이빗 서비스 경로 복구하기

엔드포인트 재생성, 퍼블릭 주소 추가 또는 NAT 기본 경로 복원 없이 연결을 수정합니다.

동일한 엔드포인트를 프라이빗 테이블로 되돌리고 퍼블릭 연결을 제거합니다.

aws ec2 modify-vpc-endpoint \
  --vpc-endpoint-id "$ENDPOINT_ID" \
  --add-route-table-ids "$PRIVATE_RT_ID" \
  --remove-route-table-ids "$PUBLIC_RT_ID"

연결을 읽습니다.

aws ec2 describe-vpc-endpoints \
  --vpc-endpoint-ids "$ENDPOINT_ID" \
  --query 'VpcEndpoints[].{State:State,Tables:RouteTableIds}' \
  --output json

프라이빗만 표시되어야 합니다. 경로를 살펴봅니다.

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" "$PUBLIC_RT_ID" \
  --query 'RouteTables[].{ID:RouteTableId,Routes:Routes}' \
  --output json

프라이빗 S3 경로가 자동 복구되고 퍼블릭에서 사라집니다. 퍼블릭 Internet 경로는 남으며 프라이빗 기본 경로는 여전히 없습니다.

새로운 Read storage object는 원본 매니페스트를 10.20.2.10에서 반환합니다. S3 엔드포인트는 일반 Internet 연결을 제공하지 않으므로 Request outbound service는 계속 실패합니다. Request private application from outside도 차단됩니다. 프라이빗 주소와 제공 보안 규칙은 유지됩니다.

엔드포인트 삭제 및 제공 NAT 상태 복원하기

자신의 엔드포인트를 삭제하고 자동 경로가 사라졌는지 확인한 뒤 제공 NAT의 프라이빗 기본 경로를 복원합니다.

자신의 엔드포인트만 삭제합니다.

aws ec2 delete-vpc-endpoints --vpc-endpoint-ids "$ENDPOINT_ID"

Unsuccessful은 비어 있어야 합니다. 삭제 가능한 태그로 필터링하지 말고 전체 목록을 확인합니다.

aws ec2 describe-vpc-endpoints \
  --query 'VpcEndpoints[].{ID:VpcEndpointId,State:State,Tables:RouteTableIds}' \
  --output json

deleted 기록은 남을 수 있지만 활성 실습 엔드포인트는 없어야 합니다. 삭제 기록은 트래픽을 전달하지 않습니다. deleting이면 몇 초 기다렸다가 같은 쿼리를 반복합니다. 테이블을 읽습니다.

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" "$PUBLIC_RT_ID" \
  --query 'RouteTables[].{ID:RouteTableId,Routes:Routes}' \
  --output json

어느 테이블에도 S3 접두사가 없어야 합니다. 프라이빗에 local만 있을 때 Read storage object는 실패하여 숨은 대체 경로가 없음을 확인합니다.

저장한 NAT ID로 기본 경로를 복원합니다. 제공 NAT, 주소, 애플리케이션이나 S3 데이터를 삭제하지 마세요.

aws ec2 create-route \
  --route-table-id "$PRIVATE_RT_ID" \
  --destination-cidr-block 0.0.0.0/0 \
  --nat-gateway-id "$NAT_ID"

복원한 경로를 읽습니다.

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" \
  --query 'RouteTables[].Routes' \
  --output json

local과 활성 NAT 기본 경로가 초기 상태에 일치합니다. 목록 조회 실패는 삭제 증거가 아닙니다. 인증된 쿼리가 성공하여 부재 또는 deleted와 자동 경로 제거를 확인해야 합니다.

Read storage object를 다시 누르면 NAT를 통해 원본이 반환되고 Source address는 NAT 퍼블릭 주소입니다. Request outbound service도 같은 주소로 성공하며 Request private application from outside는 계속 차단됩니다. VPC, 서브넷, NAT, 규칙, S3 객체와 참조 네트워크를 보존합니다.

이 단계의 완료 검사를 실행하세요.

요약

S3 엔드포인트를 만들고 프라이빗 출발지를 유지하는 실제 읽기를 검증했습니다. NAT 기본 경로 없이도 S3는 접근 가능했고 다른 목적지는 차단되었습니다. 잘못된 퍼블릭 연결은 접근을 중단시켰으며 프라이빗 연결 수정으로 애플리케이션과 규칙 변경 없이 복구했습니다.

엔드포인트와 자동 경로를 제거하고 NAT를 복원했습니다.