🌍 네트워크

포트 포워딩

Port Forwarding

외부에서 내부 네트워크의 특정 장치나 서비스에 접근할 수 있도록 라우터/방화벽의 포트 트래픽을 내부 IP로 전달하는 기술. NAT 환경에서 로컬 서버를 외부에 노출할 때 필수.

📖 상세 설명

포트 포워딩(Port Forwarding)은 외부 네트워크에서 들어오는 특정 포트의 트래픽을 내부 네트워크의 지정된 IP 주소와 포트로 전달하는 네트워크 기술입니다. 가정이나 회사에서 사용하는 공유기(라우터)는 NAT(Network Address Translation)를 통해 하나의 공인 IP로 여러 장치가 인터넷을 사용하게 하는데, 이 구조에서 외부에서 내부 서버에 접근하려면 반드시 포트 포워딩 설정이 필요합니다.

NAT 환경에서 내부 장치들은 사설 IP(192.168.x.x, 10.x.x.x 등)를 사용합니다. 외부에서는 이 사설 IP에 직접 접근할 수 없으므로, 공유기에 "외부에서 80번 포트로 들어오면 내부 192.168.0.100:80으로 보내라"라는 규칙을 설정합니다. 이것이 포트 포워딩의 핵심입니다. 마치 회사 대표번호로 전화하면 내선번호로 연결해주는 것과 같습니다.

포트 포워딩에는 여러 유형이 있습니다. 로컬 포트 포워딩(Local Port Forwarding)은 SSH 터널을 통해 로컬 포트를 원격 서버의 포트에 연결합니다. 리모트 포트 포워딩(Remote Port Forwarding)은 원격 서버의 포트를 로컬 서비스에 연결하여, 외부에서 로컬 서버에 접근할 수 있게 합니다. 다이나믹 포트 포워딩은 SOCKS 프록시처럼 동작하여 모든 트래픽을 터널링합니다.

현대 개발 환경에서 포트 포워딩은 다양하게 활용됩니다. 로컬 개발 서버를 외부에 공개하여 webhook 테스트나 클라이언트 시연에 사용하고(ngrok, Cloudflare Tunnel), 홈 서버로 미디어 서버, NAS, 게임 서버를 운영하며, SSH 터널을 통해 방화벽 뒤의 데이터베이스에 안전하게 접속합니다. 클라우드 환경에서는 Kubernetes의 kubectl port-forward 명령으로 Pod에 직접 접근하기도 합니다.

💻 코드 예제

# SSH 로컬 포트 포워딩
# 로컬 3306 포트를 원격 서버를 경유해 DB 서버 3306에 연결
ssh -L 3306:db-server.internal:3306 user@bastion.example.com

# 실사용 예: 로컬에서 MySQL 클라이언트로 접속
mysql -h 127.0.0.1 -P 3306 -u dbuser -p

# SSH 리모트 포트 포워딩 (역방향)
# 원격 서버의 8080 포트로 들어오는 요청을 로컬 3000으로 전달
# 외부에서 bastion:8080 접속 시 내 로컬 개발 서버(3000)에 연결
ssh -R 8080:localhost:3000 user@bastion.example.com

# 다이나믹 포트 포워딩 (SOCKS 프록시)
# 로컬 9090을 SOCKS5 프록시로 사용
ssh -D 9090 user@remote-server.com
# 브라우저에서 SOCKS5 프록시 localhost:9090 설정

# 백그라운드에서 SSH 터널 유지
ssh -f -N -L 5432:localhost:5432 user@server.com
# -f: 백그라운드 실행
# -N: 원격 명령 실행 안 함 (터널만 유지)

# 여러 포트 동시 포워딩
ssh -L 3306:db:3306 -L 6379:redis:6379 -L 5432:postgres:5432 user@bastion

# autossh로 끊김 없는 터널 유지 (자동 재연결)
autossh -M 0 -f -N -L 3306:db:3306 user@bastion.example.com
# Linux iptables를 이용한 포트 포워딩
# 전제: IP 포워딩 활성화
echo 1 > /proc/sys/net/ipv4/ip_forward
# 영구 설정: /etc/sysctl.conf에 net.ipv4.ip_forward=1 추가

# 외부 80 포트 → 내부 192.168.1.100:8080으로 포워딩
# PREROUTING: 패킷이 라우팅되기 전에 목적지 변경
iptables -t nat -A PREROUTING -p tcp --dport 80 \
    -j DNAT --to-destination 192.168.1.100:8080

# FORWARD 체인에서 해당 트래픽 허용
iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 8080 -j ACCEPT

# MASQUERADE: 내부에서 외부로 나갈 때 출발지 IP 변환
iptables -t nat -A POSTROUTING -j MASQUERADE

# 현재 NAT 규칙 확인
iptables -t nat -L -n -v

# 특정 규칙 삭제
iptables -t nat -D PREROUTING -p tcp --dport 80 \
    -j DNAT --to-destination 192.168.1.100:8080

# 규칙 저장 (Ubuntu/Debian)
iptables-save > /etc/iptables/rules.v4

# nftables (최신 Linux의 iptables 대체)
nft add rule nat prerouting tcp dport 80 dnat to 192.168.1.100:8080

# firewalld (CentOS/RHEL)
firewall-cmd --zone=public --add-forward-port=port=80:proto=tcp:toport=8080:toaddr=192.168.1.100 --permanent
firewall-cmd --reload
# ngrok - 로컬 서버를 공인 URL로 노출 (가장 간단)
# 설치: brew install ngrok 또는 https://ngrok.com에서 다운로드
ngrok http 3000  # 로컬 3000 포트를 인터넷에 공개
# 결과: https://abc123.ngrok.io → localhost:3000

# 커스텀 도메인 사용 (유료)
ngrok http --domain=my-app.ngrok.io 3000

# TCP 포트 포워딩 (SSH, DB 등)
ngrok tcp 22  # 로컬 SSH를 외부에서 접근 가능하게

# Cloudflare Tunnel (무료, 더 안정적)
# 1. cloudflared 설치
brew install cloudflared  # macOS
# 2. 터널 생성 및 인증
cloudflared tunnel login
cloudflared tunnel create my-tunnel
# 3. 로컬 서버 노출
cloudflared tunnel --url http://localhost:3000

# Kubernetes port-forward
# Pod에 직접 접근 (디버깅용)
kubectl port-forward pod/my-app-pod 8080:80
# 이제 localhost:8080 → Pod의 80 포트

# Service를 통한 포트 포워딩
kubectl port-forward svc/my-service 3000:80

# Docker 포트 매핑 (-p 플래그)
docker run -p 8080:80 nginx  # 호스트 8080 → 컨테이너 80
docker run -p 127.0.0.1:8080:80 nginx  # 로컬호스트만 접근 허용

🗣️ 실무에서 이렇게 말하세요

💬 홈 서버 구축 상담에서
"집에서 NAS나 미디어 서버를 운영하려면 공유기에서 포트 포워딩 설정을 해야 해요. 공유기 관리 페이지에서 '가상 서버' 또는 '포트 포워딩' 메뉴를 찾아서, 외부 포트 8080을 내부 NAS IP의 8080으로 연결하면 됩니다. 단, 보안을 위해 기본 포트(21, 22, 80)보다는 고유한 포트 번호를 사용하고, 가능하면 VPN을 설정하는 게 좋습니다."
💬 개발 미팅에서
"Stripe webhook 테스트하려면 로컬 서버를 외부에서 접근할 수 있어야 하는데, ngrok 쓰면 간단해요. 'ngrok http 3000' 실행하면 공인 URL이 생성되고, 그걸 Stripe Dashboard에 webhook endpoint로 등록하면 됩니다. 개발 끝나면 Cloudflare Tunnel로 전환하는 것도 좋아요, 무료이고 더 안정적이거든요."
💬 인프라 트러블슈팅에서
"프로덕션 DB에 직접 접근이 안 되니까 SSH 터널 통해서 들어가야 해요. 'ssh -L 5432:prod-db.internal:5432 bastion' 실행하고 나서, DBeaver에서 localhost:5432로 연결하면 됩니다. 방화벽 정책상 bastion 서버만 DB 접근이 허용되어 있어서, 이 방법이 가장 안전하고 감사 로그도 남습니다."

⚠️ 흔한 실수 & 주의사항

기본 포트를 그대로 외부에 노출

SSH(22), RDP(3389), MySQL(3306) 등 기본 포트를 그대로 열면 자동화된 봇 공격의 표적이 됩니다. 외부 포트는 30022, 33389처럼 비표준 포트로 매핑하고, fail2ban 등으로 무차별 대입 공격을 방어하세요.

DMZ 설정으로 모든 포트 개방

공유기의 DMZ 기능은 특정 IP의 모든 포트를 외부에 노출합니다. 편리하지만 매우 위험하므로, 필요한 포트만 개별적으로 포워딩하세요. DMZ는 최후의 수단으로만 사용하고, 해당 장치의 방화벽을 반드시 설정하세요.

공유기 펌웨어 UPnP 보안 취약점

UPnP(Universal Plug and Play)는 앱이 자동으로 포트 포워딩을 설정하게 해주지만, 악성 프로그램도 이를 악용할 수 있습니다. 가능하면 UPnP를 비활성화하고 수동으로 포트 포워딩을 설정하세요.

올바른 포트 포워딩 보안 설정

VPN(WireGuard, OpenVPN)을 통해 내부 네트워크에 먼저 접속한 후 서비스에 접근하는 것이 가장 안전합니다. 불가능하다면 방화벽에서 특정 IP만 허용하고, 모든 서비스에 강력한 인증(SSH 키, MFA)을 적용하세요.

🔗 관련 용어

📚 더 배우기