How to 목록

// blog post

UDP 포트 하나로 SSH도 HTTP도 다 되는 이유 — Tailscale과 WireGuard 터널링 원리

|
tailscalewireguardnetworkingvpn

포트 하나만 열었는데 다 됩니다

안녕하세요, 이프입니다.

지난 글에서 pf 방화벽으로 en0 인바운드를 전부 막고 UDP 41641만 열어뒀습니다. TCP는 전면 차단이요.

그런데 Tailscale로 접속하면 SSH(TCP 22)도 되고, 개발 서버(TCP 3000)도 되고, SFTP도 됩니다. TCP를 다 막았는데 TCP 기반 서비스가 돌아간다? 처음엔 방화벽 규칙이 잘못된 줄 알고 세 번이나 다시 확인했습니다.

결론부터 말하면, 방화벽은 정상이고 WireGuard 터널링이 그렇게 동작하는 겁니다.

택배 상자 비유

가장 직관적인 비유는 택배입니다.

UDP 41641 포트는 택배 상자입니다. 상자 겉면에는 "UDP 41641번으로 배송"이라고만 적혀 있습니다. 방화벽은 이 겉면만 봅니다. "UDP 41641이니까 통과."

상자 안에는 뭐가 들었을까요? TCP 패킷이 들어 있을 수도 있고, UDP 패킷이 들어 있을 수도 있고, ICMP ping이 들어 있을 수도 있습니다. 방화벽은 상자를 열어보지 않습니다. 겉면(UDP 41641)만 보고 판단합니다.

이게 캡슐화, encapsulation의 핵심입니다.

WireGuard가 실제로 하는 일

WireGuard는 완전한 IP 패킷을 통째로 UDP 패킷 안에 넣습니다.

예를 들어 VibeShell에서 Mac mini에 SSH 접속을 하면:

  1. 스마트폰의 Tailscale이 TCP SYN 패킷을 만듭니다 (목적지: 100.64.0.1:22)
  2. 이 TCP 패킷 전체를 WireGuard가 암호화합니다
  3. 암호화된 데이터를 UDP 패킷으로 감쌉니다 (목적지: Mac mini 공인IP:41641)
  4. en0에서는 UDP 41641 패킷만 보입니다
  5. Mac mini의 WireGuard가 UDP를 벗기고 복호화합니다
  6. 원래의 TCP SYN 패킷이 utun 인터페이스에 나타납니다
  7. SSH 서버가 이 패킷을 받아 응답합니다

en0 입장에서는 UDP 41641 트래픽만 오고 간 겁니다. TCP 22번 같은 건 한 번도 나타나지 않습니다. pf 방화벽이 TCP를 전부 차단해도 아무 문제가 없는 이유입니다.

en0과 utun의 차이

이 구조를 이해하면 두 인터페이스의 역할이 명확해집니다.

en0 (물리 인터페이스, 공인 IP)

  • 외부 인터넷과 직접 통신
  • pf 방화벽 규칙 적용 대상
  • 보이는 트래픽: UDP 41641뿐
  • TCP 인바운드 전부 차단 상태

utun (Tailscale 가상 인터페이스, 100.x.x.x)

  • WireGuard가 복호화한 패킷이 나타나는 곳
  • 모든 포트, 모든 프로토콜 사용 가능
  • SSH, HTTP, SFTP, ping 전부 여기서 동작
  • 접근 제어는 Tailscale ACL에서 처리

en0은 "택배 수령 창구"이고, utun은 "택배를 풀어서 실제로 쓰는 내부 공간"인 셈입니다.

왜 TCP가 아니라 UDP인가

VPN 터널을 만들 때 TCP 대신 UDP를 쓰는 데는 이유가 있습니다.

SSH는 TCP 기반입니다. 만약 이 TCP 패킷을 또 다른 TCP로 감싸면 어떻게 될까요?

바깥쪽 TCP에서 패킷이 유실되면 재전송합니다. 안쪽 TCP도 같은 유실을 감지하고 재전송합니다. 바깥쪽 재전송이 지연을 만들고, 안쪽 TCP는 그 지연을 "네트워크가 혼잡하다"로 판단해서 전송 속도를 줄입니다. 바깥쪽도 마찬가지로 속도를 줄입니다. 두 TCP의 혼잡 제어가 서로를 악화시키면서 성능이 급격히 떨어집니다.

이걸 TCP meltdown 또는 TCP-over-TCP 문제라고 합니다.

UDP는 혼잡 제어를 하지 않습니다. 패킷이 유실되면 그냥 버립니다. 재전송은 안쪽 TCP가 알아서 합니다. 바깥쪽 전송 계층이 안쪽과 간섭하지 않으니 성능이 안정적입니다.

그래서 WireGuard, OpenVPN, Tailscale 모두 기본 전송 프로토콜로 UDP를 씁니다. "터널 안의 TCP가 혼잡 제어를 책임지고, 터널 자체는 가능한 한 투명하게" 동작하는 구조입니다.

실제로 확인해보기

Tailscale이 직접 연결(direct)을 맺고 있는지 확인하려면:

tailscale status

출력에서 direct 표시가 있으면 UDP 41641을 통한 직접 P2P 연결입니다. relay로 나오면 DERP 릴레이 서버를 거치고 있다는 뜻이고, UDP 41641이 막혀 있을 가능성이 높습니다.

# 더 상세한 연결 정보
tailscale netcheck

netcheck을 돌리면 UDP 포트 가용 여부, 지연 시간, 사용 중인 DERP 서버까지 확인할 수 있습니다.

정리

정리하면 이렇습니다:

  • WireGuard는 IP 패킷을 통째로 UDP 안에 캡슐화합니다
  • en0에서는 UDP 41641만 오가고, 실제 서비스 트래픽은 utun에서 동작합니다
  • TCP를 TCP로 감싸면 TCP meltdown이 발생하므로, VPN은 UDP를 씁니다
  • pf에서 TCP 전부 차단 + UDP 41641만 허용해도 Tailscale을 통한 모든 서비스가 정상 동작합니다

지난 글에서 "왜 UDP 41641을 열어두나요" 부분을 짧게 넘겼는데, 그 안에 이만큼의 내용이 있었습니다. 네트워크 쪽을 깊이 파고들수록 "왜 이렇게 설계했지?"에 대한 답이 명쾌해서 재밌더라고요.