루프백 바인딩과 SSH 터널
루프백 바인딩과 SSH 터널
어떤 포트는 인터넷에서 닿아야 하고, 어떤 포트는 같은 호스트 안에서만 쓰여야 합니다. 둘을 구분하는 가장 단순한 도구는 바인딩 주소. 외부에서만 들어갈 수 있는 길을 열고 싶으면 SSH 터널이 가벼운 답. 이 글은 0.0.0.0 과 127.0.0.1 의 차이 · Docker 의 포트 매핑 · SSH 포트 포워딩 (-L · -R · -D) · VPN 도구 비교 · 외부 노출 최소화의 운영 모델.
1. 바인딩 주소에 대한 이야기
| 주소 | 의미 |
|---|---|
0.0.0.0 |
모든 인터페이스에 바인딩 (외부 접근 가능). |
127.0.0.1 |
루프백. 같은 호스트에서만 접근 가능. |
:: / ::1 |
IPv6 의 모든 인터페이스 / 루프백. |
<특정 IP> |
해당 인터페이스에만. |
리스닝 소켓이 어떤 주소에 묶이느냐가 외부 도달 가능성을 결정. 방화벽이 첫 방어선이라면, 바인딩 주소는 그 이전의 결정.
2. Docker 의 포트 매핑
Compose · docker run 의 포트 표기:
ports:
- "8080:8080" # 호스트의 0.0.0.0:8080 으로 노출
- "127.0.0.1:8080:8080" # 호스트 루프백에만 노출
- "8080" # 임의의 호스트 포트 → 컨테이너 8080
운영에서는 외부 노출이 필요한 서비스 (웹 · 프록시) 만 0.0.0.0 으로 열고, DB · 캐시 · 관리 UI 는 루프백 바인딩만 사용하는 모델이 흔함. 외부 접근이 필요할 때는 SSH 터널을 통해 일시적으로 통과.
3. SSH 포트 포워딩
ssh 의 세 가지 포워딩 옵션:
| 옵션 | 의미 |
|---|---|
-L |
로컬 포트 → 원격 호스트의 어떤 포트로 (로컬 포워딩). |
-R |
원격의 포트 → 로컬의 어떤 포트로 (원격 포워딩). |
-D |
로컬에 SOCKS 프록시 (동적 포워딩). |
-L — 운영 DB 접근:
ssh -L 5432:127.0.0.1:5432 user@server
내 PC 의 5432 로 접속하면 SSH 터널을 통해 server 의 루프백 5432 (= DB) 로 연결. 서버의 DB 포트는 인터넷에 열려 있을 필요가 없습니다.
-R — 내부 개발 서버 임시 노출:
ssh -R 8080:127.0.0.1:3000 user@server
서버의 8080 으로 들어온 연결을 내 PC 의 3000 으로. 서버 측 GatewayPorts yes 설정 필요할 수 있음.
-D — SOCKS 프록시:
ssh -D 1080 user@server
내 PC 의 1080 에 SOCKS5 프록시. 브라우저 · 도구가 서버를 경유해 통신.
4. VPN 도구
| 도구 | 메모 |
|---|---|
| WireGuard | 2016 시작. 리눅스 커널 통합 (2020). 단순하고 빠른 모던 VPN. |
| Tailscale | 2019. WireGuard 기반의 메시 VPN. 기기 간 직접 연결 + 코디네이션. |
| OpenVPN | 2001. SSL / TLS 기반의 오래된 VPN. |
SSH 터널이 한두 포트의 일시 노출에 가볍다면, VPN 은 다수 포트 · 여러 호스트를 안정적으로 잇는 자리. Tailscale 은 NAT 통과 · 제로 설정 측면에서 자주 거론됩니다.
5. 외부 노출 최소화
여러 층의 방어가 겹쳐 있을 때 가장 안쪽의 결정이 바인딩 주소.
- 클라우드 보안 그룹 / Network ACL.
- 호스트 수준의
ufw(Linux) · Windows Defender Firewall. - 컨테이너 네트워크 격리.
클라우드 보안 그룹이 0.0.0.0/0 으로 열려 있어도 서비스가 127.0.0.1 에 묶여 있으면 외부에서 닿지 못함 (역도 마찬가지).
6. bastion / jump host
여러 내부 호스트로의 접근을 한 호스트 (bastion) 로 모음:
# ~/.ssh/config
Host db1
Hostname 10.0.1.5
ProxyJump bastion
Host bastion
Hostname bastion.example.com
ssh db1 만으로 bastion 을 거쳐 db1 에. SSH 의 ProxyJump (-J) 옵션은 OpenSSH 7.3+ 에서 표준.
7. 운영 서버 일반 구성
- 80 / 443 — 외부 노출 (Caddy 또는 nginx).
- 22 — 외부 노출 (SSH). 가능하면 키 인증 · fail2ban · 포트 변경.
- 그 외 모든 포트 —
127.0.0.1바인딩 + SSH 터널로만 접근.
services:
app:
ports:
- "127.0.0.1:8080:8080"
db:
ports:
- "127.0.0.1:5432:5432"
caddy:
ports:
- "80:80"
- "443:443"
외부에 닿는 컨테이너는 Caddy 만, 그 외는 호스트 루프백을 통해서만 접근. SSH 터널이나 호스트 셸 안에서 직접 호출.
8. 개발 PC 에서 운영 DB 보기
ssh -L 15432:127.0.0.1:5432 user@server
psql -h 127.0.0.1 -p 15432 -U app appdb
로컬 포트를 5432 가 아닌 15432 로 두면 로컬에 같은 DB 가 떠 있어도 충돌 없음.
9. 자주 걸리는 자리
0.0.0.0 의도치 않은 노출 — compose 의 "PORT:PORT" 표기는 기본이 0.0.0.0. 검토 없이 복붙하면 외부 노출이 누적.
-R 의 권한 한계 — 기본 SSH 설정은 원격 포워딩을 루프백에만 묶음. 외부 접근까지 열려면 서버의 GatewayPorts yes 필요, 그만큼 보안 검토가 따라옴.
터널이 끊기는 경우 — 네트워크 단절 시 SSH 세션이 침묵하다 죽음. ServerAliveInterval · autossh 같은 보조가 도움.
방화벽 · SG · 바인딩의 책임 혼동 — 어느 층이 막고 있는지 추적이 어려움. 변경 시 한 층씩 점검.
Docker 사용자 정의 네트워크와 호스트 노출 — 컨테이너 간 통신은 사용자 네트워크로, 외부 노출은 명시적 ports. 두 자리를 섞지 않음.
하고픈 말
루프백 바인딩 + SSH 터널 둘이 결합하면 운영 서버의 보안 표면이 80 / 443 / 22 세 포트로 압축됩니다. compose 에 무심코 적힌 "5432:5432" 한 줄이 운영 사고의 출발점이 되는 자리. 모든 새 컨테이너는 127.0.0.1 접두를 기본값으로 둘 만큼.
Next
- single-server-philosophy
- local-https-mkcert
OpenSSH 매뉴얼 · Docker 네트워킹 개요 · Tailscale 공식 · WireGuard 공식 · Mozilla SSH 가이드라인 · ProxyJump (OpenSSH) 를 참고합니다.