Amazon Web Services 한국 블로그
Amazon VPC 라우팅 구체화 신규 기능을 통한 서브넷 간 트래픽 검사
2019년 12월부터 Amazon Virtual Private Cloud(VPC)를 통해 모든 수신 트래픽(노스 – 사우스 트래픽)을 특정 네트워크 인터페이스로 라우팅할 수 있습니다. 이 기능은 여러 가지 이유로 사용할 수 있습니다. 예를 들어 침입 탐지 시스템(IDS) 어플라이언스를 사용하여 수신되는 트래픽을 검사하거나 수신 트래픽을 방화벽으로 라우팅할 수 있습니다.
이 기능을 출시한 이후로 VPC 내에서 서브넷 사이의 트래픽 흐름(이스트 – 웨스트 트래픽)을 분석할 수 있는 유사한 기능을 제공해달라는 요청이 많았습니다. 오늘까지는 라우팅 테이블의 경로가 기본 로컬 경로보다 구체적일 수 없기 때문에 기능을 구현할 수 없었습니다(자세한 내용은 VPC 설명서를 참조하세요). 쉽게 말해 기본 로컬 경로(전체 VPC의 CIDR 범위)보다 작은 CIDR 범위를 사용하는 대상을 지정할 수 있는 경로가 없습니다. 예를 들어 VPC 범위가 10.0.0/16
이고 서브넷이 10.0.1.0/24
일 경우, 10.0.1.0/24
에 대한 경로가 10.0.0/16
에 대한 경로보다 구체적입니다.
이제 라우팅 테이블에 이런 제한이 없습니다. 라우팅 테이블에 기본 로컬 경로보다 구체적인 경로를 지정할 수 있습니다. 이렇게 더욱 구체적인 경로를 사용하여 모든 트래픽을 전용 어플라이언스나 서비스로 보내서 두 서브넷 간의 모든 트래픽(이스트-웨스트 트래픽)을 검사, 분석 또는 필터링할 수 있습니다. 경로 대상은 직접 구축하거나 구매한 어플라이언스에 연결된 네트워크 인터페이스(ENI), 성능 또는 고가용성을 위해 트래픽을 여러 어플라이언스에 배포하는 AWS 게이트웨이 로드 밸런서(GWLB) 엔드포인트, AWS Firewall Manager 엔드포인트 또는 NAT 게이트웨이로 지정할 수 있습니다. 또한, 서브넷과 AWS Transit Gateway 사이에 어플라이언스를 넣을 수 있습니다.
어플라이언스를 연결해 원본과 대상 서브넷 사이에 두 가지 이상의 분석 유형을 넣을 수도 있습니다. 예를 들어 우선 방화벽(AWS 관리형 또는 서드 파티 방화벽 어플라이언스)을 사용하여 트래픽을 필터링한 다음, 이 트래픽을 침입 탐지 및 예방 시스템으로 보내고 딥 패킷 검사를 실행해야 할 수도 있습니다. AWS Partner Network와 AWS Marketplace에서 가상 어플라이언스에 액세스할 수 있습니다.
어플라이언스를 연결하면 각 어플라이언스와 엔드포인트가 별개의 서브넷에 속해야 합니다.
이제 직접 새로운 기능을 사용해보겠습니다.
작동 방식
이 블로그 게시물에서는 서브넷이 세 개인 VPC가 있다고 가정합니다. 첫 번째 서브넷은 공개되어 있고 요새 호스트가 있습니다. 이는 두 번째 서브넷에 있는 리소스(예: API, 데이터베이스)에 액세스해야 합니다. 두 번째 서브넷은 비공개입니다. 요새에 필요한 리소스를 호스팅합니다. 이 설정을 배포하는 데 도움이 되도록 간단한 CDK 스크립트를 작성했습니다.
우리 회사는 규정을 준수하기 위해 침입 탐지 시스템을 통해 이 비공개 애플리케이션으로 트래픽을 보내야 합니다. CDK 스크립트는 네트워크 어플라이언스를 호스팅하는 세 번째 서브넷(비공개)을 만듭니다. 이는 세 개의 Amazon Elastic Compute Cloud(Amazon EC2) 인스턴스(요새 호스트, 애플리케이션 인스턴스, 네트워크 분석 어플라이언스)를 제공합니다. 또한, 이 스크립트는 NAT 게이트웨이를 생성해서 애플리케이션 인스턴스를 부트스트랩하고 AWS Systems Manager 세션 관리자(SSM)로 세 개의 인스턴스에 연결합니다.
이 데모에서는 네트워크 어플라이언스는 IP 라우터로 구성된 일반 Amazon Linux EC2 인스턴스입니다. 실제 환경에서는 AWS Marketplace에서 파트너가 제공하는 여러 어플라이언스나 게이트웨이 로드 밸런서 엔드포인트 중 하나, 또는 네트워크 방화벽을 사용해야 합니다.
어플라이언스를 통해 트래픽을 보내도록 라우팅 테이블을 수정해보겠습니다.
AWS Management Console 또는 AWS 명령줄 인터페이스(CLI)를 사용하여 10.0.0.0/24
와 10.0.1.0/24
서브넷 라우팅 테이블에 더욱 구체적인 경로를 추가합니다. 이 경로는 eni0
(트래픽 검사 어플라이언스의 네트워크 인터페이스)을 가리킵니다.
먼저, CLI를 사용하여 VPC ID, 서브넷 ID, 라우팅 테이블 ID, 어플라이언스의 ENI ID를 수집합니다.
VPC_ID=$(aws \
--region $REGION cloudformation describe-stacks \
--stack-name SpecificRoutingDemoStack \
--query "Stacks[].Outputs[?OutputKey=='VPCID'].OutputValue" \
--output text)
echo $VPC_ID
APPLICATION_SUBNET_ID=$(aws \
--region $REGION ec2 describe-instances \
--query "Reservations[].Instances[] | [?Tags[?Key=='Name' && Value=='application']].NetworkInterfaces[].SubnetId" \
--output text)
echo $APPLICATION_SUBNET_ID
APPLICATION_SUBNET_ROUTE_TABLE=$(aws \
--region $REGION ec2 describe-route-tables \
--query "RouteTables[?VpcId=='${VPC_ID}'] | [?Associations[?SubnetId=='${APPLICATION_SUBNET_ID}']].RouteTableId" \
--output text)
echo $APPLICATION_SUBNET_ROUTE_TABLE
APPLIANCE_ENI_ID=$(aws \
--region $REGION ec2 describe-instances \
--query "Reservations[].Instances[] | [?Tags[?Key=='Name' && Value=='appliance']].NetworkInterfaces[].NetworkInterfaceId" \
--output text)
echo $APPLIANCE_ENI_ID
BASTION_SUBNET_ID=$(aws \
--region $REGION ec2 describe-instances \
--query "Reservations[].Instances[] | [?Tags[?Key=='Name' && Value=='BastionHost']].NetworkInterfaces[].SubnetId" \
--output text)
echo $BASTION_SUBNET_ID
BASTION_SUBNET_ROUTE_TABLE=$(aws \
--region $REGION ec2 describe-route-tables \
--query "RouteTables[?VpcId=='${VPC_ID}'] | [?Associations[?SubnetId=='${BASTION_SUBNET_ID}']].RouteTableId" \
--output text)
echo $BASTION_SUBNET_ROUTE_TABLE
이제 두 개의 구체적 경로를 추가합니다. 한 경로는 어플라이언스 네트워크 인터페이스를 통해 요새 공개 서브넷에서 애플리케이션 비공개 서브넷으로 트래픽을 보냅니다. 두 번째 경로는 반대쪽에서 응답을 라우팅합니다. 어플라이언스 네트워크 인터페이스를 통해 애플리케이션 비공개 서브넷에서 요새 공개 서브넷으로 더욱 구체적인 트래픽을 라우팅합니다. 헷갈리시나요? 그럼 아래의 다이어그램을 살펴보겠습니다.
먼저 요새 라우팅 테이블을 수정합니다.
aws ec2 create-route \
--region $REGION \
--route-table-id $BASTION_SUBNET_ROUTE_TABLE \
--destination-cidr-block 10.0.1.0/24 \
--network-interface-id $APPLIANCE_ENI_ID
다음으로 애플리케이션 라우팅을 수정합니다.
aws ec2 create-route \
--region $REGION \
--route-table-id $APPLICATION_SUBNET_ROUTE_TABLE \
--destination-cidr-block 10.0.0.0/24 \
--network-interface-id $APPLIANCE_ENI_ID
Amazon VPC Console로 수정할 수도 있습니다. “배스천” 라우팅 테이블을 선택하고 경로(Routes) 탭에서 경로 편집(Edit routes)을 클릭합니다.
10.0.1.0/24
(애플리케이션의 서브넷)에 대한 트래픽을 어플라이언스 ENI(eni-055...
)로 보내는 경로를 추가합니다.
다음에는 응답에 대한 반대 경로를 정의합니다. 애플리케이션 서브넷에서 어플라이언스 ENI(eni-05...
)로 10.0.0.0/24
에 대한 트래픽을 보냅니다. 이 작업이 완료되면 다음과 같은 애플리케이션 서브넷 라우팅 테이블이 됩니다.
어플라이언스 인스턴스 구성
마지막으로 어플라이언스 인스턴스가 수신하는 모든 트래픽을 전달하도록 구성합니다. 일반적으로 소프트웨어 어플라이언스가 이 작업을 대신 수행합니다. AWS Marketplace 어플라이언스나 이 데모에 제공한 CDK 스크립트에서 생성한 인스턴스를 사용할 때는 추가적인 단계가 필요하지 않습니다. 일반 Linux 인스턴스를 사용할 경우, 아래의 추가적인 두 가지 단계를 완료하세요.
1. EC2 어플라이언스 인스턴스에 연결하고 커널에서 IP 트래픽 포워딩을 구성합니다.
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1
2. EC2 인스턴스가 자신과 다른 대상에 대한 트래픽을 수락하도록 구성합니다(원본/대상 확인이라고도 함).
APPLIANCE_ID=$(aws --region $REGION ec2 describe-instances \
--filter "Name=tag:Name,Values=appliance" \
--query "Reservations[].Instances[?State.Name == 'running'].InstanceId[]" \
--output text)
aws ec2 modify-instance-attribute --region $REGION \
--no-source-dest-check \
--instance-id $APPLIANCE_ID
설정 테스트
이제 어플라이언스가 다른 EC2 인스턴스로 트래픽을 포워딩할 수 있습니다.
데모 설정을 사용한다면 요새 호스트에 SSH 키가 설치되어 있지 않습니다. 액세스는 AWS Systems Manager 세션 관리자를 통해 제공됩니다.
BASTION_ID=$(aws --region $REGION ec2 describe-instances \
--filter "Name=tag:Name,Values=BastionHost" \
--query "Reservations[].Instances[?State.Name == 'running'].InstanceId[]" \
--output text)
aws --region $REGION ssm start-session --target $BASTION_ID
요새 호스트에 연결한 후, 다음의 cURL
명령을 실행하여 애플리케이션 호스트에 연결합니다.
sh-4.2$ curl -I 10.0.1.239 # 애플리케이션 호스트의 비공개 IP 주소 사용
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Mon, 24 May 2021 10:00:22 GMT
Content-Type: text/html
Content-Length: 12338
Last-Modified: Mon, 24 May 2021 09:36:49 GMT
Connection: keep-alive
ETag: "60ab73b1-3032"
Accept-Ranges: bytes
트래픽이 어플라이언스를 통과할 준비가 되었는지 확인하려면 다시 한번 인스턴스에 원본/대상 확인을 활성화할 수 있습니다. --source-dest-check
파라미터와 위의 modify-instance-attribute
CLI 명령을 사용하세요. 원본/대상 확인이 활성화되면 트래픽이 차단됩니다.
또한, 어플라이언스 호스트에 연결하고 tcpdump
명령으로 트래픽을 검사할 수 있습니다.
(랩톱)
APPLIANCE_ID=$(aws --region $REGION ec2 describe-instances \
--filter "Name=tag:Name,Values=appliance" \
--query "Reservations[].Instances[?State.Name == 'running'].InstanceId[]" \
--output text)
aws --region $REGION ssm start-session --target $APPLIANCE_ID
(어플라이언스 호스트)
tcpdump -i eth0 host 10.0.0.16 # the private IP address of the bastion host
08:53:22.760055 IP ip-10-0-0-16.us-west-2.compute.internal.46934 > ip-10-0-1-104.us-west-2.compute.internal.http: Flags [S], seq 1077227105, win 26883, options [mss 8961,sackOK,TS val 1954932042 ecr 0,nop,wscale 6], length 0
08:53:22.760073 IP ip-10-0-0-16.us-west-2.compute.internal.46934 > ip-10-0-1-104.us-west-2.compute.internal.http: Flags [S], seq 1077227105, win 26883, options [mss 8961,sackOK,TS val 1954932042 ecr 0,nop,wscale 6], length 0
08:53:22.760322 IP ip-10-0-1-104.us-west-2.compute.internal.http > ip-10-0-0-16.us-west-2.compute.internal.46934: Flags [S.], seq 4152624111, ack 1077227106, win 26847, options [mss 8961,sackOK,TS val 4094021737 ecr 1954932042,nop,wscale 6], length 0
08:53:22.760329 IP ip-10-0-1-104.us-west-2.compute.internal.http > ip-10-0-0-16.us-west-2.compute.internal.46934: Flags [S.], seq 4152624111, ack 1077227106, win 26847, options [mss
정리
제가 이 게시물에 제공한 CDK 스크립트를 사용했다면 작업을 완료하고 cdk destroy
를 실행하세요. 그래야 이 데모에 사용한 EC2 인스턴스와 NAT 게이트웨이에 대한 요금이 청구되지 않습니다. us-west-2
에서 데모 스크립트를 실행하면 시간당 $0.062가 청구됩니다.
주의 사항.
VPC 경로를 구체화할 때 유의해야 할 점이 몇 가지 있습니다.
- 트래픽을 보내는 네트워크 인터페이스나 서비스 엔드포인트가 전용 서브넷 안에 있어야 합니다. 트래픽의 원본 또는 대상 서브넷에 넣을 수 없습니다.
- 어플라이언스를 연결할 수 있습니다. 각 어플라이언스는 전용 서브넷에서 활성 상태여야 합니다.
- 추가하는 각 서브넷은 IP 주소 블록을 사용합니다. IPv4를 사용하는 경우 사용한 IP 주소 개수를 확인하세요(A/24 서브넷은 VPC에서 256개 주소 사용). 서브넷에 허용된 최소 CIDR 범위는 /28이며, IP 주소는 16개만 사용합니다.
- 어플라이언스의 보안 그룹은 원하는 포트에서 수신 트래픽을 허용하는 규칙이 설정되어 있어야 합니다. 마찬가지로 애플리케이션의 보안 그룹은 어플라이언스 보안 그룹이나 IP 주소에서 수신되는 트래픽을 승인해야 합니다.
이 새로운 기능은 추가 비용 없이 모든 AWs 리전에서 제공됩니다.
지금 바로 사용해보세요.