Lobsters

Self-Hosting Behind CGNAT

CGNAT 뒤에서 셀프 호스팅하기

IPv4 부족으로 통신사 CGNAT 뒤에 놓인 홈랩을 프랑스 VPS와 WireGuard로 인터넷에 공개하는 구성입니다. VPS에서 포트 포워딩과 라우팅을 처리하고, 홈랩은 터널을 먼저 연결해 공인 IP 없이 서비스를 운영합니다.

AI 요약

과거에는 공유기에서 포트를 열고 홈 서버로 포워딩하면 셀프 호스팅을 시작할 수 있었습니다. 하지만 IPv4 주소 부족으로 통신사가 여러 가입자에게 하나의 공인 주소를 공유하는 CGNAT(Carrier-Grade NAT)을 사용하면서 상황이 달라졌습니다. 집의 공유기가 가진 주소는 통신사 내부 주소이고, 외부에서 보이는 공인 주소는 통신사 장비에 할당됩니다. 따라서 집의 공유기에서 포트를 열어도 외부 요청이 홈 서버까지 도달하지 않습니다.

VPS를 브리지로 사용하는 구성

작성자는 스페인 북부에 있는 어머니 집 지하의 중급 컴퓨터에서 서비스를 운영하고, 프랑스 데이터센터의 저가 VPS를 인터넷과 홈랩 사이의 브리지로 사용합니다. 공인 인터넷에서 들어온 요청은 VPS로 도착합니다. VPS와 홈랩은 WireGuard 터널로 연결되어 있습니다. 홈랩이 터널 연결을 먼저 시작하므로 집에 고정 공인 IP를 신청하지 않아도 됩니다. 스페인에서 ISP의 고정 IP를 신청하는 대안도 있지만 월 20유로 정도가 듭니다. VPS를 거치는 대가로 왕복 지연 시간은 39ms 늘어납니다.

WireGuard는 VPS의 wg0 인터페이스에서 10.0.0.1을 사용하고, 홈랩은 10.0.0.2를 사용합니다. VPS의 피어 설정에는 홈랩 주소인 10.0.0.2/32를 넣습니다. 홈랩 설정의 Endpoint는 213.32.19.229:51820이며 PersistentKeepalive는 25초입니다. 이 값으로 CGNAT 안쪽에 있는 홈랩이 터널을 계속 유지합니다.

VPS의 NAT와 포워딩

VPS에서는 ens3로 들어온 외부 트래픽을 먼저 처리합니다. UDP 51820은 WireGuard가 직접 사용하므로 NAT 대상에서 제외합니다. TCP 2222도 제외해 VPS 자체의 SSH 접속에 남겨 둡니다. 나머지 트래픽은 목적지를 10.0.0.2로 바꾸는 DNAT 규칙을 적용합니다. 이어서 ens3와 wg0 사이의 포워딩을 허용합니다.

구성에서 목적지 주소만 바꾸고 출발지 주소는 그대로 둡니다. 따라서 홈랩 서비스는 실제 클라이언트 IP를 확인합니다. 외부에서 ssh.alvarezrosa.com의 22번 포트에 접속하면 홈랩의 SSH로 연결되고, VPS의 2222번 포트는 브리지 자체의 SSH로 남습니다. VPS의 WireGuard 설정에는 NAT와 포워딩 규칙을 올리고 내리는 PostUp, PostDown 명령을 둡니다.

홈랩의 응답 경로

홈랩에서는 Table = off로 WireGuard가 시스템의 기본 라우팅 테이블을 자동으로 바꾸지 않게 합니다. 대신 PostUp에서 별도 라우팅 테이블 200에 wg0을 통한 기본 경로를 추가하고, 10.0.0.2에서 출발한 패킷에 이 테이블을 적용하는 정책을 등록합니다. 외부 클라이언트에게 보낼 응답은 다시 WireGuard 터널을 거쳐 VPS로 돌아갑니다. 동시에 홈랩 자체의 일반 트래픽은 기존 홈 공유기를 통해 나갑니다. 터널이 홈랩의 모든 일반 네트워크 트래픽을 가로채지 않도록 출발지 기반 정책 라우팅을 사용한 구성입니다.

장애 대응

작성자는 세 지점을 장애 대상으로 봅니다. 홈랩에서는 cron 작업이 SSH 접속 가능 여부를 확인하고, 접속되지 않으면 장비를 재부팅합니다. 브리지가 중단될 때를 대비해 Cloudflare Tunnel이나 Tailscale로 홈랩에 직접 들어가는 보조 진입 경로를 권장합니다. WireGuard 터널이 짧게 끊기면 자동으로 핸드셰이크를 다시 수행합니다. 장시간 끊긴 경우에는 홈랩 재부팅이나 보조 진입 경로로 복구합니다.

커뮤니티에서 제기한 보안 문제

Lobsters에서는 VPS와 홈 네트워크를 완전히 연결하는 방식의 범위가 넓다는 우려가 나왔습니다. 브리지 VPS의 설정이 잘못되면 홈 네트워크 전체로 접근 범위가 커질 수 있기 때문입니다. 한 댓글 작성자는 autossh로 특정 로컬 IP와 포트만 VPS에 포워딩하고, 제한된 SSH 키를 사용했다고 설명합니다. 필요한 트래픽만 전달하는 Docker 컨테이너를 띄우는 방식도 제안했지만, 연결 방식에 따라 모든 웹 애플리케이션이 잘 동작하지는 않을 수 있다고 덧붙였습니다.

답글에서는 방어 계층을 나누는 방법을 제시합니다. 홈 피어를 네트워크 DMZ에 두고, 가능하면 포워딩 전용 장비로 사용합니다. VPS도 브리지 역할만 맡기고 다른 서비스는 실행하지 않습니다. OpenBSD의 pf, WireGuard, unbound를 활용하는 방법도 언급했습니다. 웹 앱만 공개한다면 홈 피어에서 브리지 IP의 TCP 443만 허용하고, 라우터에서 WireGuard를 실행할 때도 필요한 포워딩만 열어야 합니다. 시스템 업데이트도 계속 유지해야 합니다. 별도 댓글에서는 이 구성에 MTU를 더 낮추는 조정이 필요한 경우가 있다고 지적했습니다.

Lobsters 반응

  • @creesch — 흥미롭게도 저도 이런 구성을 생각해 왔습니다. 하지만 브리지와 홈 네트워크를 완전히 연결하는 방식은 마음이 편하지 않습니다. 물리 네트워크의 서버를 인터넷에 노출하는 것과 크게 다르지 않다고 봅니다. 그래도 브리지에서 무언가 잘못 구성되면 누군가 전체 네트워크에 접근할 가능성이 생깁니다. 최근에는 특정 로컬 IP와 포트만 브리지 VPS로 autossh 포트 포워딩하는 방식을 시험했습니다. 사용한 SSH 키에도 제한을 걸었습니다. 필요한 트래픽만 전달하는 컨테이너를 Docker 설정과 결합해 띄우려 했습니다. 사용한 애플리케이션에서는 잘 동작했지만, 연결 방식 때문에 모든 웹 애플리케이션이 잘 작동하지는 않을 듯합니다.
    • @ggpsv — 브리지와 홈 네트워크를 완전히 연결하는 방식이 마음에 들지 않는다면, 심층 방어를 적용해 위험을 줄일 수 있습니다. 홈 피어를 네트워크 DMZ에 두고 그 장비를 해당 용도로만 사용합니다. 구획을 나눠 VPS도 브리지 전용으로 운영합니다. 기본 도구인 pf, wg, 그리고 WireGuard 네트워크 안에서 사용자 지정 도메인을 위한 DNS를 쓴다면 unbound를 쉽게 사용할 수 있는 OpenBSD도 고려할 만합니다. 필요한 범위만 허용하는 것도 좋습니다. 웹 앱에 접근할 뿐이라면 홈 피어에서 브리지 IP의 TCP 443만 받아들이게 합니다. 라우터에서 WireGuard를 실행한다면 필요한 포워딩만 엽니다. 모든 시스템을 최신 상태로 유지합니다. 예전에 셀프 호스팅을 위한 WireGuard 토폴로지에 관한 글도 썼습니다.
  • @fazalmajid — 보통 이 구성이 작동하려면 MTU도 더 낮게 조정해야 합니다.
  • @Foxboron — 현재 집에서 로컬 hackerspace의 VPS를 사용해 같은 구성을 운영합니다. mjg가 같은 주제로 쓴 글도 있습니다. https://mjg59.dreamwidth.org/72095.html
  • @tobz619 — Oracle free forever VPS로 비슷한 구성을 운영합니다. 그래도 아직 Tailscale에서 벗어날 만큼 자신은 없습니다 :(
  • @baetylboy — 관련 없는 이야기지만, 글 시작 부분의 이니셜이 마음에 듭니다.
  • @singpolyma — 어차피 VPS 비용을 내야 한다면 서비스를 VPS에서 실행하면 되지 않습니까?

원문: David Alvarez Rosa 블로그 / 번역·요약: Trawling