- NAT Traversal은 NAT와 방화벽 뒤에 있는 장치끼리 UDP 패킷을 직접 교환하게 만들어, Tailscale이 WireGuard 터널을 중앙 허브 없이 연결할 수 있게 하는 기반 기술임
- 핵심 전제는 프로토콜이 UDP 기반이고, NAT 탐색용 패킷과 실제 통신 패킷을 같은 네트워크 소켓에서 주고받을 수 있어야 한다는 점임
- 상태 저장 방화벽은 먼저 나간 UDP 패킷과 맞는 응답만 허용하므로, 피어들이 서로의
ip:port를 알고 거의 동시에 패킷을 보내면 방화벽 상태를 열 수 있음 - NAT는 소스 IP와 포트를 바꾸기 때문에 STUN, 포트 매핑, NAT64 처리, 생일 역설 기반 포트 탐색, 릴레이 같은 보완 기법이 필요함
- ICE는 가능한 후보 경로를 동시에 시험해 가장 나은 경로를 고르며, Tailscale은 DERP 릴레이로 즉시 연결한 뒤 더 좋은 직접 경로가 발견되면 투명하게 전환함
NAT Traversal의 기본 조건
- 목표는 두 장치 사이에 양방향 UDP 패킷 흐름을 만들고, 그 위에서 WireGuard, QUIC, WebRTC 같은 프로토콜이 동작하게 하는 것임
- 직접 구현하려면 두 가지 조건이 중요함
- 프로토콜은 UDP 기반이어야 함
- TCP로도 가능하지만 복잡도가 높고, 구현 방식에 따라 커널 수정이 필요할 수 있음
- 스트림 지향 연결이 필요하면 UDP 위에서 동작하는 QUIC을 고려할 수 있음
- 프로그램이 패킷을 보내고 받는 네트워크 소켓을 직접 제어해야 함
- NAT Traversal은 본 프로토콜 외의 추가 패킷을 보내야 하므로 기존 네트워크 라이브러리에 단순히 덧붙이기 어려움
- NAT Traversal 로직과 본 프로토콜이 같은 소켓을 공유하며 병렬로 동작하는 구조가 유용함
- 프로토콜은 UDP 기반이어야 함
- 직접 소켓 접근이 어렵다면 로컬 프록시를 둘 수 있음
- 원래 프로토콜은 프록시와 통신함
- 프록시는 NAT Traversal과 피어로의 패킷 릴레이를 담당함
상태 저장 방화벽 관통
- 상태 저장 방화벽은 과거에 본 패킷을 기억해 새 패킷 허용 여부를 결정함
- Windows Defender firewall, Ubuntu
ufw, BSDpf, macOS의pf, AWS Security Groups 같은 형태가 있음 - 흔한 설정은 모든 outbound 연결은 허용하고 모든 inbound 연결은 차단하는 방식임
- Windows Defender firewall, Ubuntu
- UDP에서는 규칙이 단순함
- 방화벽이
2.2.2.2:1234에서5.5.5.5:5678로 나간 UDP 패킷을 봤다면, 반대로5.5.5.5:5678에서2.2.2.2:1234로 들어오는 패킷을 허용함 - 일부 느슨한 방화벽은 한 번 통신한 로컬 포트로 어디서든 들어오는 트래픽을 허용할 수 있지만 점점 드묾
- 방화벽이
- 서버와 클라이언트 구조에서는 방화벽 뒤 장치가 먼저 연결을 시작하면 되므로 문제가 작음
- VPN에서는 방화벽이 없는 허브와 방화벽 뒤 스포크가 연결되는 hub-and-spoke 구조가 됨
- 두 클라이언트가 직접 통신하려면 양쪽 방화벽이 서로를 막는 상황이 생김
- 양쪽 모두 먼저 나가야 응답을 받을 수 있지만 상대도 같은 조건에 놓임
- 사용자가 포트 개방을 직접 설정하는 방식은 불편하고, Tailscale 같은 메시 네트워크에서는 확장성이 낮음
- 공항이나 카페 라우터처럼 사용자가 제어할 수 없는 방화벽도 많음
- 해결의 핵심은 UDP 방화벽 규칙이 실제 응답 관계를 검증하지 않고 IP와 포트 조합만 본다는 점임
- 양쪽 피어가 상대의
ip:port를 미리 알고 동시에 UDP 패킷을 보내면 처음 일부 패킷은 차단될 수 있어도 방화벽 상태가 열림 - 이후 상대가 보낸 패킷은 응답처럼 보이면서 통과함
- 양쪽 피어가 상대의
- 이 방식에는 사이드 채널이 필요함
- 양쪽 끝점이 거의 동시에 통신을 시도해야 함
- 지연이 몇 초 있어도 되고 수천 바이트만 전달하면 되는 통신 경로면 충분함
- WebRTC는 시그널링 채널을 요구하고, Tailscale은 coordination server와 DERP 서버를 사이드 채널로 사용함
- 방화벽 상태는 영구적이지 않음
- UDP 세션 타임아웃의 흔한 값은 30초임
- 연결을 유지하려면 주기적으로 패킷을 보내거나, 필요할 때 out-of-band 방식으로 연결을 다시 시작해야 함
- 상태 저장 방화벽이 몇 겹 있더라도 outbound를 허용한다면 동시 전송 방식으로 통과할 수 있음
NAT가 문제를 더 어렵게 만드는 방식
- NAT(Network Address Translator)는 상태 저장 방화벽처럼 동작하면서 패킷의 IP 주소나 포트까지 바꿈
- NAT Traversal에서 문제가 되는 것은 주로 Source NAT(SNAT) 임
- SNAT는 여러 장치가 더 적은 수의 IP 주소, 흔히 하나의 공인 IPv4 주소를 공유하게 함
- DNAT도 존재하지만 여기서 다루는 NAT Traversal 문제와는 관련이 작음
- 예를 들어 노트북이
192.168.0.20:1234에서 인터넷 서버7.7.7.7:5678로 UDP 패킷을 보내면, 홈 라우터는 공인 IP의 빈 포트인2.2.2.2:4242를 고름- 라우터는
192.168.0.20:1234와2.2.2.2:4242가 같다는 NAT mapping을 만듦 - 이후 나가는 패킷은
2.2.2.2:4242에서 온 것처럼 바뀜 - 들어오는 응답은 다시
192.168.0.20:1234로 바뀜
- 라우터는
- 기업 네트워크에서도 같은 원리가 적용됨
- NAT 계층이 고가용성이나 용량 때문에 여러 장비로 구성될 수 있고, 여러 공인 IP를 가질 수 있다는 점이 다름
STUN과 NAT 매핑 발견
- NAT 뒤의 피어는 상대가 볼 수 있는 자신의 공인
ip:port를 알 수 없고, NAT 매핑은 보통 인터넷으로 나가는 트래픽이 생겨야 만들어짐 - STUN은 NAT 뒤 클라이언트가 인터넷에서 어떻게 보이는지 알아내는 프로토콜임
- 클라이언트가 STUN 서버에 “내 endpoint가 너에게 어떻게 보이냐”고 물음
- STUN 서버는 UDP 패킷이 온 공인
ip:port를 응답함
- STUN이 알려준 공인
ip:port를 피어와 공유하면, 방화벽 관통에서 사용한 동시 전송 기법을 적용할 수 있음 - NAT Traversal 로직과 실제 통신 프로토콜이 같은 소켓을 써야 하는 이유도 여기에 있음
- 소켓마다 NAT 장비에서 다른 매핑이 생김
- 실제 통신에 쓸 소켓이 아닌 다른 소켓으로 STUN을 수행하면 쓸모없는
ip:port를 얻게 됨
- STUN만으로 모든 NAT를 해결할 수는 없음
- 대부분의 홈 라우터에서는 작동할 수 있음
- 일부 기업 NAT 게이트웨이에서는 실패할 수 있음
- STUN으로 보이는
2.2.2.2:4242가 인터넷 전체에서 같은 의미를 갖는다는 가정이 항상 맞지는 않음
쉬운 NAT와 어려운 NAT
- NAT 장비는 목적지에 따라 매핑을 다르게 만들 수도 있고, 목적지와 무관하게 같은 매핑을 유지할 수도 있음
- RFC 4787은 목적지와 무관하게 매핑이 유지되는 쉬운 형태를 Endpoint-Independent Mapping(EIM) 이라고 부름
- 목적지에 따라 매핑이 달라지는 어려운 형태는 Endpoint-Dependent Mapping(EDM) 임
- 목적지 IP만 기준으로 달라질 수도 있고, 목적지 IP와 포트를 함께 기준으로 달라질 수도 있음
- NAT Traversal 관점에서는 둘 다 좋지 않음
- 과거 용어인 Full Cone, Restricted Cone, Port-Restricted Cone, Symmetric NAT는 NAT 매핑 동작과 방화벽 동작을 섞어서 표현함
- 실용적인 구현에서는 “Symmetric 대 나머지” 또는 EIM 대 EDM 구분이 더 중요함
- 동시 전송 기법은 여러 형태의 방화벽을 통과할 수 있음
- 실제 환경에서는 IP와 포트에 종속적인 방화벽이 압도적으로 많음
- 하지만 경로 어딘가에 hard NAT가 하나만 있어도 STUN과 동시 전송만으로는 문제가 생김
직접 연결 실패 시 릴레이
- 직접 연결은 모든 기법을 써도 실패할 수 있음
- NAT가 어렵거나, UC Berkeley guest Wi-Fi처럼 DNS를 제외한 outbound UDP를 차단하는 네트워크에서는 NAT 기법으로 해결할 수 없음
- 이 경우 양쪽이 접근 가능한 릴레이를 통해 패킷을 주고받을 수 있음
- 직접 연결보다 좋지는 않지만, 릴레이가 경로에 충분히 가깝고 대역폭이 충분하면 연결 품질 저하는 크지 않을 수 있음
- 지연이 늘거나 대역폭이 줄 수 있어도 연결이 전혀 없는 것보다는 나음
- 전통적인 릴레이 프로토콜은 TURN임
- 클라이언트가 TURN 서버에 인증함
- TURN 서버는 릴레이용
ip:port를 할당함 - 피어는 그
ip:port로 통신함
- Tailscale은 TURN 대신 DERP(Detoured Encrypted Routing Protocol) 를 만듦
- DERP는 HTTP 위에서 동작함
- 엄격한 outbound 규칙이 있는 네트워크에서 유용함
- 목적지의 공개 키를 기준으로 암호화된 페이로드를 릴레이함
- DERP는 두 역할을 맡음
- NAT Traversal 실패 시 데이터 릴레이
- NAT Traversal을 돕는 사이드 채널
- STUN, 동시 전송, 릴레이까지 구현하면 90% 이상은 직접 연결이 가능하고, 릴레이가 항상 어떤 형태의 연결성을 보장할 수 있다고 추정함
Hard NAT를 위한 추가 기법
- hard NAT에서 쉬운 쪽 피어는 어려운 쪽 NAT가 어떤 포트를 열었는지 모름
- STUN으로 IP는 대체로 맞다고 가정할 수 있음
- 모르는 것은 포트이며 가능한 값은 65,535개임
- 단순히 모든 포트를 훑으면 100패킷/초 기준 최악의 경우 약 10분이 걸리고, 포트 스캔처럼 보임
- 생일 역설을 이용하면 탐색 비용을 줄일 수 있음
- hard NAT 쪽에서 256개 소켓으로 256개 포트를 열고, 쉬운 NAT 쪽에서 임의의 대상 포트를 탐색함
- 256개 포트가 열려 있다고 가정하면 성공 확률은 다음과 같음
- 174개 임의 탐색: 50%
- 256개 임의 탐색: 64%
- 1024개 임의 탐색: 98%
- 2048개 임의 탐색: 99.9%
- 100포트/초라면 절반은 2초 안에 통과하고, 20초쯤이면 전체 공간의 4% 미만을 탐색하고도 거의 성공함
- 양쪽 모두 hard NAT이면 훨씬 어려움
- 이제
{source port, destination port}쌍이 맞아야 함 - 같은 조건에서 20초 뒤 성공 확률은 0.01%임
- 99.9% 성공 확률에는 양쪽이 각각 170,000개 probe를 보내야 하며, 100패킷/초 기준 28분이 걸림
- 이제
- 이 방법은 홈-오피스, 홈-클라우드, 일부 오피스-클라우드나 클라우드-클라우드 시나리오의 연결성을 개선할 수 있음
- 홈 라우터는 쉬운 NAT인 경향이 있고, hard NAT는 오피스 라우터나 클라우드 NAT 게이트웨이인 경향이 있음
포트 매핑 프로토콜
- NAT에 직접 “이 WAN 포트를 이 LAN
ip:port로 전달해 달라”고 요청하는 프로토콜들이 있음 - 대표적인 세 가지는 다음과 같음
- 로컬 기본 게이트웨이에 UPnP IGD, NAT-PMP, PCP를 시도해 응답이 있으면 공인 포트 매핑을 요청할 수 있음
- 성공하면 STUN처럼 공인
ip:port를 알 수 있을 뿐 아니라, NAT가 해당 포트에 대해 더 관대하게 동작하게 만들 수 있음 - 어디서 온 패킷이든 매핑된 포트로 도착하면 내부 장치로 전달됨
- 성공하면 STUN처럼 공인
- 이 프로토콜들에 의존할 수는 없음
- 장비에 구현되어 있지 않을 수 있음
- 기본적으로 꺼져 있을 수 있음
- 정책상 비활성화되어 있을 수 있음
- UPnP의 과거 취약점 때문에 정책상 꺼지는 경우가 있음
- 일부 장비는 하나의 “UPnP” 체크박스로 UPnP, NAT-PMP, PCP를 모두 함께 끄기도 함
- 사용할 수 있다면 데이터 경로에서 NAT 하나가 사실상 사라져 연결이 쉬워짐
Double NAT와 CGNAT
- 한 장치 앞에 NAT가 두 겹 있는 double NAT에서는 바깥쪽, 즉 인터넷 직전 NAT의 동작이 가장 중요함
- 여러 겹의 상태 저장 방화벽과 마찬가지로 추가 NAT 계층은 대체로 보이지 않음
- 기존 기법은 NAT 계층 수와 무관하게 동작할 수 있음
- double NAT가 크게 깨뜨리는 것은 포트 매핑 프로토콜임
- 포트 매핑은 클라이언트에 가장 가까운 NAT 계층에 작용함
- 하지만 원격 피어가 통과해야 하는 것은 가장 바깥쪽 NAT임
- 결과로 얻은
ip:port는 중간 네트워크의 주소라 원격 피어가 도달할 수 없음
- double NAT는 명시적 NAT Traversal을 하지 않는 대부분의 일반 애플리케이션에는 보이지 않음
- 하지만 많은 게임의 멀티플레이를 악화시킬 수 있고, IPv6를 제거해 NAT 없는 연결 선택지를 줄일 수 있음
- CGNAT(Carrier-Grade NAT) 는 ISP가 IPv4 주소 부족을 해결하기 위해 SNAT를 한 번 더 적용하는 구조임
- 홈 라우터가 장치를 중간 IP로 SNAT함
- ISP 네트워크 안의 두 번째 NAT 계층이 중간 IP들을 더 적은 공인 IP로 매핑함
- CGNAT에서는 사용자가 ISP의 NAT를 재설정할 수 없음
- 과거에는 고급 사용자가 홈 라우터의 포트 포워딩으로 문제를 피할 수 있었지만, CGNAT에서는 그 방법이 막힘
- CGNAT도 기본적으로 double NAT이므로 기존 기법 대부분은 계속 작동함
- 포트 매핑 프로토콜은 예외적으로 한계가 있음
Hairpinning 문제
- 같은 CGNAT 뒤에 있지만 서로 다른 홈 NAT 뒤에 있는 두 피어는 특별한 문제를 만남
- STUN 서버는 인터넷 바깥에서 본 공인
ip:port를 알려줌 - 하지만 두 피어가 실제로 필요한 것은 CGNAT 내부의 중간 네트워크에서 통하는
ip:port임
- STUN 서버는 인터넷 바깥에서 본 공인
- 홈 NAT 중 하나라도 포트 매핑 프로토콜을 지원하면 연결이 쉬워질 수 있음
- double NAT 때문에 포트 매핑 프로토콜이 중간 네트워크의
ip:port를 알려주는 것이 오히려 도움이 됨
- double NAT 때문에 포트 매핑 프로토콜이 중간 네트워크의
- 포트 매핑을 사용할 수 없으면 hairpinning이 필요함
- 예를 들어 peer A가 STUN으로 얻은 peer B의
2.2.2.2:5678로 패킷을 보냄 - CGNAT가 이 패킷을 바깥 인터넷으로 보내지 않고, 내부에서 peer B의 NAT 매핑으로 되돌려 보내야 함
- 예를 들어 peer A가 STUN으로 얻은 peer B의
- 많은 NAT는 hairpinning을 지원하지 않음
- 내부 네트워크에서 비내부 IP로 가는 패킷은 항상 인터넷으로 나간다고 가정하는 장비가 있음
- 이런 가정은 라우팅 실리콘에 박혀 새 하드웨어 없이는 고칠 수 없을 수도 있음
- CGNAT이 개입하면 hairpinning은 연결성에 중요해짐
- hairpinning과 포트 매핑이 모두 실패하면 릴레이를 써야 함
IPv6와 NAT64
- IPv6만 있는 세계라면 NAT 문제는 훨씬 단순해짐
- 모든 장치가 NAT 없이 도달 가능한 주소를 가질 수 있음
- 하지만 상태 저장 방화벽은 여전히 남으므로 방화벽 관통과 사이드 채널은 필요함
- outbound UDP를 막는 네트워크를 위해 HTTP 같은 프로토콜을 쓰는 fallback relay도 여전히 유용함
- IPv6만으로는 아직 충분하지 않음
- 세계는 대부분 IPv4이고 약 33% IPv6임
- IPv6 배포는 균일하지 않아 피어 조합에 따라 100% IPv6일 수도, 0% IPv6일 수도 있음
- 무조건 연결을 목표로 하면 IPv4+NAT 처리를 계속 해야 함
- IPv6와 IPv4 공존은 NAT64라는 추가 사례를 만듦
- NAT44는 IPv4를 다른 IPv4로 변환함
- NAT64는 내부 IPv6를 외부 IPv4로 변환함
- DNS64와 함께 쓰면 단말에는 IPv6-only 네트워크처럼 보이면서 IPv4 인터넷 접근을 제공함
- DNS 이름만 쓰는 애플리케이션은 NAT64를 거의 의식하지 않아도 됨
- 하지만 NAT Traversal은 구체적인 IP와 포트를 직접 다루므로 별도 처리가 필요함
- 장치가 CLAT(Customer-side translator) 를 지원하면 운영체제가 직접 IPv4 연결이 있는 것처럼 보이게 해 NAT64를 뒤에서 처리함
- CLAT는 모바일 장치에는 흔함
- 데스크톱, 노트북, 서버에는 드묾
- CLAT가 없으면 NAT64+DNS64를 직접 감지해야 함
ipv4only.arpa.에 DNS 요청을 보냄- 이 이름은 알려진 고정 IPv4 주소로만 해석됨
- IPv6 주소가 돌아오면 DNS64가 변환한 것이므로 NAT64 prefix를 알아낼 수 있음
- 이후 IPv4 주소와 통신하려면
{NAT64 prefix + IPv4 address}로 IPv6 패킷을 보내면 됨- NAT64를 통해 STUN을 수행해 공인
ip:port를 찾으면 다시 일반 NAT Traversal 문제로 돌아감
- NAT64를 통해 STUN을 수행해 공인
ICE로 후보 경로 통합하기
- 모든 기법 중 무엇을 써야 할지 미리 정확히 분류하는 방식은 확장성이 낮음
- 네트워크 엔지니어와 NAT 장비 구현자는 다양한 동작을 만들기 때문임
- ICE(Interactive Connectivity Establishment) 의 핵심은 가능한 모든 것을 동시에 시도하고, 작동하는 것 중 가장 좋은 경로를 고르는 알고리듬임
- 통신 시작 시 로컬 소켓에 대한 후보 endpoint 목록을 모음
- IPv6
ip:ports - IPv4 LAN
ip:ports - STUN으로 발견한 IPv4 WAN
ip:ports - NAT64 translator를 통해 발견한 IPv4 WAN
ip:ports - 포트 매핑 프로토콜로 할당받은 IPv4 WAN
ip:port - 정적으로 설정된 포트 포워딩 같은 운영자 제공 endpoint
- IPv6
- 이후 사이드 채널을 통해 후보 목록을 교환하고, 상대가 준 모든 endpoint에 probe 패킷을 보냄
- probe 패킷은 방화벽과 NAT를 열기 위한 패킷 역할을 함
- 동시에 ping/pong 형태의 상태 확인 역할도 함
- 일정 시간이 지난 뒤 작동이 확인된 후보 경로 중 휴리스틱상 가장 좋은 경로를 선택함
- ICE는 보통 LAN > WAN > WAN+NAT 같은 사전 점수를 사용함
- Tailscale은 v0.100.0부터 하드코딩된 선호 순서 대신 왕복 지연 시간을 사용함
- Tailscale은 연결을 엄격한 probe 단계와 통신 단계로 나누지 않음
- 모든 연결은 DERP가 미리 선택된 상태로 시작함
- 사용자는 fallback 경로로 즉시 연결을 쓸 수 있음
- 경로 탐색은 병렬로 돌고, 몇 초 뒤 더 나은 경로가 발견되면 투명하게 업그레이드됨
운영 중 경로 유지와 보안
- 비대칭 경로에 주의해야 함
- ICE는 양쪽 피어가 같은 네트워크 경로를 골라 양방향 패킷 흐름이 유지되도록 노력함
- 같은 수준의 절차를 구현하지 않더라도 사용 중인 모든 경로에 양방향 트래픽이 있어야 함
- 주기적인 ping/pong probe만으로도 이를 유지할 수 있음
- 현재 선택된 경로가 실패할 수도 있음
- NAT 유지보수로 상태가 사라지는 경우가 예시임
- 가능한 모든 경로를 계속 probe해 warm fallback을 유지할 수 있음
- 하지만 downgrade는 드물기 때문에 마지막 수단 릴레이로 떨어진 뒤 경로 탐색을 다시 시작하는 편이 더 효율적일 수 있음
- 상위 프로토콜이 자체 보안을 제공한다는 전제가 중요함
- QUIC은 TLS 인증서를 사용함
- WireGuard는 자체 공개 키를 사용함
- 동적으로 경로를 전환하면 IP 기반 보안은 의미가 없어짐
- 최소한 end-to-end 인증이 필요함
- 상위 계층에 end-to-end 보안이 있으면 ping/pong probe가 스푸핑 가능해도 최악의 경우 공격자가 트래픽을 자신을 경유하도록 유도하는 정도임
- 그래도 경로 탐색 패킷도 인증하고 암호화하는 편이 좋음
견고한 NAT Traversal 구성 요소
- 견고한 NAT Traversal에는 다음 요소가 필요함
- UDP 기반으로 확장할 프로토콜
- 프로그램 안에서 직접 접근 가능한 소켓
- 피어와 통신할 사이드 채널
- STUN 서버 몇 개
- 선택 사항이지만 강력히 권장되는 fallback relay 네트워크
- 수행 단계는 다음과 같음
- 직접 연결된 인터페이스에서 소켓의 모든
ip:ports를 열거함 - STUN 서버에 질의해 WAN
ip:ports와 NAT 난이도를 찾음 - 포트 매핑 프로토콜로 추가 WAN
ip:ports를 찾음 - NAT64가 있으면 이를 감지하고 그 경로로도 WAN
ip:port를 찾음 - 사이드 채널로 모든
ip:ports와 암호화 키를 피어와 교환함 - 빠른 연결 수립을 위해 fallback relay로 먼저 통신할 수 있음
- 상대의 모든
ip:ports를 probe하고, 필요하면 hard NAT를 통과하기 위해 생일 역설 기반 탐색을 수행함 - 현재 경로보다 나은 연결 경로를 찾으면 투명하게 업그레이드함
- 활성 경로가 멈추면 필요에 따라 downgrade해 연결성을 유지함
- 모든 통신은 end-to-end로 암호화되고 인증되어야 함
- 직접 연결된 인터페이스에서 소켓의 모든