LabHub
배우기 러닝패스 코스

SSHとファイル転送

-L、-R、-Dを混同しない方法

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

-L내 쪽 포트를 열어 저쪽으로 보내고, -R저쪽 포트를 열어 내 쪽으로 끌어오고, -D내 쪽에 SOCKS 프록시를 세운다. 방향만 정확히 잡으면 나머지는 문법이다.

概念マップ: 내 쪽 포트를 열어 저쪽으로 보내고・저쪽 포트를 열어 내 쪽으로 끌어오고・내 쪽에 SOCKS 프록시를 세운다・로컬 포워딩

왜 이게 필요했나

내부망 DB 에 붙어야 하는데 그 DB 는 바깥에서 접근할 수 없다. 배스천 서버에는 SSH 로 붙을 수 있다. 이때 필요한 것이 로컬 포워딩이다. 내 노트북의 15432 로 들어온 연결을 배스천을 거쳐 db.internal:5432 로 보낸다.

ssh -L 15432:db.internal:5432 bastion

읽는 법: -L <내가 열 포트>:<배스천이 볼 때의 목적지>:<그 포트>. 중간의 호스트 이름은 SSH 서버 기준으로 해석된다는 점이 핵심이다. 내 노트북에서 db.internal 이 안 풀려도 상관없다.

어떻게 동작하나

옵션 포트가 열리는 곳 트래픽 방향 대표 용도
-L 로컬 로컬 → SSH 서버 → 목적지 내부망 DB, 관리 콘솔 접근
-R 원격(SSH 서버) 원격 → SSH 서버 → 로컬 → 목적지 방화벽 뒤 서비스를 밖에 노출, 웹훅 수신
-D 로컬(SOCKS) 애플리케이션이 고른 임의 목적지 브라우저 전체를 내부망 경유

SSH 포워딩 세 방향. -L 은 내 컴퓨터에 포트를 열어 SSH 서버를 거쳐 저쪽 망의 목적지로 내보내고, -R 은 반대로 SSH 서버에 포트를 열어 들어온 것을 내 컴퓨터 쪽 목적지로 끌어오며, -D 는 내 컴퓨터에 SOCKS 프록시를 열어 애플리케이션이 고른 여러 목적지로 나간다

터널 전용 연결에는 -N -f 를 함께 쓴다. -N 은 원격 명령을 실행하지 않고, -f 는 백그라운드로 보낸다.

ssh -N -f -L 15432:db.internal:5432 bastion

-R 에는 함정이 하나 있다. 기본적으로 SSH 서버의 루프백에만 바인딩된다. 즉 서버의 다른 사람은 그 포트를 못 쓴다. 밖에 열려면 서버에서 GatewayPorts yes 를 켜야 하고, 이건 보안상 신중해야 한다.

서버 쪽에서 포워딩을 통제하는 지시어들.

AllowTcpForwarding no
AllowAgentForwarding no
GatewayPorts no
PermitOpen 10.0.5.20:5432

PermitOpen 이 특히 실용적이다. 포워딩을 완전히 막지 않으면서 목적지를 화이트리스트로 제한한다. "개발자가 운영 DB 에는 붙어야 하지만 다른 내부 서비스에는 접근하면 안 되는" 상황에 딱 맞는다.

에이전트 포워딩은 위험하다

-A(에이전트 포워딩)는 편리하지만, 경유 서버의 root 권한을 가진 사람이 내 에이전트 소켓을 통해 내 키로 다른 서버에 인증할 수 있다. 배스천 경유가 목적이라면 ProxyJump(-J)를 써라. 에이전트를 노출하지 않고 같은 결과를 얻는다.

ssh -J bastion deploy@10.0.3.14

이스케이프 시퀀스

터널을 쓰다 보면 연결이 멈춘 것처럼 보일 때가 있다. 이때 쓰는 것이 이스케이프 시퀀스다. 줄 맨 처음에서 ~ 를 누른 다음 문자를 입력한다.

시퀀스 동작
~. 연결 강제 종료 (멈춘 세션 탈출)
~^Z SSH 를 백그라운드로
~# 포워딩 중인 연결 목록
~C 커맨드라인 열기 (실행 중에 -L 추가/삭제)
~? 도움말

~C접속 중에 포워딩을 추가할 수 있다는 것을 아는 사람이 의외로 적다. 세션을 끊지 않고 터널을 하나 더 파야 할 때 유용하다.

세 방향을 헷갈리지 않는 법

-L, -R, -D 는 문자만 다르고 하는 일이 아예 다르다. 어느 쪽에서 듣고 어느 쪽에서 나가는지만 잡으면 헷갈리지 않는다.

옵션 듣는 곳 나가는 곳 쓰는 상황
-L 5432:db:5432 내 컴퓨터 원격 서버 내가 못 닿는 DB 에 붙는다
-R 8080:localhost:3000 원격 서버 내 컴퓨터 내 개발 서버를 밖에서 보게 한다
-D 1080 내 컴퓨터(SOCKS) 원격 서버 여러 목적지를 한 번에

-Ldb:5432원격 서버가 보는 이름이다. 내 hosts 파일이 아니라 서버의 DNS 로 풀린다. 여기서 어긋나는 경우가 가장 많다.

왜 원격 포트가 밖에서 안 열리나

-R 8080:localhost:3000 을 걸어 놓고 다른 장비에서 접속했는데 거절당하는 것은 설정 문제가 아니라 기본값이다. sshd 는 GatewayPorts no 아래에서 전달 포트를 원격 서버의 루프백에만 연다. 서버의 /etc/ssh/sshd_config 에서 GatewayPorts clientspecified 로 바꾸고 -R 0.0.0.0:8080:localhost:3000 처럼 주소를 명시해야 밖으로 열린다. 이 값을 켜는 것은 그 서버에 접속할 수 있는 사람이라면 누구나 내 노트북의 포트를 인터넷에 노출할 수 있게 한다는 뜻이므로, 공용 점프 서버에서는 켜지 않는다.

끊긴 터널을 되살리는 것은 ssh 가 아니다

터널은 반드시 끊긴다. NAT 표가 만료되고, 무선이 끊기고, 서버가 재시작한다. ssh 자체는 재접속하지 않으므로 다시 여는 일은 밖에서 맡는다.

autossh -M 0 -N \
  -o ServerAliveInterval=15 -o ServerAliveCountMax=3 \
  -o ExitOnForwardFailure=yes \
  -L 5432:db.internal:5432 jump.example.com

ServerAliveInterval조용한 연결이 죽었는지 확인하는 장치다. 15초마다 물어보고 세 번 답이 없으면 끊는다. ExitOnForwardFailure=yes 는 포트를 못 열었을 때 접속을 성공으로 치지 않게 한다. 이것을 빼면 터널 없이 살아 있는 ssh 가 남아서, 연결은 되어 있는데 아무것도 안 되는 상태가 된다.

다중화로 접속을 아낀다

같은 서버에 반복해서 붙는다면 연결 하나를 나눠 쓴다.

Host jump
  ControlMaster auto
  ControlPath ~/.ssh/cm-%r@%h:%p
  ControlPersist 10m

두 번째 접속부터는 핸드셰이크와 인증을 건너뛰므로 체감이 크게 달라진다. 다만 ControlPath소켓 파일이라, 홈 디렉터리가 NFS 위에 있으면 동작하지 않는다. 그럴 때는 /run/user/$UID 아래로 옮긴다.

현장에서 만나는 모습

터널을 열어 두고 잊는다. -N -f 로 백그라운드에 보낸 ssh 프로세스는 조용히 살아 있다. 몇 주 뒤 "누가 이 포트 열었지?" 가 된다. ss -ltnp 에서 프로세스가 ssh 로 보이면 터널이다. 팀 규칙으로 ControlPath 나 프로세스 이름에 용도를 남기는 곳도 있다.

다음 실습에서 할 것

로컬·원격·동적 세 방향 포워딩을 전부 만들어 보고, ss 로 각 터널이 어디에 포트를 열었는지 확인한다. 마지막에는 세 방식의 비교표를 실제 증거와 함께 제출한다.