[Network] Private Subnet 외부 접근을 위한 VPN 설정 (with. Docker)
😇 카테고리를 Docker에 달아야하나, Network에 달아야 하나 고민하다가 그냥 Network로 결정
📕 목차
1. Introduction
2. VPN(Virtual Private Network)
1. Introduction
📌 시작하기 앞서
위 블로그 진짜 미쳤습니다.
처음에 VPN 개념 이해가 잘 안 돼서 고생하고 있었는데, 덕분에 과할 정도의 지식을 충전할 수 있었음.
관심이 있다면 시리즈 별로 모두 읽어보면 좋겠지만, 난 그 정도로 low-level의 지식을 필요로 한 건 아니라 pass
📌 As-is
나처럼 가난한 학생이라면 위와 같은 구조의 서버 인프라를 구축하게 될 텐데, 딱히 인프라 구조가 얼마나 고도화되어 있는가는 사실 관계가 없다.
무슨 소린지도 모르고 AWS 강의를 따라하는 입문자들 조차도 Public Subnet과 Private Subnet이라는 이름은 들어봤을 것이다.
(혹시 모르면 위 내용을 훑어보면 좋을 듯하다.)
- Public Subnet: 외부와 자유로운 통신이 가능하고, 외부 인터넷 구간과 직접적으로 통신을 할 수 있는 Public Network
- Private Subnet: 외부에서 직접 접근할 수 없고, NAT GW 혹은 NAT instance를 통해서 내부에서 외부로 단방향 통신을 허용한다.
그런데 한 가지 문제가 발생했다.
서버가 갑자기 중단되어, 이미 퇴근한 개발자가 급히 Private Subnet의 WAS(로그, DB 뭐든 좋다.)에 접근해야 할 필요성이 생겼다고 가정하자.
그러나 보안을 너무 철저하게 지킨 나머지, 개발자는 사내망에 접근할 방도가 없어서 회사까지 급하게 택시를 타고 가야만 하는 상황이 발생한다.
(사실 이것도 On-premise 환경에서나 가능하겠지만, 느슨한 보안 정신으로 aws 서버에 회사의 Public ip로 접근하는 요청은 모두 허용해뒀다고 치자.)
개발자의 ip를 임시로 허용하면 되는 거 아닌가 싶을 수 있지만, 여러 지역에 분산되어 있는 회사처럼 본사 사설망과 지사 사설망을 연결하는 문제도 규모가 커질 뿐 같은 문제다.
그렇다면 약속된 ip를 매번 허용해놓을 것인가? 아니면 급할 때마다 사람이 수작업으로 ip를 허용할 수 있게 Inbound Rule을 수정할 것인가?
불편한 건 둘 째치고, 그럼 AWS의 Inbound 규칙을 허용하는 권한을 모든 개발자들에게 허용할 것인가? 허용하지 않을 거라면, Inbound Rule을 허용해줄 관리자가 퇴근한 상황이라면 또 어쩔 것인가?
예시를 장황하게 늘어놓았지만, 우리의 목표는 결국 단 하나다.
💡 인증된 사용자에겐 외부 접근을 허용해야 한다.
그럼 이제 어떤 방법들을 사용해볼 수 있을 지 고민해보자.
📌 가장 간단한 방법들
이름만 Private고, 실제로는 Inbound를 Private Subnet에 묶어서 접근을 허용한 사례를 많이 봤다.
특히 네트워크 개념을 잘 모르고, 해커톤 등의 이유로 빠른 개발을 수행하는 학생 개발자들이 많이 저지르는 실순데, 이건 그냥 Subnet 분리의 의미가 없어지는 행위다.
그나마 대안이 되는 것이 NAT GW, 비용이 비싸서 부담스럽다면 NAT Instance를 만들어 내부 라우팅을 해주는 것이다.
Public Subnet의 NAT Instance에 ssh 접속에 성공한다면, 터미널에서 Private Subnet의 위치를 알 수 있으므로 접근할 수 있게 된다
그러나 이는 Private Subnet에서 NAT Instance로 포트 개방을 바꿨을 뿐이지, 근본적인 해결책이 되진 않는다.
결국 NAT Instance로 접근하기 위한 외부 IP를 선택적으로 개방해줘야 한다.
📌 사설망(Private Network)
위 문제를 아주 값비싼 비용을 내고 해결하는 방법은 사설망을 설치하는 것이다.
(현실적으로 학생이 할 수 있는 게 아니지만, VPN 개념을 위한 사전 지식으로 소개)
Network를 공부했다면 알겠지만, 점차 고갈되고 있는 IPv4 Address 문제를 해결하기 위해 IETF에서 IPv4 대역 중 일부를 Private IP로 지정했다는 사실을 알고 있을 것이다.
이 주소를 가정, 기업, 기관 등 근거리 통신망(LAN; Local Area Network)에서 사용하다가, 필요한 경우에만 NAT(Network Address Translation)을 이용해 공인 인터넷 통신으로 외부 인터넷 통신을 하면 된다.
그리고 보통 사내에서 사용하는 데스크톱, 서버, DB 등은 Private IP를 할당해 사설망을 구축하는 것이다.
Legacy 방식을 생각해보자.
다양한 장소에서 생겨나기 시작한 다른 사설망과 Private IP를 통한 연결을 하려면 어떻게 해야할까?
군필자라면 답을 알 것이다.
전용 회선(Leased Line)을 구축하여 본사 사설망과 자사 사설망을 연결하면 된다.
이는 인터넷망을 사용하지 않는다.
인터넷 서비스 공급자(ISP)와 계약하여 망 일부를 완전히 독점함으로써, 물리적으로 완전히 분리된 망을 구축하는 개념이다.
따라서 회사 내부 트래픽이 아니면 절대 접근할 수 없기 때문에, 데이터의 안전이 보장되면서 Private IP를 사용해 망을 구축할 수 있게 되는 것이다.
처음에도 언급했듯이 매우 안전한 방법이긴 하지만, 망 일부를 점유하기 위한 유지비가 막대하게 빠져나간다.
국방을 책임지면서 막대한 예산을 지원받는 군 조직 이외에 이걸 할 수 있는 규모있는 조직이 몇이나 될까?
설령 된다 치더라도, 인프라가 ISP에 종속되는 것을 과연 기업이 원할까? 잘 모르지만, 나라면 고민이 많이 들 거 같다.
애초에 이건 학생은 엄두도 못 내는 방법이잖아.
그럼 나같은 가난한 학생은 AWS console을 매번 로그인해서, 모든 IP와 port를 공개해버리는 방법말고는 없는 걸까?
2. VPN(Virtual Private Network)
📌 Concept
우리의 목적을 위해 아래 항목들을 충족해야만 한다.
- Leased Line이 아닌 Internet 망을 사용해 Private Network를 연결해야 한다.
- Internet Protocol의 보안 취약점을 해결해야 한다.
✒️ HTTPS vs VPN
처음에 잘못 이해할 뻔 한 내용이 (2)의 경우엔 HTTPS를 적용하면 해결된다고 생각했으나, 생각해보니 전혀 다른 개념이었다는 점. (우린 서비스 제공을 위함이 아니라, 운영을 위해 22 port에 접근할 건데 TLS 적용해서 뭐함..)
HTTPS는 웹 브라우와 웹 서버 간의 암호화된 통신을 제공하기 위함이지만, VPN에서 말하는 암호화는 모든 인터넷 트래픽을 암호화하여 전체 네트워크 연결을 보호해야 한다.
그런데 여기에 대해 생각보다 활발한 논의가 있었다.
NordVPN에서 자사 홍보를 하는 문구를 보면 VPN은 HTTPS보다 강력한 보안을 제공한다고 하지만, Reddit에서 오고가는 이야기를 읽어보면 일반적으로 VPN은 마케팅에서 말하는 것보다 개인 정보 보호에 그다지 도움이 되질 않는다고 한다.
재밌는 비유를 하나 봤는데, "이미 연결을 설정한 사이트에서 전송된 데이터를 보호하는 것만을 고려한다면 VPN의 이점은 거의 없다. 그러나 커피숍과 같이 신뢰하지 않는 네트워크에서 연결을 설정하는 것에 대한 이야기라면, VPN은 많은 보안 이점을 제공한다. MITM 공격, SSL 스트리핑 공격, 중간자 공격(악의적인 리디렉션) 등이 HTTPS 연결이 설정되기 전에 발생하기 때문이다."
여기서 이야기하는 것들은 ssh 연결 뿐만 아니라, 다른 프로토콜도 사용하여 VPN을 유지해야 하는 경우를 말한다.
ssh 연결은 기본적으로 암호화된 채널을 사용하므로, VPN가 암호화를 해주든 말든 딱히 관심이 없음.
가상 사설망(Virtual Private Network)은 위에서 언급한 2가지 문제를 해결하기 위해 나타난 기술 혹은 장비를 말한다.
여기서 방화벽 기능까지 더하면 통합 보안장비(UTM; Unified Threat Manage)라고 부르기도 한다고.
여튼 VPN은 공인 인터넷 상에서 IP Packet을 캡슐화함과 동시에 데이터 암호화/인증 방식을 협상하는데, 이 기술을 터널링(Tunnerling)이라 한다. (VPN 터널이 뚫렸다는 건, 이 협상이 성공적으로 끝났음을 의미)
참고로 여기서 사용되는 프로토콜은 터널링 프로토콜(Tunnerling Protocol)이라 한다.
📌 How to connect?
우선, 해당 내용은 틀렸을 수 있다.
아무리 검색해도 구체적인 설명을 찾을 수 없어 클로드한테 그림 그려서 질문하면서, Microsoft와 OpenVPN 자료 등을 참고하면서 작성했다.
인프라에 VPN Server를 구축했다는 가정 하에 진행한다.
VPN Server는 Public Subnet에 배치하는 게 맞다고 한다.
- 실행: Client가 VPN Client SW를 실행하면, VPN Client는 미리 구성된 VPN Server 주소를 확인한다.
- 연결 요청: VPN Client가 VPN Server에 연결 요청을 보낸다. (UDP or TCP)
- 서버 응답: VPN Server가 인증 정보를 확인하고 연결 요청을 수신하고 응답한다.
- 인증: VPN Client가 미리 설정된 인증 정보를 서버로 전송하고, 서버가 검증한다.
- 키 교환: 인증에 성공하면 Client와 Server가 암호화 키를 교환한다. (OpenVPN은 Diffie-Hellman 키 교환 방식 사용)
- 터널 설정: 교환된 키를 사용해 암호화된 터널 설정
- IP 할당: Server가 Client에게 virtual private ip를 할당
- Client's Routing Table 설정: 클라이언트의 Routing Table을 수정하여, VPN Network로 향하는 트래픽은 새로 설정된 터널로 라우팅되도록 설정
- Split Tunnerling: VPN 트래픽과 일반 인터넷 트래픽을 분리하여 처리하는 방식
- Full Tunnerling: 모든 트래픽을 VPN 서버를 경유하도록 처리하는 방식
- 연결 완료: Client는 VPN으로 통신이 가능하며, Server와 주기적으로 keepalive 패킷을 교환하여 연결을 유지
실제로 연결을 해보면 위와 같이 가상 사설 IP를 할당받는다.
📌 Type
VPN 접속 방식은 다양한 기준으로 나눌 수 있다.
- 접속 방식: Site to Site(LAN to LAN), Client to Site
- 터널링 프로토콜: PPTP/L2TP, IPSec, SSL 기반 VPN
- 통신 계층: L2, L3, L4, ... 기반 VPN
- 운용 주체: Core, CPE 기반 VPN
이는 관점에 따라 구분이 달라질 뿐, 이들을 어떻게 조합하여 목적에 부합하는 vpn을 구축할 지 결정할 수 있다.
예를 들어, 현재 우리처럼 재택 근무, 혹은 Cloud 업체에 서버를 구축하여 접속하고 싶은 경우
Client to Site 접속 방식을 사용하며, SSL 기반의 VPN을 구축하기 위해 L4 / L7 기반의 VPN을 구축한다고 볼 수 있다. (VPN을 어떻게 구축하냐에 따라 Layer가 달라짐. 나처럼 Docker를 사용해서 구축하면 L7 기반의 VPN)
반면, 본사와 자사를 연결할 때는 Site to Stie 방식을 사용하며, IPsec VPN을 주로 사용한다고 한다.
그리고 사용하는 계층도 거의 L3 기기들을 사용한다고 하는데, 회사마다 다를 듯?
📌 AWS VPN
당연하게도 AWS 또한 VPN 서비스를 제공한다.
VPC 서비스에서 사이드바를 조금만 내려보면 쉽게 찾을 수 있을 것이다.
심지어 연결 시간당 고작 USD 0.1..? 할만 한데 🤔
AWS에서 제공하는 VPN 서비스를 사용한다면, 그냥 손쉽게 구축이 가능하니까 다른 포스팅 찾아보는 게 이득.
하지만 우리 팀은 조금이라도 돈을 절약하기 위해서, Bastion Host에 OpenVPN을 사용해서 VPN Server를 구축했다.
📌 OpenVPN
1️⃣ VPN Server 구축
위 링크는 친절히 설명해주고 있지만, 아쉽게도 container 기반의 설치가 아니다.
Docker 기반 설치는 위에 잘 나와있다.
docker run -d \
--name=openvpn-as --cap-add=NET_ADMIN \
-p 943:943 -p 443:443 -p 1194:1194/udp \
-v <path to data>:/openvpn \
openvpn/openvpn-as
하지만 OpenVPN에서 제공하는 Golden Image를 사용하면, 상세한 설정은 직접 docker exec으로 설정하거나, 설정 파일을 mount 해줘야 하는 단점이 있다.
그게 귀찮다면, 다른 사람들이 만든 Golden Image 기반의 다른 Image를 사용하면 된다.
sudo docker run \
--name openvpn \ # 컨테이너 이름 openvpn
--volume openvpn-data:/etc/openvpn \ # OpenVPN 설정 저장할 볼륨 마운트
--detach=true \ # 백그라운드 실행
-p ${port}:${port}/udp \ # 개방할 port를 host와 container 간 매핑 (udp로 설정)
--cap-add=NET_ADMIN \ # container에 network 관리 권한 부여
-e "OVPN_SERVER_CN=${public_ip}" \ # OpenVPN 서버의 Common Name 설정
-e "OVPN_NETWORK=${VPN_NETWORK}" \ # VPN 네트워크 주소 범위 설정
-e "OVPN_ROUTES=${VPN_ROUTES}" \ # VPN을 통해 Routing할 Network 설정
-e "OVPN_NAT=true" \ # NAT 활성화
-e "OVPN_DNS_SERVERS=${PRIVATE_DNS_IP}" \ # VPN Client가 사용할 DNS 서버 설정
-e "USE_CLIENT_CERTIFICATE=true" \ # Client 인증서 사용 활성화
wheelybird/openvpn-ldap-otp:v1.8 # 사용할 docker image & tag
sudo docker exec -it openvpn show-client-config
설치가 끝나면 위 명령어로 인증 정보를 추출할 수 있는데, 이걸 연결할 클라이언트에게 전달해주면 된다.
2️⃣ Client VPN 설치 및 실행
(Windows 유저용)
(MacOS 유저용)
다른 VPN Client를 사용해도 무방하다. UI 예쁘다고 Tunnelblick 쓰는 사람도 봤다.
이 다음은 굉장히 쉬운데, (1)에서 받은 인증 정보를 VPN Client에게 import 해주고 실행하면 끝난다.
위와 같이 뜬다면 연결이 성공한 것.
3️⃣ 서버 접근
Windows라면 putty를 쓸 거고, macOS라면 아래 명령어를 사용해서 private subnet 영역의 서버에 접근이 되는 지 확인해보면 된다.
ssh -i xxx.pem ubuntu@xxx.xxx.xxx.xxx
드디어 성공..
사실 VPN은 진작에 세팅이 되어 있었으나, 개념이 궁금해서 겸사겸사 블로그도 정리해봤다.