WireGuard로 멋진 VPN 서버 구축하기 - 1

박재현

안녕하세요, 저는 인프라셀에서 데브옵스 엔지니어로 일하고 있는 박재현이라고 합니다. 데브시스터즈에서는 사내 인프라 혹은 클라우드 인프라 자원에 접근하기 위해서 OpenVPN을 사용해왔습니다. 이번 글에서는 OpenVPN에서 WireGuard 기반 VPN으로 전환하게 된 이유와 어떻게 VPN 시스템을 직접 구축하게 되었는지에 대해서 설명합니다. 글의 내용이 길고 주제에 대한 연관성으로 2부로 구성하게 되었습니다. 1부에서는 WireGuard에 대한 설명과, 커널 네트워킹 스택, 그리고 AWS의 네트워킹 스택에 대한 이야기를 합니다. 2부에서는 VPN 서버에서 사용하는 인증과 eBPF를 이용한 패킷 필터링, 그리고 eBPF를 이용한 유저 편의기능을 소개합니다.

공식 WireGuard 로고

VPN?

VPN이란 Virtual Private Network의 약자로, 가상 사설망이라는 의미입니다. 일반적으로 사설망은 외부에서 접근이 안 되고, 사설망 내부에서만 통신을 할 수 있습니다. 예를 들어, 게임 서버와 데이터베이스는 외부에서 직접 접근하지 못하면서도 서로 통신은 할 수 있어야하기 때문에, 사설망 내부에 함께 두는 것이 적절합니다. 그런데 서버 개발 혹은 운영을 하다보면, 직접 그러한 자원들에 사람이 직접 접근해야할 필요가 종종 생깁니다. 이럴 때 VPN을 사용하게 되면, 외부 네트워크로부터 VPN 서버를 통해 사설망 내부 자원에 접근할 수 있게 됩니다.

VPN은 외부 네트워크와 사설 네트워크 망을 잇는 통로이기 때문에, 그만큼 보안이 중요합니다. 허가된 사용자만 접근할 수 있어야하며, 통신이 적절히 잘 암호화되어서 중간에서 도청 또한 불가능해야만 합니다. 이러한 요구조건 위에서 다른 편의기능을 추가한 여러 VPN 구현체들이 존재하고, 그 중에서 데브시스터즈는 OpenVPN이라는 솔루션을 사용하고 있었습니다.

왜 OpenVPN → WireGuard?

OpenVPN으로도 오랫동안 잘 사용하고 있었지만, 사용해오다보니 여러 가지 문제점들이 있었습니다.

  1. Single Sign-On(SSO) 연동이 불편합니다. 데브시스터즈는 Keycloak이라는 제품을 사용해 인증 및 권한 제어를 하고, Google 계정을 OAuth 2로 연동하여 사용하고 있습니다. 많은 서비스들을 여기서 제공하는 SSO로 인증을 수행하고 있었는데, OpenVPN에 연결할 때에 사용할 사용자 계정으로 SSO 연동을 하기 어려워 OpenVPN 계정은 따로 관리를 해야만 했고, 신규 입사자가 들어올 때마다 일일이 계정을 발급해주거나 퇴사자 처리를 할 때 따로 계정을 지워줘야 하는 등의 관리 상 불편함이 있었습니다.
  2. 사설망 접근 유저가 누구인지 접근 로그를 보고 특정을 하기가 어렵습니다. OpenVPN을 사용하고 있을 때, VPN 서버에서 사설망으로 가는 패킷에 대해 NAT(Network Address Translation)를 적용하고 있었습니다. 이렇게 하게 되면 NAT의 특성 상 사설망 자원들의 IP 로그에는 어떤 유저가 사용했는지에 관계 없이 모두 VPN 서버 자체의 IP가 찍히게 됩니다. 보안 사고 혹은 침해 사고 등에 대비하기 위해서는 VPN을 사용하더라도 어디에 누가 접근을 시도했는지 특정할 수 있으면 좋겠다는 의견이 있었습니다.
  3. OpenVPN 기업용 라이센스 관리가 불편합니다. 유저 수 만큼 비용이 늘어나는 데다가, 라이센스 만료에도 항상 신경쓰고 있어야하기 때문입니다.
  4. WireGuard와 비교해서 무겁고 느립니다. 여러 벤치마크 자료들을 봐도 레이턴시와 대역폭 측면에서 WireGuard가 우세에 있고, 초기 연결 속도 또한 체감이 될 정도로 차이가 납니다.

이러한 문제점들을 인식하고 있던 중, WireGuard를 사용한 VPN을 써보면 어떻겠냐는 의견이 나왔습니다.

WireGuard?

WireGuard는 다른 VPN 솔루션들과는 달리 두 지점(Peer) 사이의 통신을 암호화하는 프로토콜에 대한 구현체입니다. WireGuard의 장점은 여러 가지가 있지만, 그 중 대표적인 것만 꼽아보면 다음과 같습니다.

  1. 속도와 레이턴시가 우수합니다. UDP만을 사용하며, 불안정한 연결 환경에서도 잘 작동하도록 설계되었습니다. 또한 Linux에 내장되어 있는 암호화 기법들을 사용하면서 잘 최적화되어 있어 암호화 성능도 좋고, 모바일이나 임베디드 환경에서도 사용하기 좋습니다.
  2. 첨단 암호화 기법을 사용하여 안전하며, 성능도 우수합니다. WireGuard에서 내부적으로 사용하는 Noise protocol framework는 Zero round trip, forward secrecy 등을 지원하여 매우 빠른 연결 속도와 더 나은 통신의 보안을 지원합니다. 또한, WireGuard에서 패킷 암호화에 사용하는 Curve25519, ChaCha20 Poly1305 등의 암호화 기법들은 다른 암호화 기법들과 비교하여 훨씬 안전하고 빠른 암호화를 지원합니다.
  3. Peer 배포가 쉽습니다. WireGuard에서 Peer끼리 정보를 주고 받을 때 공개키 암호화를 사용합니다. 이는 바꿔말하면, 공개키로 자신의 신원을 증명할 수 있다는 말입니다. 따라서 WireGuard 통신을 세팅할 때에는 공개키만 서로 알고 있다면 가능하기 때문에, 우리가 자주 쓰는 SSH Key의 deploy 만큼이나 Peer 배포가 간단합니다.
  4. 연결이 안정적입니다. 일상 생활에서 스마트폰을 사용하다보면 와이파이와 LTE 네트워크 사이를 전환하는 등의 상황을 많이 접하곤 합니다. 이럴 때에 일반적인 TCP 연결이라면 재연결을 해야하는 상황이 벌어지기도 하여 사용 경험이 안 좋은 때가 있습니다. 하지만 WireGuard는 UDP만을 사용하고, 공개키를 기반으로 연결이 정의되면서 내부적으로 상태를 가지지 않는(Stateless) 특성 덕분에, 이러한 상황에서도 끊기지 않고 안정적이게 통신을 이어나갈 수 있고, 데몬 등으로 연결을 관리해주어야할 필요가 없어지기 때문에 연결 관리 코스트에서도 자유롭습니다.
  5. 리눅스에 내장되어있어 사용하기 편합니다. WireGuard는 리눅스 커널 5.6 버전부터 기본으로 포함되어 있어 그 버전 이상의 커널을 사용하면 따로 설치가 필요 없는데다가, 유저 공간의 어플리케이션이 아닌 커널 모듈로서 작동하기 때문에, 서버 어플리케이션이 비정상 종료 되어도 운영체제가 돌아가고 있는 한 WireGuard의 기능들은 모두 정상적으로 동작하여 안정적이기까지 합니다.

하지만 두 지점 사이의 통신을 구현한 프로토콜에 지나지 않기 때문에 다양한 부가 기능들로 무장한 다른 VPN 솔루션들과 편의성 측면에서는 매우 비교되는 것이 현실입니다. 바꿔말하면, 필요한 부분들을 직접 입맛에 맞게 구현해서 쓰면 유연성과 편의성, 그리고 위에 나열한 장점들까지 모두 가져갈 수 있는 좋은 VPN 솔루션을 구현할 수 있다는 결론에 다다를 수 있습니다.

요구사항 분석

OpenVPN을 사용하면서 느꼈던 단점을 개선하면서, 원래 사용하고 있던 기능들도 모두 포함해야하기 때문에, 구현해야하는 요구사항들을 분석해보았습니다.

  • 그룹 별 접근 가능한 사설망 자원 접근 제어
  • 유저마다 고유한 아이피로 사설망에 접근
  • Keycloak SSO에 인증 연동
  • 키를 일정 시간만 사용 가능하게 하도록 하기, 활성화되지 않은 키에 대한 경고 알림 보내주기
  • 기타 보안 및 편의 기능, 관리자 UI

서버 구현을 작성할 때 어떤 언어로 작성할 지도 중요합니다. 서버 구현체는 Go를 사용하기로 결정했는데, 그 이유는 다음과 같습니다.

  1. 데브시스터즈의 많은 서비스와 컴포넌트들이 Go로 작성되어 있습니다. 따라서 내부 프로그래머들에게 익숙한 언어이기 때문에 쉽게 코드를 읽고 유지보수를 할 수 있습니다.
  2. 구현에 필요한 라이브러리들이 잘 만들어져 있습니다. 예를 들면, 유저스페이스에서 WireGuard 커널 모듈과 통신할 때 wgctrl-go 라이브러리를 쓸 수 있습니다. 또한, 리눅스 네트워킹 스택과 통신을 도와주는 netlink, go-tc 라이브러리, eBPF 프로그램 로딩과 관리에 필요한 cilium/ebpf 등, 필요한 라이브러리들이 이미 소스코드가 공개되어 있습니다.

요구사항들을 잘 분석했으니, 이제 구현 과정을 한번 따라가봅시다.

그룹 별 접근 제어

데브시스터즈는 클라우드 컴퓨팅 플랫폼으로 AWS를 사용합니다. AWS에는 논리적으로 분리된 사설망인 VPC라는 개념이 있는데, 각 VPC는 IP 주소의 대역을 가지게 됩니다. 데브시스터즈에서는 용도와 권한에 따라 여러 VPC를 구성해서 쓰고 있습니다. 예를 들면, 프로덕션 게임 서버와 개발용 게임 서버는 같은 네트워크에 위치할 필요가 없으므로 VPC를 사용해 분리를 해둔다거나, 민감한 정보들을 담은 데이터베이스를 다른 서버들로 분리하고 관리자만 접근 가능하도록 하는 것입니다. 이처럼 용도 혹은 사용자의 권한에 따라서 VPC를 분리해놓았기 때문에, VPN 서버에 연결한 사용자가 내부 VPC 전부에 접근할 수 있으면 보안 측면에서 허술할 수 있습니다. 따라서 직군마다 접근할 수 있는 망 대역을 분리하는 작업이 필요합니다. Keycloak에는 그룹이라는 개념이 있는데, 이것을 그대로 사용하여 접근 제어에 사용하기로 결정했습니다.

VPN 서버 내부적으로는 각 그룹 별로 사용하는 네트워크 인터페이스를 나누고, 각자의 라우팅 테이블을 가지도록 설계했습니다.

1 route
인터페이스 별로 따로 라우팅 테이블을 만들어 관리합니다.

네트워크 인터페이스가 각각의 라우팅 테이블을 사용하도록 하기 위해서는 iproute2ip-rule을 사용해서 제어하고, 나머지 필요한 "목적지 기반" 라우팅은 ip-route를 사용하여 제어합니다.

유저 개개인은 각 직군에 대응되는 네트워크 인터페이스를 배정받아서 사용하게 됩니다. WireGuard에서는 네트워크 인터페이스 별로 다른 비밀키를 사용해서 인증하도록 되어있으므로, 다른 직군에 속한 유저가 더 넓은 권한을 가지는 직군의 네트워크 인터페이스를 사용하는 것은 불가능합니다. 만에 하나 다른 네트워크 인터페이스를 사용하기 위해서 유저가 비밀키를 알아내서 설정 파일을 조작한다고 해도, 서버에서 각 네트워크 인터페이스에서 허용하는 Peer의 공개키의 리스트를 직접 관리하기 때문에, WireGuard 모듈 단에서 패킷이 차단되어 부정적인 사용이 불가능해집니다.

네트워크 인터페이스를 나누었으니 각 인터페이스 별로 접근 가능한 VPC를 제한해야 합니다. 이는 리눅스에 기본적으로 존재하는 iproute2 의 라우팅 테이블을 사용해서 구현합니다. 기본적으로 라우팅 테이블은 Destination IP를 보고, 라우팅 테이블에 대응되는 항목이 있다면, 그 항목에 표기된 곳으로 패킷을 보내주는 기능을 수행합니다.

하지만 우리가 필요한 기능은 특정 대역으로 가는 패킷이 아니라면 패킷을 드랍해야 하는 기능입니다. 기본적으로 ip-route 는 "목적지 기반" 라우팅을 사용하는데, 이것과 다르게 우리는 그 여집합에 매치하여 패킷을 드랍하기를 원합니다. 이 행동을 구현하기 위해서 특별한 Route Type을 사용하는데요, 바로 throw 라는 타입입니다. 이 타입을 가진 항목에 패킷이 매칭되게 되면, 마치 "아무 항목에도 매치되지 않았음" 이라는 효과를 내게 합니다. 즉, throw 타입 라우트에 매치된 패킷은 곧바로 해당 라우팅 테이블에서 빠져나가고, 다음 규칙으로 이동하게 됩니다. 프로그래밍 언어로 표현하자면 마치 break 문과 비슷한 효과를 내게 됩니다.

따라서 라우팅 테이블에 허용할 VPC 대역에 대해 모두 throw 타입을 사용하고, 마지막으로 디폴트 라우트 엔트리의 타입으로 모든 패킷을 즉시 drop해버리는 blackhole 타입을 사용하게 되면, "특정" VPC 대역들만 허용하는 라우팅 규칙을 만들 수 있습니다.

2 packet filter
패킷이 허용될 때와 드랍될 때의 다이어그램

따라서 각 그룹 별로 네트워크 인터페이스를 할당하고, 각 인터페이스에 대응되는 라우팅 테이블을 위 전략으로 만들어주면, 그룹 별 VPC 대역 접근 제어 기능을 쉽게 만들 수 있습니다.

유저 별 고유한 IP로 통신하게 하기

위 OpenVPN 사용 시 단점으로 들었던 점 중 하나가, 사설망 내 자원들에 찍히는 IP 로그가 모두 VPN 서버의 IP로 나와서 누가 접근했는지 알 수 없게 된다는 것이었습니다.

3 bad vpn server log
OpenVPN을 NAT로 사용했을 때 찍히는 접근 로그. 모두 VPN 서버의 아이피가 찍혀서 의미가 없음.

WireGuard에서는 각 Peer의 통신에 사용할 IP 주소를 각자 설정할 수 있고, 더 나아가 특정 Peer에 대해 허용할 IP를 설정해줄 수 있습니다. 이 기능을 사용하면 WireGuard가 패킷을 받아서 다시 외부로 내보낼 때(포워딩), Source IP로 유저의 고유한 아이피를 달아서 보내줄 수 있습니다. 다만 이런 식으로 유저의 고유한 아이피로 찍히게 만들고 싶다면 VPN 서버에서 외부로 패킷을 송신할 때 NAT를 수행하면 안 된다는 의미입니다.

4 good vpn server log
NAT를 사용하지 않는 경우 유저별로 고유한 아이피가 남는 모습. 이상적인 상황.

위 그림처럼 WireGuard 서버가 Source 아이피가 본인의 아이피가 아닌 "유저 고유의 아이피"인 패킷을 송신할 수 있다면 사설망 자원 접근 로그에 유저를 특정할 수 있는 유의미한 로그가 남게 되어 유용할 것입니다.

하지만 여기에 문제점이 두 가지가 있습니다.

  1. AWS VPC에서는 Source/Dest IP Check라는 기능이 활성화되어 있어 패킷의 근원지와 목적지 아이피가 실제 인스턴스 아이피와 같은지에 대해서 체크하고, 만약 같지 않다면 조용히 패킷을 드랍하는 기능이 있습니다. 따라서 근원지가 "유저 고유의 아이피"인 패킷을 WireGuard 인스턴스가 보내게 되면, 패킷은 VPC 내부에서 조용히 사라지고 말 것입니다.
  2. NAT를 수행하지 않는다면 우선은 사설망 자원까지는 패킷이 잘 갈 수 있겠지만, 반대로 VPN 서버로 다시 돌아오는 패킷은 유실될 것입니다. 왜냐하면 VPN 서버의 주소가 아닌 유저 별 고유 아이피가 목적지인 패킷을 송신할 것이기 때문입니다.
5 response dropped
돌아오는 패킷은 목적지를 찾지 못하고 유실될 것입니다.

첫 번째 문제점은 비교적 쉽게 해결이 됩니다. AWS에서는 해당 기능을 기본적으로는 활성화해두지만, 유저가 직접 개별 인스턴스에 대해서 기능을 비활성화할 수 있기 때문입니다. 따라서 WireGuard 인스턴스에 대해서 Source / Destination 체크 기능을 비활성화하면 쉽게 문제가 해결됩니다.

6 aws source dest check
AWS에서는 Source/Destination check 사용 여부를 유저가 직접 결정할 수 있습니다.

두 번째 문제점은 쉽게 해결되는 문제는 아닙니다. 왜냐하면 인스턴스는 한 대이지만 유저는 여러 명일 수 있으므로, 여러 개의 유저 아이피가 목적지인 패킷에 대해서 인스턴스로 패킷을 라우팅해야하기 때문입니다. 다행히도, AWS 네트워크 스택은 충분히 유연해서, 임의의 아이피 대역에 대해 유저가 원하는 대상으로 라우팅을 설정할 수 있습니다. 여기에선 특정 대역을 VPN 서버 인스턴스의 ENI로 직접 보내주는 라우트를 만들면 됩니다.

따라서, 우선 유저 아이피를 할당할 아이피 대역을 하나 잡아두고, AWS의 다양한 네트워킹 스택을 사용해서 문제를 풀어나가려고 합니다. 주의할 점은 이 아이피 대역은 그 어떤 VPC 대역과도 겹치지 않는 대역이어야 합니다. 만약 겹치게 된다면, 그 겹치는 주소로 패킷이 송신될 때 어느 VPC 쪽으로 패킷이 가야할지 모르기 때문에, 예상치 못한 결과가 나올 수 있습니다. 이렇게 겹치지 않는 고유한 유저 아이피 대역을 하나 생각해두었다면, 통신을 할 수 있도록 하려면 어떤 작업이 필요한지 생각해봅니다.

AWS VPC는 특별하게 설정을 해주지 않았다면 외부와는 완전히 차단된 네트워크 망이 됩니다. 여기서 우리는 인터넷과 통신하기 위해서 Internet Gateway 혹은 NAT Gateway 등의 컴포넌트를 이용하고, VPC 끼리 통신하기 위해서 VPC Peering이라는 기법을 사용합니다. 데브시스터즈에서 이미 사용하고 있는 VPC가 매우 많고, 서로 통신이 필요한 VPC끼리는 Peering Connection이 이미 맺어져있어 이것을 사용하고 있는데요, 우리의 VPN 서버도 이 Peering Connection을 사용해서 다른 VPC와 패킷을 주고받으면 좋을 것 같습니다.

추가적으로 통신을 원하는 VPC의 아이피 대역을 Peering Connection으로 보내주어야 하므로, AWS VPC에서 제공하는 Route Table이라는 컴포넌트를 사용할 것입니다. 이 컴포넌트는 VPC 내부에 흐르는 트래픽에 대한 라우팅 규칙을 만들수 있는 기능으로, 리눅스의 라우팅 테이블과 매우 유사한 작동 방식을 가집니다.

따라서 다음 컴포넌트들이 있으면, VPN 서버에서 대상 VPC로의 단방향 통신이 잘 될 것으로 예상할 수 있습니다.

  • VPN 서버의 VPC → 통신을 원하는 VPC의 Peering Connection
  • 위 Peering Connection에 대한 Route Table Entry

하지만 되돌아오는 경로는 어떻게 할까요? 아까 말했듯이 NAT가 되지 않은 패킷이므로, 이제 목적지 IP 주소가 VPN 서버의 주소가 아닌 그 어디에도 존재하지 않는 유저 개별 고유 아이피를 가지게 됩니다. 특별한 처리 없이는 이 패킷을 다시 VPN 서버로 오게 만들 수 없을 것 같군요.

그렇다면 원격 VPC의 Route Table에서 유저 고유 아이피 대역에 대한 라우팅 테이블 엔트리가 필요할 것 같습니다. 우리가 설정한 고유 유저 아이피 대역을 곧바로 VPN 서버가 있는 VPC의 Peering Connection으로 다시 보내도록 라우팅 테이블 항목을 만들면 되지 않을까요?

안타깝게도 다시 되돌아오는 경로는 VPC Peering Connection의 한계에 의해서 불가능합니다. VPC Peering Connection은 기본적으로 "목적지" 주소가 해당 VPC 대역과 불일치하게 되면 조용히 패킷을 드랍하는 행동을 보입니다. 유저 고유 아이피 대역은 WireGuard 서버가 있는 VPC 대역과 불일치하므로 되돌아오는 패킷을 Peering Connection으로 보내는 것은 불가능해보입니다.

여기서 또 다른 컴포넌트를 사용하게 되는데요, 바로 Transit Gateway입니다. Transit Gateway는 기존 Peering Connection을 사용할 때 불편한 점들이 많았는데, 그런 불편한 점들을 개선해주는 매우 유용한 도구입니다. VPC Peering은 1:1의 관계로 정의되기 때문에 VPC가 N개가 있다면, 모든 VPC가 서로 통신할 수 있게 하려면 N * (N-1) / 2 개의 Peering Connection이 필요합니다. 관리가 너무 힘들어집니다. 하지만 Transit Gateway는 마치 중간에서 허브처럼 작동해서, 각 VPC를 Transit Gateway와 연결해두고 적절히 라우팅 테이블만 만져주면 VPC 끼리 통신을 하게 만들 수 있습니다. 필요한 커넥션의 개수가 확연하게 줄어들면서 동시에 관리 코스트가 줄어들게 됩니다. 멋지네요.

7 aws transit gateway
AWS Transit Gateway 다이어그램

그와 동시에 Peering Connection과는 달리, Source / Destination에 대한 특별한 제약사항이 없기 때문에, 우리가 원하는 양상의 라우팅을 가능하게 해 줍니다. 따라서 다시 "되돌아 오는" 길은 Transit Gateway를 통해서 라우팅해주면 오고 가는 길 모두 완성해줄 수 있을 것 같습니다. 정리하자면,

  1. VPN 서버의 VPC → 대상 VPC (Peering Connection)
  2. 대상 VPC → 내부 자원 (Local Route)
  3. 내부 자원 → Transit Gateway (Route Table)
  4. Transit Gateway → VPN 서버 VPC (Transit Gateway Route Table)
  5. VPN 서버 VPC → VPN 서버 (Route Table)
8 example route
예시 라우팅 다이어그램

실제로 위 구성으로 테스트해보면 VPN 서버와 다른 VPC의 자원이 잘 통신이 되는 것을 볼 수 있습니다. 하지만 Transit Gateway도 분명히 한계가 있습니다. 그것은 바로 같은 AWS 리전 내에서만 구성할 수 있다는 것입니다.

데브시스터즈에서는 고가용성 혹은 지역성을 위해서 여러 리전에 걸쳐서 인프라를 구성하고 있습니다. 따라서 VPN 서버도 여러 리전과 통신을 해야할 필요가 있습니다. 따라서 위 네트워크 구성은 같은 리전 내에서는 잘 작동하지만, 리전을 넘어가서 다시 되돌아오는 패킷의 경로는 구성할 수 없습니다.

여기서 Transit Gateway에 비교적 최근에 추가된, Transit Gateway Peering Connection을 사용할 수 있습니다. VPC Peering과 비슷하게, Transit Gateway끼리의 Peering을 구성할 수 있는 기능인데요, 이것을 사용하면 각 리전의 Transit Gateway가 서로 통신할 수 있도록 만들 수 있으므로, 리전 간 통신도 잘 구성할 수 있게 됩니다.

따라서, 리전을 넘어서 통신해야 하는 경우는 다음과 같은 흐름으로 구성하면 됩니다.

  1. VPN 서버의 VPC → 대상 VPC (Peering Connection)
  2. 대상 VPC → 내부 자원 (Local Route)
  3. 내부 자원 → Transit Gateway B (Route Table)
  4. Transit Gateway B → Transit Gateway A (Transit Gateway Peering Connection)
  5. Transit Gateway A → VPN 서버 VPC (Transit Gateway Route Table)
  6. VPN 서버 VPC → VPN 서버 (Route Table)
9 inter region example route
리전 간 통신의 경우에 대한 라우팅 다이어그램

복잡하지만 실제로 작동하는 네트워크 구성으로 보입니다. 이렇게 하면 실제로도 잘 작동할까요? 안타깝지만 여기서도 현실적인 문제에 부딪히게 됩니다. 같은 리전 내에서 실험을 해보면 아주 잘 작동하지만, 리전을 넘어가는 상황(Inter-region)에서는 1 → 2 경로가 잘 작동하지 않습니다. 안타깝지만 "근원지(Source)" IP가 해당 VPC의 대역에 포함되지 않는 경우에는 리전 간 Peering Connection 통신은 잘 지원이 안 되는 현상이 관찰됩니다. Undefined behaviour이거나 VPC Peering의 버그로 보입니다.

그럼 어떻게 하면 될까요? 답은 간단합니다. 1 → 2를 Peering Connection을 사용하지 않고 똑같이 Transit Gateway를 사용하면 됩니다. 따라서 다음과 같은 경로가 완성됩니다.

  1. VPN 서버의 VPC → Transit Gateway A (Route Table)
  2. Transit Gateway A → Transit Gateway B (Transit Gateway Peering Connection)
  3. Transit Gateway B → 대상 VPC (Transit Gateway Route Table)
  4. 대상 VPC → 내부 자원 (Local Route)
  5. 내부 자원 → Transit Gateway B (Route Table)
  6. Transit Gateway B → Transit Gateway A (Transit Gateway Peering Connection)
  7. Transit Gateway A → VPN 서버 VPC (Transit Gateway Route Table)
  8. VPN 서버 VPC → VPN 서버 (Route Table)

이렇게 네트워크 경로를 구성해주면 리전 내부든 리전 간이든 모두 커버할 수 있게 됩니다. 실제로 사설망 리소스에 찍히는 IP 로그를 보게 되면, 각 유저별로 고유한 아이피가 찍혀서 나오는 것을 볼 수 있습니다.

10 final route diagram
간략화된 최종 라우팅 다이어그램

정리

커널의 네트워킹 스택과 AWS의 네트워킹 스택을 잘 조합하여 다음과 같은 요구사항을 충족시켰습니다.

  • 커널의 네트워킹 스택으로 그룹 별 접근 제어를 구현함
  • AWS 네트워크 스택을 잘 이용해서 유저 별 고유 IP로 액세스 로그가 남을 수 있도록 함

이 글은 2부에서 이어집니다. 2부에서는 유저 인증, eBPF, 그리고 eBPF를 이용한 편의 기능과 보안 기능을 다룹니다.

데브시스터즈는 최고의 인재를 찾고 있습니다.

데브시스터즈에서는 능력있는 DevOps Engineer (경력)DevOps Engineer (신입)를 찾고 있습니다.
자세한 내용은 채용 사이트를 확인해주세요!
DevOpsinfra채용

© 2021 Devsisters Corp. All Rights Reserved.