SSH 포트 포워딩 — -L, -R, -D 완전 정복

SSH 로컬(-L)·원격(-R)·동적(-D) 포트 포워딩의 원리와 사용법, ~/.ssh/config 영구 설정, 실전 활용 패턴을 설명합니다.

· 6 min read · PALDYN Team

지난 글에서 네트워크 네임스페이스로 격리된 네트워크 환경을 구성하는 방법을 살펴봤습니다. 이번에는 SSH의 강력한 기능인 포트 포워딩(포트 터널링)을 다룹니다. SSH 포트 포워딩은 암호화된 SSH 채널을 통해 다른 포트의 트래픽을 전달하는 기술로, VPN 없이 방화벽 우회, NAT 뒤 서버 접근, 프록시 구성 등에 활용됩니다.

세 가지 포워딩 방식

SSH 포트 포워딩은 트래픽 방향과 방식에 따라 세 가지로 나뉩니다.

SSH 포트 포워딩 유형

세 방식 모두 SSH 연결이 살아있는 동안만 터널이 유지됩니다. -N 플래그를 추가하면 원격 명령 실행 없이 포워딩만 합니다.

로컬 포워딩 (-L)

로컬 포워딩은 내 로컬 포트에 접속하면 원격 서버가 다른 곳으로 전달하는 방식입니다. 방화벽 뒤 내부 서비스에 접근할 때 주로 씁니다.

ssh -L [로컬IP:]로컬포트:목적지호스트:목적지포트 SSH서버
# 예시 1: 원격 DB(5432)를 로컬 5432로 포워딩
ssh -N -L 5432:localhost:5432 user@db-jump.example.com
# → 로컬에서 psql -h localhost -p 5432 로 접속 가능

# 예시 2: 내부망 웹서버를 로컬 8080으로
ssh -N -L 8080:internal-web.corp:80 user@jump.corp.com
# → 브라우저에서 http://localhost:8080 으로 접근

# 예시 3: 특정 인터페이스에만 바인딩 (보안 강화)
ssh -N -L 127.0.0.1:5432:db.internal:5432 user@jump

원격 포워딩 (-R)

원격 포워딩은 반대 방향입니다. 원격 서버의 포트에 접속하면 내 로컬로 전달됩니다. NAT나 방화벽 뒤에 있는 로컬 서버를 외부에서 접근할 때 유용합니다.

ssh -R [원격IP:]원격포트:목적지호스트:목적지포트 SSH서버
# 예시: 로컬 개발 서버(3000)를 퍼블릭 서버의 9000으로 노출
ssh -N -R 0.0.0.0:9000:localhost:3000 user@public-server.example.com
# → public-server.example.com:9000 으로 로컬 앱에 접근 가능

# 원격에서 전체 IP 바인딩을 허용하려면 sshd_config에 설정 필요
# /etc/ssh/sshd_config
# GatewayPorts yes

다이나믹 포워딩 (-D): SOCKS5 프록시

다이나믹 포워딩은 로컬에 SOCKS5 프록시를 열어, 그 프록시를 통한 모든 트래픽을 SSH 서버를 거쳐 인터넷으로 전달합니다. 간이 VPN처럼 쓸 수 있습니다.

# 로컬 1080 포트에 SOCKS5 프록시 생성
ssh -N -D 1080 user@jump.example.com

# 백그라운드로 실행
ssh -fN -D 1080 user@jump.example.com

# curl에서 SOCKS5 프록시 사용
curl --proxy socks5h://localhost:1080 http://example.com

# 브라우저 설정: SOCKS 호스트 = localhost, 포트 = 1080

socks5hh는 DNS 해석도 원격에서 하도록 지시합니다. 로컬 DNS 누출을 방지하는 옵션입니다.

명령어 옵션 정리

# 자주 쓰는 SSH 포워딩 옵션
-N   # 원격 명령 실행 안 함 (포워딩 전용)
-f   # 백그라운드로 실행
-C   # 압축 활성화 (느린 링크에서 유용)
-v   # 상세 디버그 출력
-g   # 다른 호스트에서도 포워딩된 포트 접근 허용
     # (GatewayPorts 설정과 연관)

# 연결 유지 옵션 (터널 끊김 방지)
-o ServerAliveInterval=60
-o ServerAliveCountMax=3

~/.ssh/config 영구 설정

매번 긴 명령을 입력하는 대신, ~/.ssh/config에 포워딩을 영구 설정할 수 있습니다.

# ~/.ssh/config
Host db-tunnel
  HostName jump.example.com
  User admin
  LocalForward 5432 db.internal:5432
  LocalForward 6379 redis.internal:6379
  ServerAliveInterval 60
  ServerAliveCountMax 3

Host expose-dev
  HostName public.example.com
  User deploy
  RemoteForward 9000 localhost:3000

Host socks-proxy
  HostName jump.example.com
  User admin
  DynamicForward 1080
  ExitOnForwardFailure yes
# 설정 파일에 정의된 터널 연결
ssh -N db-tunnel       # DB와 Redis 포워딩
ssh -N socks-proxy     # SOCKS 프록시 시작

SSH 포트 포워딩 명령어

실전 패턴

Jupyter Notebook 원격 접근:

# 원격 서버에서 Jupyter 실행 (no-browser)
# 로컬에서
ssh -N -L 8888:localhost:8888 user@ml-server
# 브라우저에서 http://localhost:8888 접근

여러 포워딩 동시에:

ssh -N \
  -L 5432:db.internal:5432 \
  -L 6379:redis.internal:6379 \
  -L 9200:elastic.internal:9200 \
  user@jump.example.com

포워딩 프로세스 관리:

# 실행 중인 포워딩 SSH 프로세스 찾기
ps aux | grep 'ssh.*-[NfLD]'

# PID로 종료
kill <PID>

# 또는 이름으로
pkill -f 'ssh.*-N.*5432'

JumpHost (ProxyJump)와의 조합

포워딩과 JumpHost를 조합하면 다단계 터널도 만들 수 있습니다.

# 점프 서버를 거쳐 내부 서버로 로컬 포워딩
ssh -N -J jumpuser@jump.example.com \
  -L 5432:db.internal:5432 \
  admin@bastion.internal

# config 파일로 설정
Host internal-tunnel
  HostName bastion.internal
  User admin
  ProxyJump jumpuser@jump.example.com
  LocalForward 5432 db.internal:5432

SSH 포트 포워딩은 네트워크 접근이 제한된 환경에서 안전하게 통신하는 가장 간단한 방법 중 하나입니다. 보안 키 기반 인증과 함께 사용하면 편의성과 보안성을 모두 확보할 수 있습니다.


지난 글: 네트워크 네임스페이스 — 격리된 네트워크 스택

다음 글: rsync over SSH — 원격 파일 동기화


읽어주셔서 감사합니다. 😊