SSH 가이드
OpenSSH 서버(sshd)와 클라이언트, 키, 파일 전송, 포트포워딩(-L · -R · -D), 점프 호스트
sshd가 포트를 열고 기다리고, 클라이언트의 ssh가 그쪽으로 붙는다. 터널을 열어도 이 방향은 같다.
1. 서버와 클라이언트
1-1. 누가 listen 하고, 누가 connect 하는가
서버는 sshd 데몬이다. 이름 끝의 d = daemon, 요청이 올 때까지 떠 있는 프로세스다. 기본으로 22/tcp에서 listen 하고, 설정 파일은 /etc/ssh/sshd_config다. 패키지는 OpenSSH server.
클라이언트는 ssh 명령이다. listen 하지 않는다. user@host로 connect 한다.
연결을 여는 화살표는 항상 내 PC에서 서버 22번으로 간다. 셸의 입출력은 그 위로 오간다.
서버 설치와 확인 (Ubuntu 계열)
sudo apt install openssh-server
systemctl status ssh
sudo systemctl enable --now ssh
ss -tlnp | grep :22
status에 active (running) 또는 소켓이 포트를 받고 있으면 서버가 떠 있는 것이다. ss의 :22 줄이 listen이다. 프로세스 이름이 안 보이면 sudo ss -tlnp | grep :22.
배포판·macOS에서 이름이 다를 때
- Ubuntu/Debian 유닛 이름은
ssh다. 프로세스 파일은sshd다. RHEL·Fedora·Arch는 유닛 이름이sshd인 경우가 많다.systemctl status ssh가 없으면systemctl status sshd. - Ubuntu 22.10 이후, 24.04 포함 많은 설치는
ssh.socket이 22번을 받고 접속이 올 때ssh.service를 띄운다. 접속 전에systemctl status ssh가 inactive여도systemctl status ssh.socket이 listening이면 정상일 수 있다. 출처는 Ubuntu Discourse의 소켓 활성화 안내. - 그 경우 부팅 시 켜 두는 명령은
sudo systemctl enable --now ssh.socket이다. 전통적인enable --now ssh도 배포판에 따라 통한다. 상태 확인으로 판별한다. - macOS 서버는 시스템 설정 → 일반 → 공유 → 원격 로그인. 이것도
sshd가 22/tcp를 연다.
1-2. 호스트 키와 사용자 키
열쇠가 두 종류다. 섞이면 known_hosts와 authorized_keys를 반대로 고치게 된다.
- 호스트 키 = 서버의 신원. 서버 파일
/etc/ssh/ssh_host_*. 클라이언트는 지문을~/.ssh/known_hosts에 기억한다. - 사용자 키 = 나의 신원. 개인키는 내 PC(
id_ed25519), 공개키만 서버~/.ssh/authorized_keys에 있다.
처음 접속하면 지문을 믿고 저장할지 묻는다. yes는 호스트 키를 known_hosts에 넣는 뜻이다. y만으로는 안 되고 yes를 친다. 사용자 비밀번호나 내 공개키와는 다른 질문이다.
서버가 챌린지를 보내고, 내 PC가 개인키로 서명한다. 서버는 authorized_keys의 공개키로 그 서명을 확인한다.
지문을 콘솔에서 대조하는 법
서버 모니터나 클라우드 콘솔처럼 SSH가 아닌 경로로 서버에 들어가, 호스트 공개키 지문을 본다.
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
클라이언트에 찍힌 SHA256: 지문과 같아야 yes다. 처음 보는 서버의 지문을 화면만 보고 습관적으로 yes 하지 않는다.
2. 기본 접속
ssh user@host
ssh -p 2222 user@host
ssh -i ~/.ssh/id_ed25519 user@host
ssh host 'uptime'
ssh -t host tmux attach
-p는 소문자. 포트. 기본 22이면 생략한다.-i는 그 개인키로 인증한다.ssh host 'uptime'은 셸을 붙잡지 않고 원격 명령 한 줄만 실행하고 끊는다.- 원격 명령을 줄 때는 기본으로 TTY가 없다.
tmux·top처럼 화면이 필요하면-t로 TTY를 강제한다. 세션이 없으면ssh -t host tmux new -As work. - 끝난 셸은
exit.
~. 이면 클라이언트 쪽에서 세션을 끊는다. ~?는 이스케이프 목록이다. 틸드는 줄의 맨 앞에서만 특수 문자다.
이스케이프 몇 가지
~.연결 종료~?목록~#포워딩된 연결 목록~~틸드 글자 그대로 전송
메뉴얼: OpenSSH ssh(1)의 ESCAPE CHARACTERS.
3. 키 인증
내 PC에서 키 쌍을 만든다. -C는 공개키 끝에 붙는 메모(주석)다. 암호가 아니다. passphrase는 개인키 파일을 잠그는 암호라서, 비우기보다 걸고 ssh-agent에 올리는 쪽이 낫다.
ssh-keygen -t ed25519 -C "메모"
~/.ssh/id_ed25519— 개인키. 내 PC 밖에 복사하지 않는다.~/.ssh/id_ed25519.pub— 공개키. 서버에 등록한다.
등록은 ssh-copy-id가 공개키를 서버 ~/.ssh/authorized_keys에 추가하고 권한도 맞춘다.
ssh-copy-id user@host
ssh-copy-id가 없는 환경이면 공개키를 직접 붙인다. 이 명령도 내 PC에서 실행한다.
cat ~/.ssh/id_ed25519.pub | ssh user@host \
'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
| 경로 | 어디 | 모드 |
|---|---|---|
~/.ssh | 내 PC, 서버 홈 모두 | 700 |
id_ed25519 개인키 | 내 PC | 600 |
authorized_keys | 서버의 그 사용자 홈 | 600 |
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
그룹이나 다른 사용자가 쓸 수 있는 개인키·authorized_keys·~/.ssh는 sshd가 무시한다. 그때 증상은 Permission denied (publickey)다.
ssh-agent
에이전트가 개인키를 메모리에 들고, ssh가 필요할 때마다 서명을 부탁한다. 접속마다 passphrase를 다시 치지 않아도 된다.
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
macOS는 키체인에 넣는다.
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
macOS 키체인 설정, 리눅스에는 넣지 말 것
UseKeychain은 Apple OpenSSH 전용이다. 리눅스 ~/.ssh/config에 넣으면 Bad configuration option으로 ssh가 거부할 수 있다.
Host *
UseKeychain yes
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
예전 macOS의 ssh-add -K는 --apple-use-keychain으로 바뀌었다. 지금 쓰던 옵션은 man ssh-add로 확인한다.
확인:
ssh -o PreferredAuthentications=publickey -i ~/.ssh/id_ed25519 user@host
서버 /etc/ssh/sshd_config(또는 그 드롭인)에 넣고 다시 읽힌다. 기존 세션은 reload로 끊기지 않는다.
PasswordAuthentication no
PermitRootLogin no
sudo systemctl reload ssh
유닛 이름이 sshd인 배포판은 sudo systemctl reload sshd.
설정이 안 먹는 경우 (Include, 키보드 인터랙티브)
OpenSSH는 같은 키워드의 먼저 나온 값을 쓴다. Ubuntu 클라우드 이미지는 sshd_config 맨 위의 Include /etc/ssh/sshd_config.d/*.conf가 먼저라, 50-cloud-init.conf 같은 파일의 PasswordAuthentication yes가 본문 아래의 no를 이긴다. 적용된 값만 믿는다.
sudo sshd -T | grep -Ei 'passwordauthentication|kbdinteractive|permitrootlogin'
비밀번호가 키보드 인터랙티브로 남아 있으면 KbdInteractiveAuthentication no도 같이 끈다. /run/sshd가 없어 sshd -t가 실패하는 소켓 활성 환경이면 sudo mkdir -p /run/sshd 후 다시 검사한다. 포트를 바꿨다면 소켓 활성 Ubuntu에서는 sudo systemctl daemon-reload 다음 sudo systemctl restart ssh.socket까지 해야 listen 포트가 바뀌는 경우가 있다.
4. ~/.ssh/config
호스트 별칭을 만들어 두면 ssh myserver만으로 사용자, 포트, 키, 점프, 포워딩이 붙는다. 파일은 내 PC의 ~/.ssh/config, 모드는 600.
chmod 600 ~/.ssh/config
Host myserver
HostName 203.0.113.10
User ubuntu
Port 22
IdentityFile ~/.ssh/id_ed25519
ProxyJump bastion
LocalForward 8080 localhost:8080
ServerAliveInterval 60
Host bastion
HostName bastion.example.com
User ubuntu
IdentityFile ~/.ssh/id_ed25519
Host *
AddKeysToAgent yes
ServerAliveInterval 30
ServerAliveCountMax 3
그 다음부터는 ssh myserver. LocalForward의 localhost도 명령행 -L과 같이 서버 입장의 localhost다. Host *는 맨 아래에 둔다.
먼저 나온 값이 이긴다, IdentitiesOnly
ssh_config도 키워드마다 처음 값만 쓴다. Host *를 파일 맨 위에 두고 User를 적으면, 아래의 호스트별 User는 무시된다.
키가 여러 개면 기본 키를 먼저 시도하다 MaxAuthTries에 걸릴 수 있다. 그 호스트에는 IdentitiesOnly yes를 넣어 IdentityFile만 쓰게 한다.
클라이언트에 적용된 결과를 보려면 ssh -G myserver. 개인키 내용이 아니라 설정 해석 결과다.
5. 파일 전송
scp, sftp, rsync -e ssh 모두 내 PC에서 명령을 실행한다. SSH 연결도 내 PC → 서버:22 다. 바뀌는 것은 파일의 방향이다.
위는 업로드, 아래는 다운로드. 실선(SSH)은 두 경우 모두 내 PC에서 서버로 나간다.
scp
# 로컬 → 원격
scp a.txt user@host:/tmp/
# 원격 → 로컬
scp user@host:/tmp/a.txt .
# 디렉터리. 포트는 대문자 P
scp -r -P 2222 ./site user@host:/var/www/
ssh -p 2222는 소문자다. scp -P 2222는 대문자다. scp -p는 포트가 아니라 타임스탬프·권한 유지다.
sftp
대화형이다. 프롬프트도 내 PC의 sftp 클라이언트다.
sftp user@host
sftp> get remote.txt
sftp> put local.txt
sftp> bye
rsync
rsync -avz -e ssh ./src/ user@host:/dst/
rsync -avz -e ssh user@host:/dst/ ./src/
-a는 아카이브(권한·시간 등), -v는 목록, -z는 전송 압축, -e ssh는 운반을 SSH로 한다. -P는 진행률과 이어받기(--partial --progress)지 포트가 아니다. 다른 포트는 -e "ssh -p 2222".
| 명령 (내 PC에서) | 방향 | 의미 |
|---|---|---|
scp a.txt user@host:/tmp/ | 로컬 → 원격 | 파일 업로드 |
scp user@host:/tmp/a.txt . | 원격 → 로컬 | 파일 다운로드 |
sftp 다음 put | 로컬 → 원격 | 대화형 업로드 |
sftp 다음 get | 원격 → 로컬 | 대화형 다운로드 |
rsync -avz -e ssh ./src/ user@host:/dst/ | 로컬 → 원격 | src 내용을 dst 안으로 |
rsync -avz -e ssh ./src user@host:/dst/ | 로컬 → 원격 | dst 아래 src 디렉터리가 생김 |
rsync -avz -e ssh user@host:/dst/ ./src/ | 원격 → 로컬 | 원격 내용을 로컬로 |
소스 경로 끝의 슬래시가 "그 디렉터리 자체"와 "그 안의 내용"을 가른다.
rsync --delete는 소스에 없는 파일을 목적지에서 지운다. 방향을 반대로 적으면 로컬이 지워질 수 있다. 먼저 --dry-run(-n)으로 목록만 본다.
6. 포트포워딩
문법은 세 가지다. bind 주소는 생략하면 보통 루프백이다.
ssh -L [bind:]로컬포트:목적지호스트:목적지포트 user@server
ssh -R [bind:]서버포트:목적지호스트:목적지포트 user@server
ssh -D [bind:]로컬포트 user@server
-L의 목적지 이름은 서버가 해석한다. -R의 목적지 이름은 내 PC가 해석한다. 연결 자체는 둘 다 내 PC → 서버:22.
6-1. -L 같은 포트 8080:localhost:8080
ssh -L 8080:localhost:8080 user@server
http://localhost:8080이고, 이 localhost는 내 PC다. 뒤의 localhost:8080은 서버가 자기 자신에게 접속할 주소다. 서버에서만 127.0.0.1:8080으로 떠 있는 Jupyter·웹앱에, 내 브라우저가 터널로 닿는다.
브라우저는 내 PC의 8080만 본다. 서버 sshd가 자기 127.0.0.1:8080으로 이어 준다.
| 명령 | 브라우저 (내 PC) | 서버가 접속하는 곳 |
|---|---|---|
-L 8080:localhost:8080 |
localhost:8080 |
서버의 localhost:8080 |
-L 9000:localhost:8080 |
localhost:9000 |
서버의 localhost:8080 |
포트를 바꾸면 내 PC에서 여는 번호만 바뀐다. 도착 앱의 포트는 뒤 숫자다. 앱이 IPv4로만 떠 있으면 목적지 호스트를 localhost 대신 127.0.0.1로 적는다.
6-2. -L 로 서버 너머 제3 호스트
ssh -L 5432:db.internal:5432 user@bastion
내 PC의 5432로 들어온 연결을, 배스천이 db.internal:5432로 넘긴다. 그 이름은 내 PC DNS가 아니라 배스천이 해석한다. 데이터베이스는 배스천에서만 보여도 된다.
실선은 내 PC에서 배스천 22번까지만 SSH다. DB로 가는 TCP는 배스천이 연다.
6-3. -R 리모트(역) 포워딩
ssh -R 9000:localhost:3000 user@vps
서버의 9000으로 들어온 요청이 내 PC의 localhost:3000으로 나간다. NAT 뒤의 개발 PC를, 밖에서 직접 포트포워드 하지 않고 보여주고 싶을 때 쓴다. SSH를 시작하는 쪽은 여전히 내 PC다. 요청만 반대다.
실선은 내 PC → VPS:22. 점선 요청은 VPS의 9000에서 내 PC의 3000으로 거꾸로 간다.
서버 sshd_config의 GatewayPorts 기본값은 no다. 그래서 -R 9000:...은 서버의 127.0.0.1:9000에만 묶인다. VPS 밖에서 vps:9000으로는 안 닿고, 서버 안에서 curl localhost:9000만 된다. 밖에 열려면 서버에서 GatewayPorts를 바꾸고 reload 한 뒤, 바인드 주소를 지정한다.
# 서버 sshd_config — clientspecified 가 클라이언트가 적은 주소만 허용
GatewayPorts clientspecified
sudo systemctl reload ssh
# 내 PC
ssh -R 0.0.0.0:9000:localhost:3000 user@vps
GatewayPorts no / yes / clientspecified
no(기본): 리모트 포워딩은 서버 루프백만. 클라이언트가0.0.0.0을 적어도 무시된다.clientspecified:-R에 적은 bind 주소를 서버가 존중한다.yes: 리모트 포워딩을 와일드카드에 묶는다. 범위가 더 넓다.
이 키워드는 서버 sshd_config다. 내 PC ~/.ssh/config의 GatewayPorts는 -L/-D용이라 다른 설정이다. 메뉴얼: sshd_config(5) GatewayPorts, ssh(1)의 -R.
6-4. -D 동적 SOCKS
ssh -D 1080 user@server
내 PC의 127.0.0.1:1080에 SOCKS5 프록시가 열린다. 브라우저는 목적지를 직접 열지 않고 이 프록시로 보내고, 서버가 그 TCP를 대신 연다. 고정된 목적지 포트가 없다.
브라우저 프록시만 127.0.0.1:1080으로 둔다. 실제 사이트 접속은 서버가 나간다.
브라우저 SOCKS5 호스트 127.0.0.1, 포트 1080. curl은 이름 해석을 프록시(서버)에 맡기는 옵션을 쓴다.
curl --socks5-hostname 127.0.0.1:1080 https://example.com
이 옵션 이름은 --socks5-hostname이다. 서버에서만 풀리는 내부 호스트 이름에 필요하다. --socks5만 쓰면 이름을 내 PC에서 먼저 찾는다.
6-5. 공통 옵션, 바인드 주소, 종료
ssh -N -f \
-o ExitOnForwardFailure=yes \
-o ServerAliveInterval=60 \
-L 127.0.0.1:8080:localhost:8080 user@server
-N— 원격 셸·명령을 실행하지 않는다. 터널만.-f— 인증 뒤 백그라운드. 보통-N과 같이 쓴다.ExitOnForwardFailure=yes— 포트를 못 잡으면 ssh가 끝난다. 이게 없으면 포워딩만 실패한 채 붙어 있을 수 있다.ServerAliveInterval— 이 초마다 연결 유지 패킷. 중간에 죽은 NAT 세션을 정리하는 데 쓴다.ServerAliveCountMax(기본 3)만큼 답이 없으면 끊는다.
bind를 비우거나 127.0.0.1로 두면 내 PC의 그 포트는 나만 쓴다. 0.0.0.0이나 -g는 같은 LAN의 다른 기기도 내 PC의 포워딩 포트로 들어올 수 있다. 공개 와이파이에서는 열지 않는다. -L/-D의 이 동작은 내 PC 설정이고, -R을 서버 밖에 여는 것은 서버 GatewayPorts다.
이미 쓰는 포트면 bind: Address already in use다. 번호를 바꾸거나, 그 포트를 가진 프로세스를 끝낸다.
ss -tlnp | grep 8080
lsof -i :8080
kill PID
포그라운드 터널이면 그 터미널에서 Ctrl-C, 또는 엔터 다음 ~.. -f로 사라진 프로세스는 ss/lsof의 PID를 kill한다.
1024 아래 포트, localhost와 127.0.0.1
listen 포트가 1024 미만이면 그 기계에서 root가 필요하다. 개발용은 8080, 9000, 1080처럼 높은 포트를 쓴다.
localhost가 ::1로 먼저 풀리고 앱은 127.0.0.1에만 떠 있으면 연결이 거절된다. 목적지 칸에 127.0.0.1을 적는다.
7. 점프 호스트
내부 서버가 내 PC에서 직접 안 보일 때, 배스천을 TCP 중계로 쓴다. 한 번의 명령으로 배스천을 거쳐 내부에 로그인한다.
ssh -J user@bastion user@internal
ssh -J user@a,user@b user@internal
-J a,b는 a 다음 b 다음 최종 호스트다. 설정 파일에서는 ProxyJump bastion 또는 ProxyJump bastion1,bastion2. 안쪽 SSH는 내 PC와 internal 사이에서 암호화되고, 배스천은 그 TCP를 중계한다.
바깥 연결은 배스천:22. 점선은 내 PC에서 내부 서버까지 가는 안쪽 SSH다.
ssh -A는 배스천이 내 ssh-agent로 서명하게 한다. 배스천 관리자는 그 세션 동안 내 자격으로 다른 서버에 들어갈 수 있다. 점프만 필요하면 -J / ProxyJump를 쓴다. -A는 그 호스트를 믿을 때만, Host 블록으로 한정한다.
ForwardAgent를 어쩔 수 없이 쓸 때
기본값은 전달 안 함이다. 필요할 때만 그 호스트에 ForwardAgent yes. 전역 Host *에 넣지 않는다. ssh-add -c는 서명을 쓸 때마다 확인을 묻게 할 수 있다(에이전트·데스크톱에 따라 동작이 다르다).
8. 치트시트
실행 위치가 서버인 줄만 서버에서 친다. 나머지는 내 PC다. SSH를 여는 방향은 내 PC → 서버다.
| 명령 | 어디서 실행 | 어디 → 어디 | 용도 |
|---|---|---|---|
ssh user@host | 내 PC | 내 PC → 서버:22 | 로그인 셸 |
ssh -p 2222 user@host | 내 PC | 내 PC → 서버:2222 | 다른 포트. 소문자 p |
ssh -i ~/.ssh/id_ed25519 user@host | 내 PC | 내 PC → 서버:22 | 그 개인키로 인증 |
ssh host 'uptime' | 내 PC | 내 PC → 서버, 명령 한 줄 | 원격 명령 후 종료 |
ssh -t host tmux attach | 내 PC | 내 PC → 서버 TTY | tmux에 붙기 |
~. / ~? | SSH 세션, 줄의 맨 앞 | 클라이언트가 세션 종료 | 멈춘 연결 끊기 / 도움말 |
ssh-keygen -t ed25519 -C "메모" | 내 PC | 로컬에 키 쌍 생성 | 개인키·공개키 만들기 |
ssh-copy-id user@host | 내 PC | 공개키 → 서버 authorized_keys | 키 등록 |
ssh-add ~/.ssh/id_ed25519 | 내 PC | 개인키 → ssh-agent | 에이전트에 적재 |
ssh myserver | 내 PC | config의 HostName으로 | 별칭 접속 |
scp a.txt user@host:/tmp/ | 내 PC | 로컬 파일 → 원격 | 업로드 |
scp -P 2222 user@host:/tmp/a.txt . | 내 PC | 원격 파일 → 로컬 | 다운로드. 포트는 대문자 P |
sftp user@host | 내 PC | put / get 양방향 | 대화형 전송 |
rsync -avz -e ssh ./src/ user@host:/dst/ | 내 PC | 로컬 내용 → 원격 디렉터리 | 동기화. 끝 슬래시 주의 |
ssh -L 8080:localhost:8080 user@server | 내 PC | 내 :8080 → 서버의 127.0.0.1:8080 | 같은 포트 로컬 포워딩 |
ssh -L 5432:db.internal:5432 user@bastion | 내 PC | 내 :5432 → 배스천 너머 DB | 제3 호스트. 이름은 서버가 해석 |
ssh -R 9000:localhost:3000 user@vps | 내 PC | 서버 :9000 → 내 PC :3000 | 역터널. 연결은 내 PC가 시작 |
ssh -D 1080 user@server | 내 PC | SOCKS → 서버가 임의 목적지 | 동적 프록시 |
ssh -J user@bastion user@internal | 내 PC | 내 PC → 배스천 → internal | 점프. 여러 단은 -J a,b |
ssh -N -f -o ExitOnForwardFailure=yes -L ... | 내 PC | 터널만, 백그라운드 | 셸 없이 포워딩. 실패 시 종료 |
ssh -v user@host | 내 PC | 디버그 로그를 내 터미널에 | 접속 실패 추적. 더 보면 -vvv |
ssh-keygen -R host | 내 PC | known_hosts에서 그 호스트 삭제 | 호스트 키 변경 후. 원인 확인 전제 |
systemctl status ssh | 서버 | 서버 로컬 systemd | sshd 상태. 배포판에 따라 sshd |
9. 트러블슈팅
Permission denied (publickey)
서버가 공개키 인증을 거절했거나, 제시한 키를 인정하지 않았다. 흔한 원인은 네 가지다.
- 공개키가 그 사용자의
authorized_keys에 없다. 사용자 이름이 틀리면 다른 홈을 본다. - 권한.
~/.ssh는700,authorized_keys와 개인키는600. 홈 디렉터리가 그룹 쓰기 가능이면 sshd가 키를 무시하기도 한다. - 다른 개인키.
-i또는 configIdentityFile이 등록된 공개키와 짝이 아니다. User가 의도와 다르다.ssh -G host의user줄을 본다.
클라이언트에서 ssh -v user@host로 시작하고, 부족하면 -vvv. 앞쪽 키 교환 덤프보다 끝부분이 원인이다.
# 따라갈 줄 (예시)
debug1: Connecting to host [203.0.113.10] port 22.
debug1: Offering public key: /home/me/.ssh/id_ed25519 ED25519
debug1: Server accepts key: ...
# 거절이면 대신 이런 결말
debug1: Authentications that can continue: publickey
user@host: Permission denied (publickey).
Offering public key에 기대한 파일이 없으면 -i가 안 먹은 것이다. Server accepts key 없이 끝나면 서버의 authorized_keys·권한·사용자를 본다. 서버 로그:
sudo journalctl -u ssh -n 50 --no-pager
유닛이 sshd면 -u sshd. Failed publickey와 Accepted publickey를 찾는다.
REMOTE HOST IDENTIFICATION HAS CHANGED
known_hosts의 호스트 키와 지금 서버가 내민 키가 다르다. 서버를 재설치하면 호스트 키가 바뀌어 이 경고가 난다. 같은 증상은 중간자 공격에서도 난다. 재설치·이미지 교체를 콘솔 등 다른 경로로 확인한 뒤에만 지운다.
ssh-keygen -R host
포트가 22가 아닐 때
known_hosts 키는 [host]:2222 형태다. 대괄호가 필요하다.
ssh-keygen -R '[host]:2222'
Connection refused 와 Connection timed out
| 메시지 | 의미 | 먼저 볼 곳 |
|---|---|---|
Connection refused |
그 IP까지는 갔고, 그 포트는 거절했다. listen 하는 프로세스가 없다. | 서버에서 systemctl status ssh(또는 sshd, ssh.socket). 포트가 22가 아니면 클라이언트 -p. |
Connection timed out |
응답이 없다. 패킷이 버려진다. | IP·보안 그룹·방화벽·경로. sshd를 고쳐도 타임아웃은 그대로인 경우가 많다. |
ssh -v의 Connecting to ... port N으로 붙으려는 주소와 포트를 확인한다. 여기서 기대한 포트가 아니면 config의 Port나 HostName이 다른 블록에 걸린 것이다.
-v 출력을 읽는 순서
Connecting to— 호스트와 포트가 의도한 값인가.identity file/Offering public key— 어떤 개인키를 꺼내는가.Server accepts key또는Authentications that can continue— 서버가 받았는가.- 마지막 줄의
Permission denied (...)— 괄호 안이 서버가 아직 허용하는 방법이다.
-vvv는 같은 흐름에 알고리즘 협상까지 붙는다. -v로 포트·키가 맞는지 본 다음에 올린다.
한눈에
sshd가 listen,ssh가 connect. 터널·점프·scp도 연결은 내 PC → 서버:22.- 호스트 키는
known_hosts, 사용자 공개키는authorized_keys. 개인키는 내 PC에만. -L 8080:localhost:8080의 앞은 내 포트, 뒤는 서버 자신의 포트.-R은 요청만 거꾸로. 밖에 열려면 서버GatewayPorts.-D는 SOCKS.-J는 점프.-A는 에이전트를 그 서버에 맡긴다.- 비밀번호 로그인을 끄기 전에 키 세션을 하나 더 열어 둔다.
10. 출처
- OpenSSH ssh(1) —
-L-R-D-J-N-f이스케이프, 로컬 포트의 GatewayPorts. - OpenSSH sshd(8), sshd_config(5) —
GatewayPorts기본값no(루프백),PasswordAuthentication,PermitRootLogin. - OpenSSH ssh_config(5), ssh-keygen(1), ssh-copy-id(1).
- Ubuntu Discourse, SSHd socket activation (22.10+) — 24.04의
ssh.socket. 배포판·포인트 릴리스마다 유닛 구성은 다를 수 있으니systemctl status로 확인한다. - Apple, 원격 로그인 허용. macOS
ssh-add --apple-use-keychain은 그 플랫폼 메뉴얼 기준. - curl manpage —
--socks5-hostname. rsync 끝 슬래시와--delete는 rsync(1).