OpenVPN TLS 핸드셰이크 실패 원인과 구조 이해하기
OpenVPN이 TLS를 쓰는 방식, 웹브라우저랑 뭐가 달라요?
OpenVPN은 “TLS(전송 계층 보안)”를 이용해서 서로가 진짜 클라이언트/서버가 맞는지 확인하고(인증), 그다음에 터널에서 쓸 암호화 키를 안전하게 합의하거든요. 그런데 여기서 많은 분들이 “그럼 HTTPS랑 똑같이 TLS 하는 거네요?”라고 생각하시는데, 결이 좀 달라요.
제가 2023년에 베트남(호치민) 쪽 출장자 VPN 장애를 원격으로 같이 잡아본 적이 있는데, 같은 설정인데도 호텔 와이파이에서는 붙고 공항 와이파이에서는 TLS handshake에서만 10번 중 9번이 터지더라고요.
OpenVPN 포럼 자료에 따르면 OpenVPN의 TLS 핸드셰이크는 웹브라우저가 하는 일반적인 TLS처럼 보이지 않을 수 있고, OpenVPN 캡슐화(자기 프로토콜 포장) 안에서 진행되는 형태라서 네트워크 장비가 “이거 OpenVPN이네?” 하고 잡아내기도 합니다(https://forums.openvpn.net/viewtopic.php?t=34905).
즉, TLS를 쓰긴 쓰는데 “어디에, 어떻게 담아서” 쓰느냐가 구조적 포인트예요.
실무에서 이 포인트를 간과하면 인증서가 멀쩡해도 “길목에서 생긴 문제”를 전부 인증서 탓으로 오해해서 삽질이 길어지기 딱 좋거든요.
구조의 핵심: 제어 채널(TLS)과 데이터 채널(터널 데이터)이 따로 논다
OpenVPN을 집에 비유하면 이해가 쉬워요.
제어 채널(control channel): “초인종 누르고 신분증 확인하는 단계”
여기서 TLS 핸드셰이크가 일어나고, 인증서로 서로를 확인하고, 세션 키를 협상해요.
데이터 채널(data channel): “집 안으로 들어와서 실제로 대화/물건 전달하는 단계”
여기서는 이미 합의된 키로 사용자 트래픽(인터넷 접속, 사내망 접속 등)을 암호화해서 실어 나릅니다.
OpenVPN 커뮤니티 문서에 따르면 TLS 모드에서는 SSL/TLS + 인증서 기반으로 양방향 인증과 키 교환을 수행하고, 이후 실제 터널 데이터는 별도 암호화된 패킷 형태로 흐릅니다(https://openvpn.net/community-docs/openvpn-cryptographic-layer.html).
그래서 “핸드셰이크(입장 심사)”가 실패하면, 데이터 채널은 아예 열리지도 못해요.
왜 굳이 둘을 나눠요?
현실적인 이유가 있어요.
제어 채널은 신뢰성 있게(중간에 빠지면 안 됨) 교섭해야 하고
데이터 채널은 빠르고 유연하게(UDP 같은) 보내는 게 유리하거든요
그래서 OpenVPN은 “TLS는 신뢰성 있게 굴리고, 실제 데이터는 가볍게”라는 운영 철학이 깔려 있어요.
UDP 위에서 TLS를 굴리는 방식: “겉은 UDP, 속은 TLS”
여기서 OpenVPN의 재미있는(그리고 트러블을 부르는) 포인트가 나와요. OpenVPN은 UDP를 많이 쓰는데, UDP는 원래 택배로 치면 “놓고 갑니다” 스타일이라 누락/순서보장이 없잖아요. 그런데 TLS는 원래 TCP처럼 신뢰성 있는 전송을 기대하는 경우가 많고요.
OpenVPN 문서에 따르면 OpenVPN은 UDP 위에 자체 신뢰성 계층을 제공해서, TLS 같은 절차가 안정적으로 진행되도록 도와주고, 실제 터널 데이터는 UDP로 흘려보내는 구조를 취합니다(https://openvpn.net/community-docs/openvpn-cryptographic-layer.html).
한마디로 “UDP 위에 TLS가 잘 굴러가게 받쳐주는 받침대”를 OpenVPN이 깔아둔 거예요.
이 구조가 실전에서 만드는 현상: 포트/프로토콜 미스매치
“TLS handshake failed”의 흔한 원인 중 하나가, 서버는 UDP로 듣고 있는데 중간 장비(NAT/포워딩/방화벽)는 TCP로 열어놨다든지, 반대로 해놨다든지 하는 거예요.
Server Fault 사례에 따르면 UDP로 구성했는데 게이트웨이 포워딩을 TCP로 해둬서 핸드셰이크가 실패했고, UDP로 바꾸니 정상 동작했다고 합니다(https://serverfault.com/questions/709860/fix-tls-error-tls-handshake-failed-on-openvpn-client).
겉으로는 “TLS가 실패했다”지만, 실제 원인은 “길 안내(포트/프로토콜)가 틀렸다”인 경우가 꽤 있죠.
이런 식의 “핸드셰이크는 암호 문제가 아니라 통신 경로 문제일 수도 있다”는 관점은 NIST에서 말하는 TLS 구성/운영 가이드(예: SP 800-52 계열)에서도 반복해서 강조되는 부분이기도 해요.
“TLS handshake failed”가 특히 잘 나는 환경: 막는 쪽이 똑똑할 때
이 에러 메시지는 진짜 범인이 여러 명이라서, 한 방에 단정하면 위험해요(환경마다 다를 수 있어요). 다만 “구조적으로 왜 이런 일이 생기기 쉬운지”는 설명할 수 있습니다.
OpenVPN 포럼 자료에 따르면 특정 국가/네트워크에서 OpenVPN 접속 시 TLS 핸드셰이크 단계의 패킷이 필터링되거나 OpenVPN 프로토콜 자체가 차단되는 정황이 있고, SSH 포트포워딩으로 우회하면 트래픽이 SSH처럼 보여서 붙는 경우가 있습니다(https://forums.openvpn.net/viewtopic.php?t=34905).
즉, OpenVPN의 TLS는 “웹브라우저의 HTTPS처럼 평범하게 보이는 TLS”가 아니라, OpenVPN 특유의 형태로 보여서 차단/식별의 타깃이 될 수 있다는 얘기예요.
왜 SSH 터널로 감싸면 될 때가 있어요?
SSH로 포트포워딩을 하면, 바깥에서 보이는 건 “SSH 세션 하나”로 보이거든요. 그 안에 OpenVPN이 들어가 있으면, 중간 검문소 입장에서는 “아, 이건 SSH 통신이네” 하고 통과시키는 그림이 나옵니다. 물론 이것도 네트워크 정책/탐지 수준에 따라 다르고요.
보안적으로 중요한 옵션: tls-auth가 하는 일(핸드셰이크에 자물쇠 하나 더)
OpenVPN은 TLS 핸드셰이크 자체를 더 단단하게 만들 수 있는 옵션이 있어요. 대표적으로 --tls-auth(또는 최신 구성에서 tls-crypt 계열을 함께 언급하곤 하죠) 같은 것들입니다.
OpenVPN 문서에 따르면 --tls-auth 옵션은 TLS 핸드셰이크 패킷에 HMAC 인증을 추가해서 보안을 강화합니다(https://openvpn.net/community-docs/openvpn-cryptographic-layer.html).
비유하면, 초인종 누르는 사람에게 “우리만 아는 도장(HMAC)”이 찍혀 있어야 문을 열어주는 거예요. 도장 없는 사람은 아예 대화도 시작 안 하니까, 스캔/방해 트래픽을 줄이는 데 도움이 됩니다.
흔한 오해 3가지: “TLS 쓰면 다 HTTPS처럼 되는 거 아닌가요?”
OpenVPN 포럼 자료에 따르면 웹사이트용 공인 인증서를 OpenVPN에 그대로 쓰는 건 불가능하다고 언급됩니다(https://forums.openvpn.net/viewtopic.php?t=34905). 이 지점에서 오해가 많이 생겨요.
1) “443 포트 쓰면 HTTPS로 보이니까 무조건 안 막힌다”
포트는 “문 번호”일 뿐이고, 안에서 오가는 대화 내용(프로토콜 특징)이 다르면 식별될 수 있어요. 443/UDP 같은 조합도 가능은 하지만, 네트워크가 무엇을 어떻게 필터링하는지에 따라 결과가 달라집니다.
2) “TLS handshake failed면 무조건 인증서 문제다”
인증서/시간 동기화 문제일 수도 있지만, 포트포워딩 프로토콜 불일치(UDP/TCP), 방화벽, ISP/국가 단위 필터링 등도 충분히 원인이 됩니다. 에러 문구는 “핸드셰이크가 끝까지 못 갔다”는 결과만 말해주는 경우가 많거든요.
3) “VPN은 프록시랑 비슷하죠?”
VPN은 보통 “전체 트래픽을 터널로” 보내는 쪽에 가깝고, 프록시/SSH 터널은 범위가 더 제한적일 때가 많아요. ITMAYA 자료에서도 VPN이 공용 인터넷에서 사설망처럼 동작하게 하며 전체 트래픽을 암호화한다는 점을 강조합니다(https://itmaya.co.kr/wboard/view.php?wb=tech&idx=85).
OpenVPN에서 TLS는 그 터널을 여는 “신분 확인 + 키 교환” 역할이라고 보면 딱 맞습니다.
핵심 정리: OpenVPN의 TLS는 “웹 TLS”가 아니라 “터널을 여는 TLS”예요
다만, 이 글에서 말한 내용은 OpenVPN의 전형적인 TLS 기반 동작 구조를 설명한 거라서, 버전/옵션(tls-crypt, DCO 등)이나 네트워크 정책에 따라 체감 증상은 케이스마다 다를 수 있습니다.
OpenVPN은 TLS로 양방향 인증(인증서)과 키 교환을 하고, 그 다음 데이터 채널로 실제 트래픽을 암호화해요(제어 채널과 데이터 채널 분리).
UDP 위에서 TLS가 굴러가도록 OpenVPN이 자체 신뢰성 계층을 얹는 구조라서, 포트/프로토콜 설정이 어긋나면 핸드셰이크 실패로 바로 터져요.
어떤 네트워크에서는 OpenVPN 특유의 핸드셰이크/패킷 형태가 식별돼 차단될 수 있고, SSH 포워딩처럼 “겉모습을 바꾸면” 통과하는 경우도 있습니다(환경마다 다름).
tls-auth 같은 옵션은 핸드셰이크 단계에 “도장(HMAC)”을 추가해 불필요한 접근/교란을 줄이는 데 도움을 줍니다.
원하시면, “OpenVPN 로그에서 TLS handshake failed를 봤을 때 어디부터 체크하면 좋은지”를 체크리스트로도 정리해드릴게요(UDP/TCP, 포워딩, 시간, 인증서 체인, tls-auth 키 불일치 같은 것들요).