SSH와 Nginx로 셀프호스팅 HTTP 터널 구축
원제: Self-hosted HTTP tunnels with SSH and Nginx
왜 중요한가
상용 터널 서비스 의존 없이 표준 SSH·Nginx만으로 안전한 터널을 구성하는 실용적 레퍼런스로, 셀프호스팅 수요에 직접 적용 가능하다.
개인 서버에서 OpenSSH와 Nginx만으로 HTTP 터널을 구현하는 방법이 공개됐다. `ssh -R 0` 옵션으로 원격 서버에 동적 포트를 할당하고, Nginx의 와일드카드 서버명 설정과 `ngx_http_secure_link_module`을 조합해 접근 제어까지 구현한다. ngrok 같은 상용 서비스 없이 로컬 서비스를 외부에 안전하게 공유할 수 있다.
로컬에서만 실행 중인 서비스를 외부에 공유해야 할 때 ngrok, Cloudflare Quick Tunnels 같은 상용 서비스나 frp, localtunnel 같은 전용 클라이언트가 필요한 도구를 쓰는 것이 일반적이다. 이 글은 추가 소프트웨어 없이 표준 OpenSSH와 Nginx만으로 동일한 기능을 구현하는 방법을 설명한다.
핵심은 `ssh -N -R 0:localhost:8080 web02.luffy.cx` 명령이다. 원격 포트에 `0`을 지정하면 서버가 빈 포트(예: 41535)를 자동 할당한다. 이후 Nginx에서 `~^p(?<port>\d{5})\.ssh\.luffy\.cx$` 패턴으로 서브도메인을 파싱해 해당 포트로 역방향 프록시를 설정한다. `*.ssh.luffy.cx`에 대한 DNS CNAME 레코드와 Let's Encrypt 와일드카드 인증서도 필요하다.
보안 측면에서 할당 포트 번호만을 비밀로 삼는 방식은 엔트로피가 약 14.8비트에 불과해 취약하다. 이를 보완하기 위해 Nginx의 `ngx_http_secure_link_module`을 활용한다. 포트 번호, 만료 타임스탬프, 서버 측 비밀키를 조합한 MD5 해시를 생성하고, 이를 URL의 사용자명 자리에 넣어 HTTP Basic 인증으로 전달하는 방식이다. 예를 들면 `https://6J3jK1WmB15c6WmjW_X-Wg--1789928654@p41535.ssh.luffy.cx/` 형태다. 만료 타임스탬프를 포함하는 이유는 같은 포트가 이후 세션에 재사용될 수 있기 때문이다. URL에 콤마 대신 더블 대시를 구분자로 쓴 이유도 일부 앱이 콤마 포함 URL을 제대로 인식하지 못하기 때문이다.