SSH 와 파일 전송 · 터널링과 포트포워딩 · 이론
-L, -R, -D 를 헷갈리지 않는 법
한 줄 요약
-L 은 내 쪽 포트를 열어 저쪽으로 보내고, -R 은 저쪽 포트를 열어 내 쪽으로 끌어오고, -D 는 내 쪽에 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) | 애플리케이션이 고른 임의 목적지 | 브라우저 전체를 내부망 경유 |
터널 전용 연결에는 -N -f 를 함께 쓴다. -N 은 원격 명령을 실행하지 않고, -f 는 백그라운드로 보낸다.
ssh -N -f -L 15432:db.internal:5432 bastion-R 에는 함정이 하나 있다. 기본적으로 SSH 서버의 루프백에만 바인딩된다. 즉 서버의 다른 사람은 그 포트를 못 쓴다. 밖에 열려면 서버에서 GatewayPorts yes 를 켜야 하고, 이건 보안상 신중해야 한다.
서버 쪽에서 포워딩을 통제하는 지시어들.
AllowTcpForwarding noAllowAgentForwarding noGatewayPorts noPermitOpen 10.0.5.20:5432PermitOpen 이 특히 실용적이다. 포워딩을 완전히 막지 않으면서 목적지를 화이트리스트로 제한한다. "개발자가 운영 DB 에는 붙어야 하지만 다른 내부 서비스에는 접근하면 안 되는" 상황에 딱 맞는다.
에이전트 포워딩은 위험하다
-A(에이전트 포워딩)는 편리하지만, 경유 서버의 root 권한을 가진 사람이 내 에이전트 소켓을 통해 내 키로 다른 서버에 인증할 수 있다. 배스천 경유가 목적이라면 ProxyJump(-J)를 써라. 에이전트를 노출하지 않고 같은 결과를 얻는다.
ssh -J bastion deploy@10.0.3.14이스케이프 시퀀스
터널을 쓰다 보면 연결이 멈춘 것처럼 보일 때가 있다. 이때 쓰는 것이 이스케이프 시퀀스다. 줄 맨 처음에서 ~ 를 누른 다음 문자를 입력한다.
| 시퀀스 | 동작 |
| --- | --- |
| ~. | 연결 강제 종료 (멈춘 세션 탈출) |
| ~^Z | SSH 를 백그라운드로 |
| ~# | 포워딩 중인 연결 목록 |
| ~C | 커맨드라인 열기 (실행 중에 -L 추가/삭제) |
| ~? | 도움말 |
~C 로 접속 중에 포워딩을 추가할 수 있다는 것을 아는 사람이 의외로 적다. 세션을 끊지 않고 터널을 하나 더 파야 할 때 유용하다.
현장에서 만나는 모습
터널을 열어 두고 잊는다. -N -f 로 백그라운드에 보낸 ssh 프로세스는 조용히 살아 있다. 몇 주 뒤 "누가 이 포트 열었지?" 가 된다. ss -ltnp 에서 프로세스가 ssh 로 보이면 터널이다. 팀 규칙으로 ControlPath 나 프로세스 이름에 용도를 남기는 곳도 있다.
다음 실습에서 할 것
로컬·원격·동적 세 방향 포워딩을 전부 만들어 보고, ss 로 각 터널이 어디에 포트를 열었는지 확인한다. 마지막에는 세 방식의 비교표를 실제 증거와 함께 제출한다.