문제 상황

클라우드 환경에서 흔한 구성이 있다. 보안을 위해 내부 서버(DB, 앱 서버 등)는 외부에서 직접 접속할 수 없고, Bastion 서버(점프 서버)를 통해서만 접근할 수 있는 구조다.

[내 PC] → [Bastion 서버] → [내부 서버]
           (공인 IP)        (사설 IP)

이런 구조에서 내부 서버에 접속하려면 보통 두 단계를 거친다:

# 1단계: Bastion 서버에 접속
ssh user@bastion-host

# 2단계: Bastion에서 내부 서버에 접속
ssh user@internal-host

매번 두 번 SSH를 치는 건 번거롭고, 파일 전송(scp)은 더 복잡해진다. SSH ProxyJump를 사용하면 이 과정을 한 줄로 줄일 수 있다.

ProxyJump란?

OpenSSH 7.3(2016년)부터 추가된 기능으로, SSH 접속 시 중간 서버를 점프 호스트로 지정할 수 있다. 중간 서버에 셸을 열지 않고, TCP 포워딩만 수행한다.

핵심 포인트:

  • Bastion 서버에 로그인하지 않고 통과만 한다
  • 최종 목적지까지 암호화가 유지된다 (Bastion에서 트래픽을 볼 수 없다)
  • 파일 전송(scp, rsync)도 동일하게 동작한다

기본 사용법: -J 플래그

ssh -J user@bastion-host user@internal-host

이 한 줄로 Bastion을 거쳐 내부 서버에 바로 접속한다.

예시

# Bastion(203.0.113.10)을 거쳐 내부 DB 서버(10.0.1.50)에 접속
ssh -J admin@203.0.113.10 dbadmin@10.0.1.50

포트가 다른 경우

# Bastion이 2222 포트를 사용하는 경우
ssh -J admin@203.0.113.10:2222 dbadmin@10.0.1.50

SSH Config로 영구 설정

매번 -J 플래그를 타이핑하는 건 여전히 번거롭다. ~/.ssh/config에 설정해두면 ssh internal-db만으로 접속할 수 있다.

# Bastion 서버
Host bastion
    HostName 203.0.113.10
    User admin
    IdentityFile ~/.ssh/id_ed25519

# 내부 DB 서버 (Bastion 경유)
Host internal-db
    HostName 10.0.1.50
    User dbadmin
    ProxyJump bastion
    IdentityFile ~/.ssh/id_ed25519

이제 이렇게 접속한다:

ssh internal-db

파일 전송도 자연스럽게 동작한다:

# 내부 서버로 파일 복사
scp backup.sql internal-db:/tmp/

# 내부 서버에서 파일 가져오기
scp internal-db:/var/log/app.log ./

# rsync도 동일
rsync -avz ./deploy/ internal-db:/var/www/app/

다중 점프 (Multi-hop)

Bastion이 여러 단계인 경우도 지원한다.

[내 PC] → [Bastion 1] → [Bastion 2] → [최종 서버]

-J 플래그 방식

ssh -J user@bastion1,user@bastion2 user@final-host

쉼표로 구분하면 순서대로 점프한다.

SSH Config 방식

Host bastion1
    HostName 203.0.113.10
    User admin

Host bastion2
    HostName 10.0.1.1
    User admin
    ProxyJump bastion1

Host final-server
    HostName 10.0.2.50
    User deploy
    ProxyJump bastion2

체인처럼 연결된다: final-serverbastion2bastion1 → 인터넷.

ProxyJump vs ProxyCommand

SSH Config에서 비슷한 역할을 하는 ProxyCommand가 있다. 차이점:

# ProxyJump (간단)
Host internal
    ProxyJump bastion

# ProxyCommand (구버전 호환)
Host internal
    ProxyCommand ssh -W %h:%p bastion
ProxyJumpProxyCommand
도입OpenSSH 7.3+OpenSSH 5.4+
문법간단복잡
다중 점프쉼표로 구분중첩 필요
커맨드라인-J 플래그-o ProxyCommand=...

OpenSSH 7.3 이상이면 ProxyJump를 쓰는 게 낫다. 더 간단하고 다중 점프도 깔끔하다.

OpenSSH 버전 확인: ssh -V

실전 팁

1. Bastion 서버에 에이전트 포워딩

내부 서버 접속 시 로컬의 SSH 키를 사용하려면 에이전트 포워딩을 활성화한다:

Host bastion
    HostName 203.0.113.10
    User admin
    ForwardAgent yes

보안 주의: ForwardAgent는 Bastion 서버의 root가 에이전트 소켓에 접근할 수 있으므로, 신뢰할 수 있는 서버에서만 사용해야 한다.

2. 접속 끊김 방지 (KeepAlive)

점프 호스트를 거치면 타임아웃이 발생하기 쉽다:

Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3

60초마다 keepalive 패킷을 보내서 연결을 유지한다.

3. 포트 포워딩과 함께 사용

Bastion을 거쳐 내부 DB 포트를 로컬로 포워딩할 수도 있다:

# 내부 PostgreSQL(5432)을 로컬 15432로 포워딩
ssh -J bastion -L 15432:10.0.1.50:5432 admin@10.0.1.50 -N

이제 localhost:15432로 내부 DB에 접속할 수 있다:

psql -h localhost -p 15432 -U postgres

SSH Config로 설정하면 더 깔끔하다:

Host db-tunnel
    HostName 10.0.1.50
    User dbadmin
    ProxyJump bastion
    LocalForward 15432 localhost:5432
ssh -N db-tunnel
# 다른 터미널에서: psql -h localhost -p 15432 -U postgres

정리

상황명령어
단일 점프ssh -J bastion internal
다중 점프ssh -J bastion1,bastion2 final
파일 전송scp -J bastion file internal:/path
포트 포워딩ssh -J bastion -L 15432:db:5432 db-host -N

SSH ProxyJump는 Bastion 서버 환경에서 필수적인 기능이다. SSH Config에 한 번 설정해두면, 내부 서버도 직접 연결된 것처럼 쓸 수 있다.