SSH 터널링 완전 정복 — 원격 서버 개발 환경 구축
방화벽 뒤의 서버에 접근해야 할 때
우리 회사의 데이터베이스 서버는 방화벽 뒤에 있다. VPN으로 접근할 수 있지만, VPN은 느리다. 게다가 로컬에서 직접 DB에 연결해서 개발하고 싶었다. 이때 SSH 터널링이 답이었다.
SSH 터널을 사용하면서 VPN 없이도 보안 연결을 할 수 있고, 속도도 훨씬 빠르다. 방법을 정리하겠다.
로컬 포트 포워딩 (Local Port Forwarding)
가장 기본적인 방식이다. 로컬의 포트를 원격 서버의 포트로 연결한다.
<?xml version="1.0"?>
# 형식
ssh -L [local-port]:[remote-host]:[remote-port] [ssh-user]@[ssh-host]
# 예: 로컬 3306 포트를 원격 DB 서버의 3306에 연결
ssh -L 3306:db.internal.com:3306 user@jump-host.example.com
# 이제 로컬에서
mysql -h localhost -P 3306 -u dbuser -p
# -N 플래그로 셸 실행 안 함
ssh -L 3306:db.internal.com:3306 -N user@jump-host.example.com
이제 로컬의 localhost:3306이 원격 DB와 연결된다. 마치 로컬에 DB가 있는 것처럼 작동한다.
원격 포트 포워딩 (Remote Port Forwarding)
반대로 원격 서버에서 로컬 포트에 접근하고 싶을 때:
<?xml version="1.0"?>
# 형식
ssh -R [remote-port]:[local-host]:[local-port] [user]@[remote-host]
# 예: 원격 서버의 8000 포트를 로컬의 3000 포트로 연결
ssh -R 8000:localhost:3000 user@remote-server.com
# 이제 원격 서버에서
curl localhost:8000 # 로컬의 3000 포트로 접근됨
디버깅할 때 유용하다. 원격 서버의 테스트 스크립트가 로컬의 개발 서버에 요청을 보낼 수 있다.
동적 포트 포워딩 (Dynamic Port Forwarding)
모든 트래픽을 SSH를 통해 터널링하고 싶을 때:
<?xml version="1.0"?>
# SOCKS 프록시 생성
ssh -D 1080 user@jump-host.example.com
# 이제 로컬의 1080 포트가 SOCKS 프록시 역할
# 브라우저나 앱에서 프록시 설정:
# SOCKS 호스트: localhost
# 포트: 1080
# 또는 curl에서
curl --socks5-hostname localhost:1080 http://internal.example.com
ProxyJump와 Jump Host
여러 서버를 거쳐서 접근할 때:
<?xml version="1.0"?>
# ssh/config 파일에서
Host db-server
HostName db.internal.com
User postgres
ProxyJump jump-host
Host jump-host
HostName jump.example.com
User ubuntu
IdentityFile ~/.ssh/jump_key
# 이제 바로 접근 가능
ssh db-server # 자동으로 jump-host를 거쳐서 연결됨
# 로컬 포트 포워딩도 자동으로 jump-host를 거친다
ssh -L 3306:db-server:3306 db-server
VS Code Remote SSH
SSH 터널을 VS Code와 통합하면 정말 강력하다:
<?xml version="1.0"?>
# ssh/config
Host remote-dev
HostName dev.example.com
User developer
ProxyJump jump-host
IdentityFile ~/.ssh/dev_key
ServerAliveInterval 60
ServerAliveCountMax 3
// VS Code: Remote Explorer에서 remote-dev 선택
// 자동으로 SSH 연결되고, 원격 서버의 코드를 로컬에서 편집
// 터미널도 원격 서버의 터미널
지속적인 연결 (Persistent Tunnel)
항상 켜둔 터널이 필요할 때:
<?xml version="1.0"?>
# Systemd 서비스로 등록 (Linux)
# ~/.config/systemd/user/ssh-tunnel.service
[Unit]
Description=SSH Tunnel to DB Server
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/ssh -N -L 3306:db.internal.com:3306 user@jump-host.example.com
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
# 활성화
systemctl --user enable ssh-tunnel.service
systemctl --user start ssh-tunnel.service
macOS에서는 launchd를 쓴다:
<?xml version="1.0"?>
# ~/Library/LaunchAgents/com.user.ssh-tunnel.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.ssh-tunnel</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/ssh</string>
<string>-N</string>
<string>-L</string>
<string>3306:db.internal.com:3306</string>
<string>user@jump-host.example.com</string>
</array>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
# 로드
launchctl load ~/Library/LaunchAgents/com.user.ssh-tunnel.plist
보안 고려사항
SSH 터널을 안전하게 사용하려면:
- Key-based authentication만 사용 (비밀번호 금지)
- SSH 키에 passphrase 설정
- ssh-agent로 키 관리
- StrictHostKeyChecking 설정
- 로그 모니터링
<?xml version="1.0"?>
# ssh/config에서 보안 강화
Host *
StrictHostKeyChecking accept-new
AddKeysToAgent yes
UseKeychain yes
IdentitiesOnly yes
Host jump-host
HostName jump.example.com
User ubuntu
IdentityFile ~/.ssh/id_jump_only
# 특정 포트만 열기
LocalForward 3306 db.internal.com:3306
터널 모니터링
<?xml version="1.0"?>
# 활성 터널 확인
ps aux | grep ssh
# 더 자세한 정보
lsof -i :3306 # 3306 포트 사용 중인 프로세스
# SSH 연결 로그
ssh -v -L 3306:db.internal.com:3306 user@jump-host.example.com
실전 예제
우리 회사에서 사용하는 설정:
<?xml version="1.0"?>
# ssh/config
Host prod-jump
HostName jump.prod.example.com
User devops
IdentityFile ~/.ssh/prod_key
ControlMaster auto
ControlPath ~/.ssh/control-%h-%p-%r
ControlPersist 600
Host prod-db
HostName postgres.internal
ProxyJump prod-jump
User postgres
IdentityFile ~/.ssh/prod_db_key
Host prod-api
HostName api.internal
ProxyJump prod-jump
User ubuntu
IdentityFile ~/.ssh/prod_api_key
Host dev-*
ProxyJump prod-jump
# 터널 설정
LocalForward 5432 postgres.internal:5432 # DB
LocalForward 6379 redis.internal:6379 # Redis
LocalForward 9200 elasticsearch.internal:9200 # ES
마무리
SSH 터널링은 보안과 편의성을 동시에 제공한다. VPN보다 빠르고, 필요한 서비스만 로컬에 노출할 수 있다. 원격 개발 환경 구축에 필수적인 도구다.
처음엔 복잡해 보이지만, 한 번 설정해두면 정말 편하다. 특히 마이크로서비스 아키텍처에서는 여러 서버를 접근해야 하니까, 이런 설정이 있으면 개발 생산성이 크게 향상된다.