[AWS Networking] Connecting On-Premise to AWS VPC with Site-to-Site VPN

Kim Jun Young·2025년 12월 16일

AWS

목록 보기
9/19
post-thumbnail

0. Overview

한 가지 시나리오를 생각해보자. 회사에서 온프레미스(On-Premise) 또는 자체적인 IDC를 운영하고 있었으나, AWS 클라우드 환경으로 마이그레이션을 하게 되는 상황이 있을 수 있다.

만약 웹 서버 애플리케이션은 AWS 환경으로 마이그레이션을 하였으나, DB는 기술적인 문제와 운영 정책 상 마이그레이션하지 못하는 상황이 있을 수 있다.

이때 AWS는 이러한 온프레미스 환경과 AWS VPC 간의 네트워크 연결을 위한 서비스가 크게 2가지가 있는데, Direct Connect(이하 DX)Site to Site VPN(이하 S2S VPN)을 사용할 수 있다.
(Client VPN은 현재 상황과 맞지 않기 때문에 제외하였다. 이는 우리가 생각하는 일반적인 OpenVPN 기반의 사용자가 사용할 수 있는 VPN 서비스이다.)

DX를 사용한다면 AWS 전용 백본 네트워크를 사용하기 때문에 보안적인 측면이나 전송할 수 있는 데이터의 대역폭 등 모두 S2S VPN보다 뛰어나지만 DX는 높은 비용과 복잡성, 사용할 수 있을 때 까지의 리드 타임, BGP를 지원하는 물리적인 장비가 필요하다는 등의 문제로 소/중 규모의 서비스에서 DX를 도입하기엔 어려움이 있을 수 있다.

여담으로 필자가 있던 곳은 아니였는데, Data Driven을 중요하게 생각하던 스타트업이 있었다. DB가 위치한 자체적인 온프레미스 서버와 AWS EKS 간의 통신을 위해 DX를 도입한 것을 본 적이 있었다.

1. What is Site to Site VPN ?

만약 DX를 도입하기 어려운 상황에서 온프레미스 환경과 AWS에 대한 네트워크 연결이 필요하다면 Site to Site VPN(S2S VPN)을 생각해볼 수 있다.

S2S VPN은 인터넷 위에 암호화된(IPSec) 전용 터널을 만들어서 온프레미스 네트워크와 AWS 네트워크를 가상 사설망(VPN)처럼 연결한다. Client VPN는 사용자 중심(User to Network)이라면 S2S VPN은 이름 처럼 온프레미스 네트워크와 AWS 네트워크 간의 연결을 중심으로 둔다.

먼저 S2S VPN은 DX와는 다르게 내부 백본 망이 아닌 인터넷을 거치는데, IPSec 프로토콜을 사용한다.

이때의 암호화된 통신 경로를 터널(Tunnel)이라고 하는데, 고가용성을 위해 2개의 터널을 제공하여 이중화한다. 필요에 따라 하나의 터널만 Active 상태로 유지, 다른 터널을 Standby 상태로 유지할 수 있고 둘 다 Activy 상태로 유지할 수 도 있다.

이는 SPOF(단일 지점 장애)를 방지하기 위함으로, 하나의 터널로 두 네트워크를 연결하였을 때 해당 터널의 연결이 끊기게 된다면 서비스 전체가 다운되는 SPOF 위험이 있기 때문이다.

CGW(Customer Gateway)는 온프레미스(Customer)의 VPN 게이트웨이 기기(라우터, 방화벽 등) 또는 소프트웨어(strongSwan 등)를 AWS에 나타내는 논리적 리소스이며, Public IP와 BGP ASN와 같은 정보를 AWS에 제공한다.

VGW(Virtual Private Gateway)는 VPC에 연결되어 온프레미스 네트워크와 통신하기 위한 게이트웨이이다.

위 다이어그램에선 필자가 단일 VPC를 기준으로 제작하여서 빠져있지만, 여러 VPC를 Hub and Spoke 모델로 묶는 라우팅 허브 서비스인 TGW(Transit Gateway)를 사용할 수 있다.

대규모 아키텍처에선 이 방식을 선호한다. TGW에 대해선 이 포스팅을 참고하면 좋을 듯 하고, Accelerated VPN이라고 해서 Global Accelerator를 붙일 수 있는데 이는 TGW에 붙은 VPN만 지원한다.

CGW가 가리키는 온프레미스가(물리적인 장비나 소프트웨어 등) BGP를 지원한다면 동적 라우팅을 사용하는 것을 권장하고, 그렇지 않다면 정적 라우팅을 사용할 수 있다. (참고: AWS Docs — Static and dynamic routing in AWS Site-to-Site VPN)

그 외의 DPD(Dead Peer Detection), MTU/MSS 클램핑 등의 구성은 언젠가 IPSec에 대해 다뤄볼 때 자세히 다뤄보도록 하고, 이 포스팅에선 생략한다. 원한다면 직접 찾아보길 바란다.

2. Example Demo

실제 온프레미스 환경에서 실습하고 도입하여 결과를 본다면 더할 나위 없겠지만, 현재 필자가 온프레미스 환경을 구축하기 어려운 상황이기 때문에 AWS VPC를 별도로 만들어 온프레미스 환경을 만들어보겠다.

실습의 목표와 동작은 아래와 같다. 자세한 구성은 아키텍처를 직접 구축해보면서 설명하도록 하겠다.
(아키텍처 다이어그램이 잘 보이지 않는다면 이미지 링크를 복사하여 새 탭에서 열어보자.)

  • VPC는 AWS 클라우드 환경을 가정한 VPC A(CIDR 10.10.0.0/16)과 VPC B(CIDR 10.20.0.0/16), 그리고 온프레미스 환경을 가정한 VPC C(CIDR 10.30.0.0/16)가 존재한다.
  • AWS 클라우드 환경을 가정한 VPC A와 B는 온프레미스 환경을 가정한 VPC C와 통신할 수 있어야하며, VPC A와 B는 서로 통신할 수 없도록 구성한다.
  • S2S VPN을 통해 온프레미스 환경(VPC C=10.30.0.0/16)과 클라우드 환경의 TGW를 연결하고, 아키텍처에 명시된 대로 적절하게 Route Table을 설정하고 VPC/VPN Attachment를 Associate 한다.

  • 온프레미스 환경(VPC C=10.30.0.0/16)에서 아키텍처 상 존재하는 "Public StrongSwan EC2"는 멀티 플랫폼 IPsec 구현체인 StrongSwan을 사용하고, Default Gateway, VPN Router와 같이 동작한다. 다만 간단한 실습을 위해 동일한 EC2에서 ICMP Ping을 테스트하고, CGW 구성을 위해 EIP를 할당해야 한다.
  • CGW는 정적 라우팅(Static)을 사용한다. (BGP를 구현하기 위해선 소프트웨어적으로 구성해야 할 내용이 많아 이 포스팅에선 다루지 않겠다.)

2-1. Cloud

클라우드 환경으로 가정한 VPC A와 VPC B를 구성하고, ICMP Ping 대상인 Private EC2를 프로비저닝하고, 반대로 온프레미스 환경으로 통신이 되는지 테스트하기 위해 Bastion Host를 구성하겠다. Bastion Host는 Public IP를 부여받는다.

(1) VPC A, VPC B

VPC A는 CIDR 10.10.0.0/16, VPC B는 CIDR 10.20.0.0/16 이며, 2개의 Private Subnet과 2개의 Public Subnet을 가지는 일반적인 구성이다.

각각 위와 같이 구성하였다. 만약 Bastion Host를 사용하지 않는다면 IGW 없이 오로지 Private Subnet으로만 구성할 수 있을 것이다. (Bastion Host를 구성하지 않고 모든 구성이 끝난 이후 VPC C의 Public StrongSwan EC2에서 SSH 접속을 해도 무방하다.)

(2) Private EC2, (Option) Bastion Host

방금 만들었던 VPC 2개의 Private Subnet에 EC2를 프로비저닝하자. 이는 곧 온프레미스를 가정한 EC2에서 해당 Private EC2로 통신이 될 수 있는 것을 테스트하기 위함이다.

EC2를 프로비저닝할 때 주의깊게 봐야할 것은 네트워크 구성과 보안그룹 구성이다. A, B VPC를 선택하고 Private Subnet을 선택한다.

그리고 보안 그룹 규칙을 추가하는데, ICMP를 선택하고 CIDR 10.30.0.0/16을 입력한다. 이는 곧 온프레미스 환경의 트래픽만 받겠다는 의미이다. SSH는 필요에 따라 추가하자.

이렇게 VPC A, B에 각각 하나씩의 Private EC2를 프로비저닝하면 된다.

2-2. On-Premise

다음으로 온프레미스를 모방한 VPC C, CIDR 10.30.0.0/16을 만들고 EC2에 StrongSwan을 설치하여 VPN Default Gateway 역할을 하도록 구성하겠다.

VPC C 내부 다른 서버들은 StrongSwan을 설치한 EC2를 Default Gateway로 설정하면 되는데, 이 포스팅에선 StrongSwan을 설치함과 동시에 최종 테스트까지 해보겠다.

(1) VPC C

CIDR은 10.30.0.0/16으로 설정하고, 그 외에 구성할 것은 따로 없다. 다만 Public StrongSwan EC2는 Public IP와 EIP가 필요하므로 IGW 및 Public Subnet은 필수적으로 필요하다.

(2) Public StrongSwan EC2, EIP

Public StrongSwan EC2는 앞서 여러번 언급했지만 VPN Default Gateway (Router) 역할을 하는 EC2이다. CGW에 Public IP를 설정해야 하는데, 이를 위해 EIP를 할당하도록 하겠다.

그리고 인바운드 규칙에선 SSH 접속을 위해 SSH를 추가하고, IPSec의 IKE와 NAT-T를 위해 UDP 500 및 UDP 4500 포트를 열어줘야 한다.

인스턴스 후 Public IP가 부여된 것을 확인하였다면 EIP를 할당해줘야 한다. 필수는 아니지만, StrongSwan EC2를 재부팅하였을 때 IP가 바뀌지 않도록 할당해주는 것이 좋다.

2-3. Transit Gateway

다음으로 TGW를 구성하고 Route Table을 만든 다음, VPC Attachment를 Associate 해보겠다. VPC C는 온프레미스 환경이라고 가정하였기 때문에 VPC Attachment를 만들지 않고 이후 S2S VPN을 만들어 VPN Attachment를 만들고 Associate 하도록 한다.

(1) TGW

TGW를 만들 때 옵션은 기본값으로 두도록 하자. 조금만 기다리면 아래와 같이 활성화된다.

(2) VPC Attachment

VPC A와 VPC B에 대한 Attachment를 구성하도록 하자.

(3) Route Table

그리고 Route Table을 만드는데, VPC A와 VPC B는 서로 통신이 되면 안되고 오로지 VPC A와 VPC C, 그리고 VPC B와 VPC C 간 통신이 되어야 하기 때문에 VPC 별로 Route Table을 만들어 주도록 하겠다.

이제 VPC C를 제외하고 만들어둔 Attachment를 각각의 Route Table에 Associate 한다.

이미 연결이 되어있다고 나올 수 있는데, Attachment를 만들면 기본 Route Table에 Associate 되기 때문이다. 기본 Route Table에서 Associate를 삭제하고 다시 시도하면 된다.

마찬가지로 B Route Table도 구성하면 된다.

라우팅 설정은 VPN Attachment를 만들고 tgw-rt-vpn에 Associate 한 다음 설정하겠다.


그리고 각 VPC A, B Route Table에 만든 TGW를 설정해둬야 한다.

이때 VPC A, B 모두 VPC C로만 통신이 되어야 하기 때문에 대상을 10.30.0.0/16으로 설정한다.

2-4. S2S VPN

다음으로 S2S VPN을 구성하고 TGW에 연결해보도록 하겠다. 그러기 위해선 먼저 CGW를 생성하도록 하자.

(1) CGW

여기서 IP 주소는 아까 프로비저닝해둔 VPC C의 Strongswan EC2의 Public IP(EIP)를 입력한다.

(2) VPN Connection

다음으로 만들어둔 CGW와 TGW를 연결하기 위해 VPN Connection을 생성하도록 한다.

라우팅 옵션은 정적 라우팅을 선택한다.

이렇게 VPN Connection을 만들게 된다면 아래와 같이 "구성 다운로드" 버튼이 있는데, 클릭하여 구성을 다운로드하자.

(3) StrongSwan

다음으로 Public Strong Swan EC2에 접속하여 StrongSwan을 설치하고 구성해야 한다. Ubuntu를 기준으로 한다.

sudo apt update
sudo apt install -y strongswan
ipsec version # 5.5.1 이상 필요

그리고 /etc/sysctl.conf를 VIM이나 Nano로 열어서 아래와 같이 수정하자.

# /etc/sysctl.conf

net.ipv4.ip_forward = 1

아래의 명령어로 적용한다.

sudo sysctl -p

이제 /etc/ipsec.conf를 구성해야 하는데, 값들은 다운 받은 구성 파일을 참고하여 적절히 수정하자. (Docs 참고)

# /etc/ipsec.conf

config setup
    uniqueids=no

conn Tunnel1
    auto=start
    left=%defaultroute
    leftid=3.36.6.148
    right=43.200.198.51

    type=tunnel
    keyexchange=ikev1
    leftauth=psk
    rightauth=psk

    ike=aes128-sha1-modp1024
    esp=aes128-sha1-modp1024
    ikelifetime=8h
    lifetime=1h

    leftsubnet=0.0.0.0/0
    rightsubnet=0.0.0.0/0

    dpddelay=10s
    dpdtimeout=30s
    dpdaction=restart

    mark=100

conn Tunnel2
    auto=start
    left=%defaultroute
    leftid=3.36.6.148
    right=43.203.78.35

    type=tunnel
    keyexchange=ikev1
    leftauth=psk
    rightauth=psk

    ike=aes128-sha1-modp1024
    esp=aes128-sha1-modp1024

    leftsubnet=0.0.0.0/0
    rightsubnet=0.0.0.0/0

    dpddelay=10s
    dpdtimeout=30s
    dpdaction=restart

    mark=200

다음을 /etc/ipsec.secrets를 구성하자. 여기에 PSK 값을 넣어야 한다.

# /etc/ipsec.secrets

3.36.6.148 43.200.198.51 : PSK "VCwEJPl6ncxL_k.YBHd_ujpc8eMCpWFg"
3.36.6.148 43.203.78.35 : PSK "eJY36Ndv2b3VDm2gTotcYkzobRVgifmY"

다음으로 Route Based를 위해 VTI 인터페이스를 생성하겠다.

# Tunnel 1

sudo ip link add Tunnel1 type vti local 3.36.6.148 remote 43.200.198.51 key 100
sudo ip addr add 169.254.216.30/30 remote 169.254.216.29/30 dev Tunnel1
sudo ip link set Tunnel1 up mtu 1419

# Tunnel 2

sudo ip link add Tunnel2 type vti local 3.36.6.148 remote 43.203.78.35 key 200
sudo ip addr add 169.254.123.170/30 remote 169.254.123.169/30 dev Tunnel2
sudo ip link set Tunnel2 up mtu 1419

sudo ip route add 10.10.0.0/16 dev Tunnel1 metric 100
sudo ip route add 10.10.0.0/16 dev Tunnel2 metric 200

sudo ip route add 10.20.0.0/16 dev Tunnel1 metric 100
sudo ip route add 10.20.0.0/16 dev Tunnel2 metric 200

sudo iptables -t mangle -A FORWARD -o Tunnel1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
sudo iptables -t mangle -A INPUT -p esp -s 43.200.198.51 -d 3.36.6.148 -j MARK --set-xmark 100

sudo iptables -t mangle -A FORWARD -o Tunnel2 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
sudo iptables -t mangle -A INPUT -p esp -s 43.203.78.35 -d 3.36.6.148 -j MARK --set-xmark 200

마지막으로 /etc/sysctl.conf를 아래와 같이 수정하자. (네트워크 인터페이스는 ifconfig 또는 ip addr로 확인한 후 수정하자. 필자의 경우 enX0 였지만 eth0 등으로 되어있을 수 있다.)

# /etc/sysctl.conf

net.ipv4.conf.Tunnel1.rp_filter=2
net.ipv4.conf.Tunnel1.disable_policy=1

net.ipv4.conf.Tunnel2.rp_filter=2
net.ipv4.conf.Tunnel2.disable_policy=1

net.ipv4.conf.enX0.disable_xfrm=1
net.ipv4.conf.enX0.disable_policy=1

이제 sysctl을 적용하고 StrongSwan도 재시작을 해보자.

sudo sysctl -p
sudo ipsec restart

sudo ipsec status
root@ip-10-30-12-85:~# sudo ipsec status
Security Associations (2 up, 0 connecting):
     Tunnel2[2]: ESTABLISHED 10 minutes ago, 10.30.12.85[3.36.6.148]...43.203.78.35[43.203.78.35]
     Tunnel2{2}:  INSTALLED, TUNNEL, reqid 2, ESP in UDP SPIs: c3f1aaa0_i cfc3a4c8_o
     Tunnel2{2}:   0.0.0.0/0 === 0.0.0.0/0
     Tunnel1[1]: ESTABLISHED 10 minutes ago, 10.30.12.85[3.36.6.148]...43.200.198.51[43.200.198.51]
     Tunnel1{1}:  INSTALLED, TUNNEL, reqid 1, ESP in UDP SPIs: cb5f84a9_i c38b9b99_o
     Tunnel1{1}:   0.0.0.0/0 === 0.0.0.0/0

ESTABLISHED라고 표시되면 성공인 것이다. 콘솔에서도 UP 상태가 된다.

여기까지 구성을 완료하였다면 다시 AWS TGW로 돌아가 VPN Attachment를 demo-tgw-rt-vpn에 Associate 하고 라우팅 설정을 해보자.

(4) TGW Route Table

기본 Route Table에 Associate 되어 있던 VPN Attachment를 끊어주고 새롭게 Associate를 맺어주자.

다음으로 라우팅은 아래와 같이 구성한다.

  • tgw-rt-a : 10.30.0.0/16, VPN attachment
  • tgw-rt-b : 10.30.0.0/16, VPN attachment

  • tgw-rt-vpn : 10.10.0.0/16, VPC A attachment
  • tgw-rt-vpn : 10.20.0.0/16, VPC B attachment

2-5. Testing

포스팅에선 적지 않았지만 VPC A와 B 모두 테스트해보길 바란다. 여기선 VPC A와 VPC C 간의 ICMP Ping이 가는지 확인해보자.

VPC A Private EC2

root@ip-10-10-141-106:/home/ubuntu# ping 10.30.12.85 -c 5
PING 10.30.12.85 (10.30.12.85) 56(84) bytes of data.
64 bytes from 10.30.12.85: icmp_seq=1 ttl=63 time=3.12 ms
64 bytes from 10.30.12.85: icmp_seq=2 ttl=63 time=2.64 ms
64 bytes from 10.30.12.85: icmp_seq=3 ttl=63 time=2.49 ms
64 bytes from 10.30.12.85: icmp_seq=4 ttl=63 time=2.47 ms
64 bytes from 10.30.12.85: icmp_seq=5 ttl=63 time=2.66 ms

VPC C StrongSwan

root@ip-10-30-12-85:~# ping 10.10.141.106 -c 5
PING 10.10.141.106 (10.10.141.106) 56(84) bytes of data.
64 bytes from 10.10.141.106: icmp_seq=1 ttl=62 time=3.16 ms
64 bytes from 10.10.141.106: icmp_seq=2 ttl=62 time=2.23 ms
64 bytes from 10.10.141.106: icmp_seq=3 ttl=62 time=2.43 ms
64 bytes from 10.10.141.106: icmp_seq=4 ttl=62 time=2.33 ms
64 bytes from 10.10.141.106: icmp_seq=5 ttl=62 time=2.38 ms

위와 같이 잘 통신되는 것을 볼 수 있다. 추가적으로 이 포스팅에선 StrongSwan에서 직접 Ping을 테스트하여 문제가 없었지만, 이 포스팅 처럼 EC2가 라우터 역할을 하는 경우 Source/Destination Check를 비활성화 해야한다. (대표적인 예시가 NAT Gateway 인스턴스이다.)

추가적으로 블로그에서 다뤘던 VTI, iptables, route 등은 재부팅 시 초기화될 수 있다. 때문에 재부팅을 해도 날라가지 않게 설정해야 하는데, 이건 직접 찾아보길 바란다.

포스팅을 급하게 쓰느라 실습 부분에서 빠진 내용이 있을 것 같은데, 추후 BGP 동적 라우팅을 통해 S2S VPN을 구성하는 포스팅을 작성하면서 보강하도록 하겠다.

profile
세명컴퓨터고등학교 보안과 11기 / 클라우드, DevOps 동아리 — Null4U 부장 / (광고는 제가 넣는게 아닙니다..)

0개의 댓글