[AWS] 네트워킹 VPC - Part 2: 고급 네트워킹 (NAT, VPN, Peering)

silver's avatar
Dec 03, 2025
[AWS] 네트워킹 VPC - Part 2: 고급 네트워킹 (NAT, VPN, Peering)

🔄 NAT (Network Address Translation)

NAT가 필요한 이유

프라이빗 서브넷의 인스턴스가 인터넷에 접근해야 하지만(소프트웨어 업데이트 등), 인터넷에서 직접 접근당하면 안 되는 경우 NAT를 사용
┌────────────────────────────────────────┐ │ 인터넷 │ └──────────────┬─────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ 인터넷 게이트웨이 (IGW) │ └──────────────┬─────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ 퍼블릭 서브넷 (10.0.1.0/24) │ │ │ │ ┌──────────────────────┐ │ │ │ NAT Gateway │ │ │ │ - 공인 IP 보유 │ │ │ └──────────┬───────────┘ │ └────────────────┼────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ 프라이빗 서브넷 (10.0.2.0/24) │ │ │ │ EC2 인스턴스들 │ │ - 사설 IP만 보유 │ │ - NAT를 통해 아웃바운드만 가능 │ └────────────────────────────────────────┘

NAT Gateway

AWS 관리형 NAT 서비스로, 고가용성과 높은 대역폭을 제공

NAT Gateway 생성

# 1. Elastic IP 할당 EIP_ALLOC_ID=$(aws ec2 allocate-address \ --domain vpc \ --query 'AllocationId' \ --output text) # 2. NAT Gateway 생성 NAT_GW_ID=$(aws ec2 create-nat-gateway \ --subnet-id subnet-12345678 \ # 퍼블릭 서브넷 --allocation-id $EIP_ALLOC_ID \ --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=MyNATGateway}]' \ --query 'NatGateway.NatGatewayId' \ --output text) # 3. NAT Gateway 상태 확인 (available까지 대기) aws ec2 describe-nat-gateways \ --nat-gateway-ids $NAT_GW_ID # 4. 프라이빗 서브넷 라우팅 테이블에 라우트 추가 aws ec2 create-route \ --route-table-id rtb-private123 \ --destination-cidr-block 0.0.0.0/0 \ --nat-gateway-id $NAT_GW_ID

Python으로 NAT Gateway 구성

import boto3 import time class NATGatewayManager: def __init__(self, region='us-east-1'): self.ec2 = boto3.client('ec2', region_name=region) def create_nat_gateway(self, public_subnet_id, name='MyNATGateway'): """NAT Gateway 생성 및 설정""" # 1. Elastic IP 할당 print("📍 Elastic IP 할당 중...") eip = self.ec2.allocate_address(Domain='vpc') allocation_id = eip['AllocationId'] public_ip = eip['PublicIp'] print(f"✅ Elastic IP 할당 완료: {public_ip}") # 2. NAT Gateway 생성 print("\n🔄 NAT Gateway 생성 중...") response = self.ec2.create_nat_gateway( SubnetId=public_subnet_id, AllocationId=allocation_id, TagSpecifications=[{ 'ResourceType': 'natgateway', 'Tags': [{'Key': 'Name', 'Value': name}] }] ) nat_gateway_id = response['NatGateway']['NatGatewayId'] print(f"NAT Gateway ID: {nat_gateway_id}") # 3. Available 상태까지 대기 print("\n⏳ NAT Gateway 활성화 대기 중...") while True: status = self.ec2.describe_nat_gateways( NatGatewayIds=[nat_gateway_id] ) state = status['NatGateways'][0]['State'] print(f"현재 상태: {state}") if state == 'available': print("✅ NAT Gateway 활성화 완료!") break elif state == 'failed': print("❌ NAT Gateway 생성 실패") return None time.sleep(10) return { 'nat_gateway_id': nat_gateway_id, 'allocation_id': allocation_id, 'public_ip': public_ip } def configure_private_route_table(self, route_table_id, nat_gateway_id): """프라이빗 라우팅 테이블 구성""" print(f"\n🛣️ 라우팅 테이블 {route_table_id} 구성 중...") self.ec2.create_route( RouteTableId=route_table_id, DestinationCidrBlock='0.0.0.0/0', NatGatewayId=nat_gateway_id ) print("✅ NAT Gateway 라우트 추가 완료") def setup_high_availability_nat(self, public_subnets, private_route_tables): """고가용성 NAT Gateway 구성 (AZ별)""" nat_gateways = [] for i, (public_subnet, private_rt) in enumerate( zip(public_subnets, private_route_tables), 1 ): print(f"\n{'='*50}") print(f"NAT Gateway {i} 구성 시작") print(f"{'='*50}") nat_gw = self.create_nat_gateway( public_subnet, name=f'NAT-Gateway-{i}' ) if nat_gw: self.configure_private_route_table( private_rt, nat_gw['nat_gateway_id'] ) nat_gateways.append(nat_gw) return nat_gateways # 사용 예제 if __name__ == '__main__': manager = NATGatewayManager() # 단일 AZ NAT Gateway nat_gw = manager.create_nat_gateway('subnet-public123') manager.configure_private_route_table('rtb-private123', nat_gw['nat_gateway_id']) # 다중 AZ NAT Gateway (고가용성) public_subnets = ['subnet-pub-1a', 'subnet-pub-1b'] private_rts = ['rtb-private-1a', 'rtb-private-1b'] nat_gateways = manager.setup_high_availability_nat( public_subnets, private_rts )

NAT Gateway vs NAT Instance

특성
NAT Gateway
NAT Instance
가용성
AZ 내 고가용성
단일 장애점
대역폭
최대 100 Gbps
인스턴스 타입에 따름
유지보수
AWS 관리
사용자 관리
비용
$0.045/시간 + 데이터
EC2 비용
보안 그룹
불가
가능
Bastion 호스트
불가
가능
권장 사용
프로덕션
개발/테스트

NAT Instance 구성 (비용 절감용)

# NAT Instance 설정 # 1. Amazon Linux AMI로 EC2 시작 # 2. 소스/대상 확인 비활성화 aws ec2 modify-instance-attribute \ --instance-id i-1234567890abcdef0 \ --no-source-dest-check # 3. 인스턴스에서 IP 포워딩 활성화 (SSH 접속 후) sudo sysctl -w net.ipv4.ip_forward=1 sudo /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE # 4. 재부팅 시에도 유지되도록 설정 echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf sudo service iptables save

🔐 VPN 연결

AWS Site-to-Site VPN

온프레미스 네트워크와 AWS VPC를 안전하게 연결
┌────────────────────────────────────┐ │ 온프레미스 데이터센터 │ │ │ │ ┌──────────────────────┐ │ │ │ Customer Gateway │ │ │ │ (물리적 또는 SW) │ │ │ └──────────┬───────────┘ │ └─────────────┼────────────────────────┘ │ IPsec Tunnel │ (암호화된 연결) │ ┌─────────────┼────────────────────────┐ │ AWS VPC ▼ │ │ ┌──────────────────────┐ │ │ │ Virtual Private │ │ │ │ Gateway (VGW) │ │ │ └──────────┬───────────┘ │ │ │ │ │ ┌──────────▼───────────┐ │ │ │ 프라이빗 서브넷 │ │ │ │ - EC2, RDS 등 │ │ │ └──────────────────────┘ │ └────────────────────────────────────┘

Site-to-Site VPN 구성

# 1. Virtual Private Gateway 생성 VGW_ID=$(aws ec2 create-vpn-gateway \ --type ipsec.1 \ --amazon-side-asn 64512 \ --tag-specifications 'ResourceType=vpn-gateway,Tags=[{Key=Name,Value=MyVGW}]' \ --query 'VpnGateway.VpnGatewayId' \ --output text) # 2. VPC에 연결 aws ec2 attach-vpn-gateway \ --vpn-gateway-id $VGW_ID \ --vpc-id vpc-12345678 # 3. Customer Gateway 생성 CGW_ID=$(aws ec2 create-customer-gateway \ --type ipsec.1 \ --public-ip 203.0.113.10 \ # 온프레미스 공인 IP --bgp-asn 65000 \ --tag-specifications 'ResourceType=customer-gateway,Tags=[{Key=Name,Value=MyCGW}]' \ --query 'CustomerGateway.CustomerGatewayId' \ --output text) # 4. VPN Connection 생성 VPN_CONN_ID=$(aws ec2 create-vpn-connection \ --type ipsec.1 \ --customer-gateway-id $CGW_ID \ --vpn-gateway-id $VGW_ID \ --options TunnelOptions='[{TunnelInsideCidr=169.254.10.0/30},{TunnelInsideCidr=169.254.11.0/30}]' \ --tag-specifications 'ResourceType=vpn-connection,Tags=[{Key=Name,Value=MyVPN}]' \ --query 'VpnConnection.VpnConnectionId' \ --output text) # 5. VPN 구성 다운로드 aws ec2 describe-vpn-connections \ --vpn-connection-ids $VPN_CONN_ID \ --query 'VpnConnections[0].CustomerGatewayConfiguration' \ --output text > vpn-config.txt # 6. 라우트 전파 활성화 aws ec2 enable-vgw-route-propagation \ --route-table-id rtb-12345678 \ --gateway-id $VGW_ID

AWS Client VPN

개별 사용자가 VPC에 안전하게 접근 가능하게 한다
import boto3 class ClientVPNManager: def __init__(self): self.ec2 = boto3.client('ec2') self.acm = boto3.client('acm') def create_client_vpn_endpoint( self, server_cert_arn, client_cert_arn, vpc_id, subnet_ids, client_cidr='10.100.0.0/16' ): """Client VPN Endpoint 생성""" response = self.ec2.create_client_vpn_endpoint( ClientCidrBlock=client_cidr, ServerCertificateArn=server_cert_arn, AuthenticationOptions=[{ 'Type': 'certificate-authentication', 'MutualAuthentication': { 'ClientRootCertificateChainArn': client_cert_arn } }], ConnectionLogOptions={ 'Enabled': True, 'CloudwatchLogGroup': '/aws/clientvpn', 'CloudwatchLogStream': 'connection-log' }, VpcId=vpc_id, SecurityGroupIds=[], # 보안 그룹 추가 TagSpecifications=[{ 'ResourceType': 'client-vpn-endpoint', 'Tags': [{'Key': 'Name', 'Value': 'My-Client-VPN'}] }] ) endpoint_id = response['ClientVpnEndpointId'] # 서브넷 연결 for subnet_id in subnet_ids: self.ec2.associate_client_vpn_target_network( ClientVpnEndpointId=endpoint_id, SubnetId=subnet_id ) # 권한 부여 규칙 self.ec2.authorize_client_vpn_ingress( ClientVpnEndpointId=endpoint_id, TargetNetworkCidr='0.0.0.0/0', AuthorizeAllGroups=True ) return endpoint_id # 사용 예제 vpn_manager = ClientVPNManager() endpoint_id = vpn_manager.create_client_vpn_endpoint( server_cert_arn='arn:aws:acm:region:account:certificate/xxx', client_cert_arn='arn:aws:acm:region:account:certificate/yyy', vpc_id='vpc-12345678', subnet_ids=['subnet-123', 'subnet-456'] )

🔗 VPC Peering

두 VPC 간의 프라이빗 네트워크 연결을 생성합니다.

VPC Peering 개념

VPC A (10.0.0.0/16) VPC B (10.1.0.0/16) ┌──────────────────┐ ┌──────────────────┐ │ │ │ │ │ EC2 Instances │◄──────►│ EC2 Instances │ │ │ Peer │ │ │ 10.0.1.5 │Connection 10.1.1.10 │ │ │ │ │ └──────────────────┘ └──────────────────┘ 특징: - 프라이빗 IP로 통신 - 리전 간 가능 - 전이적 피어링 불가 (A-B, B-C ≠ A-C) - CIDR 중복 불가

VPC Peering 구성

# 1. Peering Connection 생성 PEER_CONN_ID=$(aws ec2 create-vpc-peering-connection \ --vpc-id vpc-11111111 \ # Requester VPC --peer-vpc-id vpc-22222222 \ # Accepter VPC --peer-region us-west-2 \ # 다른 리전인 경우 --tag-specifications 'ResourceType=vpc-peering-connection,Tags=[{Key=Name,Value=VPC-A-to-B}]' \ --query 'VpcPeeringConnection.VpcPeeringConnectionId' \ --output text) # 2. Peering 수락 (Accepter VPC 계정에서) aws ec2 accept-vpc-peering-connection \ --vpc-peering-connection-id $PEER_CONN_ID \ --region us-west-2 # 3. Requester VPC 라우팅 테이블 업데이트 aws ec2 create-route \ --route-table-id rtb-requester \ --destination-cidr-block 10.1.0.0/16 \ --vpc-peering-connection-id $PEER_CONN_ID # 4. Accepter VPC 라우팅 테이블 업데이트 aws ec2 create-route \ --route-table-id rtb-accepter \ --destination-cidr-block 10.0.0.0/16 \ --vpc-peering-connection-id $PEER_CONN_ID \ --region us-west-2

VPC Peering 자동화

class VPCPeeringManager: def __init__(self, region='us-east-1'): self.ec2 = boto3.client('ec2', region_name=region) def create_peering(self, vpc_a_id, vpc_b_id, vpc_b_region=None): """VPC Peering Connection 생성""" params = { 'VpcId': vpc_a_id, 'PeerVpcId': vpc_b_id, 'TagSpecifications': [{ 'ResourceType': 'vpc-peering-connection', 'Tags': [{'Key': 'Name', 'Value': f'{vpc_a_id}-to-{vpc_b_id}'}] }] } if vpc_b_region: params['PeerRegion'] = vpc_b_region response = self.ec2.create_vpc_peering_connection(**params) pcx_id = response['VpcPeeringConnection']['VpcPeeringConnectionId'] print(f"✅ Peering Connection 생성: {pcx_id}") # 상태 확인 waiter = self.ec2.get_waiter('vpc_peering_connection_exists') waiter.wait(VpcPeeringConnectionIds=[pcx_id]) return pcx_id def accept_peering(self, pcx_id, region=None): """Peering Connection 수락""" ec2_accepter = boto3.client('ec2', region_name=region or 'us-east-1') ec2_accepter.accept_vpc_peering_connection( VpcPeeringConnectionId=pcx_id ) print(f"✅ Peering Connection 수락: {pcx_id}") def update_route_tables(self, pcx_id, route_table_id, destination_cidr): """라우팅 테이블 업데이트""" self.ec2.create_route( RouteTableId=route_table_id, DestinationCidrBlock=destination_cidr, VpcPeeringConnectionId=pcx_id ) print(f"✅ 라우트 추가: {destination_cidr}{pcx_id}") def setup_bidirectional_peering( self, vpc_a_id, vpc_b_id, vpc_a_rt, vpc_b_rt, vpc_a_cidr, vpc_b_cidr ): """양방향 Peering 설정""" # Peering 생성 및 수락 pcx_id = self.create_peering(vpc_a_id, vpc_b_id) self.accept_peering(pcx_id) # 양방향 라우트 추가 self.update_route_tables(vpc_a_rt, vpc_b_cidr, pcx_id) self.update_route_tables(vpc_b_rt, vpc_a_cidr, pcx_id) print("\n🎉 양방향 Peering 설정 완료!") return pcx_id # 사용 예제 peering_mgr = VPCPeeringManager() peering_mgr.setup_bidirectional_peering( vpc_a_id='vpc-11111111', vpc_b_id='vpc-22222222', vpc_a_rt='rtb-aaa', vpc_b_rt='rtb-bbb', vpc_a_cidr='10.0.0.0/16', vpc_b_cidr='10.1.0.0/16' )

🌐 Transit Gateway

여러 VPC와 온프레미스 네트워크를 중앙 집중식으로 연결

Transit Gateway vs VPC Peering

VPC Peering (Mesh 토폴로지): VPC-A ←→ VPC-B ↕ ↕ VPC-C ←→ VPC-D 문제: NVPC 연결 시 N(N-1)/2개의 Peering 필요 Transit Gateway (Hub-Spoke 토폴로지): VPC-AVPC-C[TGW]VPC-BVPC-D 장점: NVPC 연결 시 N개의 Attachment만 필요

Transit Gateway 구성

# 1. Transit Gateway 생성 TGW_ID=$(aws ec2 create-transit-gateway \ --description "Central Transit Gateway" \ --options AmazonSideAsn=64512,AutoAcceptSharedAttachments=enable,DefaultRouteTableAssociation=enable,DefaultRouteTablePropagation=enable \ --tag-specifications 'ResourceType=transit-gateway,Tags=[{Key=Name,Value=Central-TGW}]' \ --query 'TransitGateway.TransitGatewayId' \ --output text) # 2. VPC Attachment 생성 TGW_ATTACH=$(aws ec2 create-transit-gateway-vpc-attachment \ --transit-gateway-id $TGW_ID \ --vpc-id vpc-12345678 \ --subnet-ids subnet-123 subnet-456 \ --tag-specifications 'ResourceType=transit-gateway-attachment,Tags=[{Key=Name,Value=VPC-A-Attachment}]' \ --query 'TransitGatewayVpcAttachment.TransitGatewayAttachmentId' \ --output text) # 3. VPC 라우팅 테이블 업데이트 aws ec2 create-route \ --route-table-id rtb-12345678 \ --destination-cidr-block 10.0.0.0/8 \ --transit-gateway-id $TGW_ID # 4. Transit Gateway 라우팅 테이블 조회 aws ec2 describe-transit-gateway-route-tables \ --filters "Name=transit-gateway-id,Values=$TGW_ID"

📍 VPC Endpoints

AWS 서비스에 프라이빗하게 접근할 수 있게 한다

Interface Endpoint vs Gateway Endpoint

특성
Interface Endpoint
Gateway Endpoint
기술
PrivateLink (ENI)
라우팅 테이블
지원 서비스
대부분의 AWS 서비스
S3, DynamoDB만
비용
$0.01/시간 + 데이터
무료
AZ
AZ별 배치 가능
VPC 전체
보안 그룹
적용 가능
불가

Gateway Endpoint 생성 (S3, DynamoDB)

# S3 Gateway Endpoint 생성 aws ec2 create-vpc-endpoint \ --vpc-id vpc-12345678 \ --service-name com.amazonaws.us-east-1.s3 \ --route-table-ids rtb-123 rtb-456 \ --policy-document '{ "Statement": [{ "Effect": "Allow", "Principal": "*", "Action": "s3:*", "Resource": "*" }] }' # DynamoDB Gateway Endpoint 생성 aws ec2 create-vpc-endpoint \ --vpc-id vpc-12345678 \ --service-name com.amazonaws.us-east-1.dynamodb \ --route-table-ids rtb-123 rtb-456

Interface Endpoint 생성

class VPCEndpointManager: def __init__(self, region='us-east-1'): self.ec2 = boto3.client('ec2', region_name=region) self.region = region def create_interface_endpoint( self, service_name, vpc_id, subnet_ids, security_group_ids, endpoint_name ): """Interface Endpoint 생성""" full_service_name = f'com.amazonaws.{self.region}.{service_name}' response = self.ec2.create_vpc_endpoint( VpcEndpointType='Interface', ServiceName=full_service_name, VpcId=vpc_id, SubnetIds=subnet_ids, SecurityGroupIds=security_group_ids, PrivateDnsEnabled=True, TagSpecifications=[{ 'ResourceType': 'vpc-endpoint', 'Tags': [{'Key': 'Name', 'Value': endpoint_name}] }] ) endpoint_id = response['VpcEndpoint']['VpcEndpointId'] print(f"✅ Interface Endpoint 생성: {endpoint_id}") print(f" 서비스: {full_service_name}") return endpoint_id def create_gateway_endpoint( self, service_name, vpc_id, route_table_ids, endpoint_name ): """Gateway Endpoint 생성""" full_service_name = f'com.amazonaws.{self.region}.{service_name}' response = self.ec2.create_vpc_endpoint( VpcEndpointType='Gateway', ServiceName=full_service_name, VpcId=vpc_id, RouteTableIds=route_table_ids, TagSpecifications=[{ 'ResourceType': 'vpc-endpoint', 'Tags': [{'Key': 'Name', 'Value': endpoint_name}] }] ) endpoint_id = response['VpcEndpoint']['VpcEndpointId'] print(f"✅ Gateway Endpoint 생성: {endpoint_id}") return endpoint_id def setup_common_endpoints( self, vpc_id, private_subnet_ids, route_table_ids, security_group_id ): """일반적으로 필요한 Endpoints 생성""" endpoints = {} # Gateway Endpoints (무료) print("🌐 Gateway Endpoints 생성 중...") endpoints['s3'] = self.create_gateway_endpoint( 's3', vpc_id, route_table_ids, 'S3-Endpoint' ) def setup_common_endpoints( self, vpc_id, private_subnet_ids, route_table_ids, security_group_id ): """일반적으로 필요한 Endpoints 생성""" endpoints = {} # Gateway Endpoints (무료) print("🌐 Gateway Endpoints 생성 중...") endpoints['s3'] = self.create_gateway_endpoint( 's3', vpc_id, route_table_ids, 'S3-Endpoint' ) endpoints['dynamodb'] = self.create_gateway_endpoint( 'dynamodb', vpc_id, route_table_ids, 'DynamoDB-Endpoint' ) # Interface Endpoints (유료) print("\n🔌 Interface Endpoints 생성 중...") interface_services = [ 'ec2', 'ssm', 'ssmmessages', 'ec2messages', 'logs', 'secretsmanager' ] for service in interface_services: try: endpoint_id = self.create_interface_endpoint( service, vpc_id, private_subnet_ids, [security_group_id], f'{service.upper()}-Endpoint' ) endpoints[service] = endpoint_id except Exception as e: print(f"⚠️ {service} Endpoint 생성 실패: {e}") print(f"\n✅ 총 {len(endpoints)}개 Endpoint 생성 완료") return endpoints

사용 예제

 
### VPC Endpoint 활용 예제 ```python # VPC Endpoint를 통한 S3 접근 import boto3 # Endpoint를 통해 S3 접근 (인터넷 게이트웨이 불필요) s3 = boto3.client('s3') # 프라이빗 서브넷에서 실행해도 작동 response = s3.list_buckets() # VPC Endpoint 정책으로 특정 버킷만 접근 허용 가능 endpoint_policy = { "Statement": [{ "Effect": "Allow", "Principal": "*", "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::my-company-bucket/*" ] }] }

🛡️ Network Access Control List (NACL)

NACL vs Security Group

특성
NACL
Security Group
작동 레벨
서브넷 레벨
인스턴스 레벨 (ENI)
상태
Stateless
Stateful
규칙
허용 & 거부
허용만
규칙 평가
번호 순서대로
모든 규칙 평가
적용
자동 (서브넷)
명시적 연결
기본 동작
모두 허용
모두 거부

NACL 구성

# 1. NACL 생성 NACL_ID=$(aws ec2 create-network-acl \ --vpc-id vpc-12345678 \ --tag-specifications 'ResourceType=network-acl,Tags=[{Key=Name,Value=Custom-NACL}]' \ --query 'NetworkAcl.NetworkAclId' \ --output text) # 2. Inbound 규칙 추가 # 규칙 100: HTTP 허용 aws ec2 create-network-acl-entry \ --network-acl-id $NACL_ID \ --rule-number 100 \ --protocol tcp \ --port-range From=80,To=80 \ --cidr-block 0.0.0.0/0 \ --rule-action allow \ --ingress # 규칙 110: HTTPS 허용 aws ec2 create-network-acl-entry \ --network-acl-id $NACL_ID \ --rule-number 110 \ --protocol tcp \ --port-range From=443,To=443 \ --cidr-block 0.0.0.0/0 \ --rule-action allow \ --ingress # 규칙 120: SSH 허용 (특정 IP만) aws ec2 create-network-acl-entry \ --network-acl-id $NACL_ID \ --rule-number 120 \ --protocol tcp \ --port-range From=22,To=22 \ --cidr-block 203.0.113.0/24 \ --rule-action allow \ --ingress # 규칙 130: Ephemeral Ports 허용 aws ec2 create-network-acl-entry \ --network-acl-id $NACL_ID \ --rule-number 130 \ --protocol tcp \ --port-range From=1024,To=65535 \ --cidr-block 0.0.0.0/0 \ --rule-action allow \ --ingress # 3. Outbound 규칙 추가 aws ec2 create-network-acl-entry \ --network-acl-id $NACL_ID \ --rule-number 100 \ --protocol -1 \ --cidr-block 0.0.0.0/0 \ --rule-action allow \ --egress # 4. 서브넷 연결 aws ec2 replace-network-acl-association \ --association-id aclassoc-12345678 \ --network-acl-id $NACL_ID

NACL 설계 모범 사례

class NACLManager: def __init__(self): self.ec2 = boto3.client('ec2') def create_web_tier_nacl(self, vpc_id, subnet_id): """웹 계층용 NACL 생성""" # NACL 생성 nacl = self.ec2.create_network_acl( VpcId=vpc_id, TagSpecifications=[{ 'ResourceType': 'network-acl', 'Tags': [{'Key': 'Name', 'Value': 'Web-Tier-NACL'}] }] ) nacl_id = nacl['NetworkAcl']['NetworkAclId'] # Inbound 규칙 inbound_rules = [ (100, 80, 80, '0.0.0.0/0', 'HTTP from Internet'), (110, 443, 443, '0.0.0.0/0', 'HTTPS from Internet'), (120, 22, 22, '10.0.0.0/16', 'SSH from VPC'), (130, 1024, 65535, '0.0.0.0/0', 'Return traffic') ] for rule_num, from_port, to_port, cidr, desc in inbound_rules: self.ec2.create_network_acl_entry( NetworkAclId=nacl_id, RuleNumber=rule_num, Protocol='6', # TCP PortRange={'From': from_port, 'To': to_port}, CidrBlock=cidr, RuleAction='allow', Ingress=True ) print(f"✅ Inbound 규칙 {rule_num}: {desc}") # Outbound 규칙 outbound_rules = [ (100, 80, 80, '0.0.0.0/0', 'HTTP to Internet'), (110, 443, 443, '0.0.0.0/0', 'HTTPS to Internet'), (120, 3306, 3306, '10.0.2.0/24', 'MySQL to DB tier'), (130, 1024, 65535, '0.0.0.0/0', 'Return traffic') ] for rule_num, from_port, to_port, cidr, desc in outbound_rules: self.ec2.create_network_acl_entry( NetworkAclId=nacl_id, RuleNumber=rule_num, Protocol='6', PortRange={'From': from_port, 'To': to_port}, CidrBlock=cidr, RuleAction='allow', Egress=True ) print(f"✅ Outbound 규칙 {rule_num}: {desc}") # 서브넷 연결 associations = self.ec2.describe_network_acls( Filters=[ {'Name': 'association.subnet-id', 'Values': [subnet_id]} ] ) for assoc in associations['NetworkAcls'][0]['Associations']: if assoc['SubnetId'] == subnet_id: self.ec2.replace_network_acl_association( AssociationId=assoc['NetworkAclAssociationId'], NetworkAclId=nacl_id ) break print(f"\n✅ NACL 생성 및 연결 완료: {nacl_id}") return nacl_id def create_database_tier_nacl(self, vpc_id, subnet_id, app_tier_cidr): """데이터베이스 계층용 NACL 생성""" nacl = self.ec2.create_network_acl( VpcId=vpc_id, TagSpecifications=[{ 'ResourceType': 'network-acl', 'Tags': [{'Key': 'Name', 'Value': 'DB-Tier-NACL'}] }] ) nacl_id = nacl['NetworkAcl']['NetworkAclId'] # Inbound: 애플리케이션 계층에서만 접근 허용 self.ec2.create_network_acl_entry( NetworkAclId=nacl_id, RuleNumber=100, Protocol='6', PortRange={'From': 3306, 'To': 3306}, CidrBlock=app_tier_cidr, RuleAction='allow', Ingress=True ) # Ephemeral ports self.ec2.create_network_acl_entry( NetworkAclId=nacl_id, RuleNumber=110, Protocol='6', PortRange={'From': 1024, 'To': 65535}, CidrBlock=app_tier_cidr, RuleAction='allow', Ingress=True ) # Outbound: 애플리케이션 계층으로만 응답 self.ec2.create_network_acl_entry( NetworkAclId=nacl_id, RuleNumber=100, Protocol='6', PortRange={'From': 1024, 'To': 65535}, CidrBlock=app_tier_cidr, RuleAction='allow', Egress=True ) print(f"✅ DB NACL 생성 완료: {nacl_id}") return nacl_id # 사용 예제 nacl_mgr = NACLManager() web_nacl = nacl_mgr.create_web_tier_nacl('vpc-12345678', 'subnet-web123') db_nacl = nacl_mgr.create_database_tier_nacl( 'vpc-12345678', 'subnet-db123', '10.0.2.0/24' # 애플리케이션 계층 CIDR )

🎓 Part 2

  1. Q: NAT Gateway와 NAT Instance의 주요 차이점은?
      • A: NAT Gateway는 AWS 관리형(고가용성), NAT Instance는 사용자 관리형(단일 장애점)
  1. Q: VPC Peering에서 전이적 피어링이 불가능한 이유는?
      • A: 보안과 비용 관리를 위해 명시적 연결만 허용
  1. Q: Transit Gateway를 사용해야 하는 경우는?
      • A: 3개 이상의 VPC 연결, 온프레미스 연결, 중앙 집중식 관리가 필요한 경우
  1. Q: Gateway Endpoint와 Interface Endpoint의 비용 차이는?
      • A: Gateway Endpoint는 무료, Interface Endpoint는 시간당 + 데이터 처리 비용
  1. Q: NACL과 Security Group 중 먼저 평가되는 것은?
      • A: NACL (서브넷 레벨이 인스턴스 레벨보다 먼저)

📋 체크리스트

NAT 구성

고가용성을 위한 AZ별 NAT Gateway 배치
NAT Gateway용 Elastic IP 할당
프라이빗 서브넷 라우팅 테이블 업데이트
비용 최적화 검토 (NAT Instance 고려)

VPN 연결

VPN 타입 결정 (Site-to-Site vs Client VPN)
Virtual Private Gateway 생성
Customer Gateway 구성
VPN 터널 이중화
라우트 전파 활성화

VPC Peering

CIDR 블록 중복 확인
양방향 라우팅 설정
Security Group 규칙 업데이트
리전 간 레이턴시 고려

VPC Endpoints

S3, DynamoDB용 Gateway Endpoint 생성
필수 서비스용 Interface Endpoint 생성
Endpoint 보안 그룹 구성
Private DNS 활성화

NACL

계층별 NACL 설계
Ephemeral Ports 허용
규칙 번호 체계 정의
Stateless 특성 이해

Share article

silver