리눅스 ssh 명령어 사용법 - 접속, 포트 포워딩, 터널링 실전 예제
이 명령어를 언제 쓰는가
ssh는 원격 서버에 접속하는 명령이지만, 실무에서 값어치의 절반은 접속 이외의 용도에 있다. 배스천을 거쳐 사설망 서버에 들어가고, 로컬 PC 의 DB 툴을 방화벽 뒤 MySQL 에 연결하고, 서버 20대에 같은 점검 명령을 한 번에 돌리는 일이 전부 ssh 한 줄이다.
운영 서버가 공인 IP 없이 배스천 뒤에 있는 구성은 이제 기본이다. 매번 배스천에 들어갔다가 다시 들어가는 대신 -J 와 ~/.ssh/config 를 쓰면 한 번에 간다. 이걸 아느냐 모르느냐가 하루 작업량을 바꾼다.
기본 형식
ssh [옵션] [사용자@]호스트 [원격에서 실행할 명령]
- 사용자 — 생략하면 로컬 사용자 이름으로 접속을 시도한다. 로컬 계정명과 서버 계정명이 다르면 반드시 적는다.
- 원격 명령 — 붙이면 로그인하지 않고 그 명령만 실행한 뒤 종료한다. 스크립트에서 쓰는 형태다.
자주 쓰는 옵션
| 옵션 | 의미 | 예시 |
|---|---|---|
-p 포트 | 접속 포트(소문자 p) | ssh -p 22022 deploy@10.0.1.20 |
-i 키파일 | 사용할 개인키 지정 | ssh -i ~/.ssh/prod.pem ec2-user@1.2.3.4 |
-J 경유호스트 | 배스천을 거쳐 접속(ProxyJump) | ssh -J bastion app@10.0.1.20 |
-L 로컬:대상:포트 | 로컬 포트를 원격 쪽으로 전달 | -L 13306:127.0.0.1:3306 |
-R 원격:대상:포트 | 원격 포트를 로컬 쪽으로 전달 | -R 8080:127.0.0.1:3000 |
-N | 원격 명령 실행 없이 터널만 유지 | ssh -N -L 13306:127.0.0.1:3306 bastion |
-f | 인증 후 백그라운드로 보낸다 | ssh -fN -L ... |
-t | 강제로 TTY 할당. sudo·vim 등에 필요 | ssh -t host 'sudo systemctl restart nginx' |
-o 설정=값 | 설정 파일 항목을 한 번만 덮어쓴다 | -o ConnectTimeout=5 |
-v / -vvv | 접속 과정 디버깅. 인증 실패 원인 추적 | ssh -vvv deploy@10.0.1.20 |
실전 예제
1. 접속하지 않고 원격 명령만 실행한다. 서버 여러 대의 상태를 한 번에 훑을 때 쓴다.
for h in web01 web02 web03; do
echo "== $h"
ssh -o ConnectTimeout=5 "$h" 'uptime; df -h / | tail -1'
done
ConnectTimeout 이 없으면 죽은 서버 하나에서 반복문 전체가 멈춘다.
2. 방화벽 뒤 DB 에 로컬 툴로 붙는다. 실무에서 가장 많이 쓰는 터널이다. 아래를 실행한 뒤 DB 클라이언트에서 127.0.0.1:13306 으로 접속하면 원격 MySQL 에 연결된다.
ssh -fN -L 13306:127.0.0.1:3306 deploy@bastion.example.com
여기서 127.0.0.1:3306 은 배스천 입장에서 본 주소다. DB 가 별도 서버라면 -L 13306:10.0.2.15:3306 처럼 적는다. 끝낼 때는 pkill -f 'ssh -fN -L 13306' 로 정리한다.
3. 배스천을 거쳐 한 번에 들어간다. 두 번 로그인할 필요가 없다.
ssh -J deploy@bastion.example.com app@10.0.1.20
4. 설정 파일에 별칭을 만들어 손을 줄인다. ~/.ssh/config 에 적어두면 ssh prod-web 한 줄로 끝나고, scp·rsync·git 도 같은 설정을 그대로 쓴다.
Host bastion
HostName bastion.example.com
User deploy
IdentityFile ~/.ssh/id_ed25519
Host prod-web
HostName 10.0.1.20
User app
Port 22022
ProxyJump bastion
ServerAliveInterval 30
ServerAliveCountMax 3
ServerAliveInterval 은 유휴 세션이 NAT 타임아웃으로 조용히 끊기는 것을 막는다. 장시간 작업하는 서버에는 반드시 넣는다.
5. 비밀번호 없이 키로 접속하도록 등록한다. 자동화의 전제 조건이다.
ssh-keygen -t ed25519 -C "deploy@$(hostname)"
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@10.0.1.20
함정과 주의점
개인키 권한이 틀리면 접속 자체가 거부된다. 키 파일을 복사해 오면 권한이 644 로 붙는 경우가 많고, 그러면 이렇게 나온다.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/deploy/.ssh/id_ed25519' are too open.
chmod 600 ~/.ssh/id_ed25519 로 고친다. 서버 쪽 ~/.ssh 는 700, authorized_keys 는 600 이어야 한다. 키를 넣었는데 계속 비밀번호를 묻는다면 십중팔구 서버 쪽 권한 문제다.
호스트 키 경고가 떴을 때 습관적으로 known_hosts 를 지우지 않는다. REMOTE HOST IDENTIFICATION HAS CHANGED 는 서버를 재설치했을 때도 뜨지만, 중간자 공격일 때도 똑같이 뜬다. 서버를 재설치했거나 IP 를 재사용한 것이 확실할 때만 ssh-keygen -R 호스트명 으로 해당 항목만 지운다. 파일 전체를 날리면 다른 서버의 신뢰 정보까지 잃는다.
원격 명령 안의 변수는 로컬에서 먼저 확장된다. 아래 두 줄은 결과가 완전히 다르다.
ssh host "rm -rf $TARGET/*" # $TARGET 이 로컬에서 빈 값이면 rm -rf /* 가 된다
ssh host 'rm -rf $TARGET/*' # 원격의 $TARGET 을 쓴다
큰따옴표와 작은따옴표의 차이가 서버 하나를 날린다. 원격에서 평가돼야 할 변수는 반드시 작은따옴표 안에 둔다.
에이전트 포워딩(-A)은 아무 서버에나 켜지 않는다. 그 서버의 root 권한을 가진 사람이 내 키로 다른 서버에 접속할 수 있게 된다. 배스천 경유가 목적이라면 -A 대신 -J(ProxyJump)를 쓴다.
함께 보면 좋은 명령어
scp— 파일 한두 개를 빠르게 주고받는다. ssh 설정을 그대로 쓴다.rsync— 디렉터리 동기화와 재개 가능한 전송.-e ssh로 같은 통로를 탄다.tmux— 세션이 끊겨도 원격 작업이 살아남는다. 장시간 배포·마이그레이션에는 필수다.