서버를 새로 올린 뒤 ssh 가 막혔다면, known_hosts 는 이 순서로 손댄다
환경 OpenSSH 10.2 기준 · Linux · macOS
장비를 재설치하고 다시 붙으려는데 ssh 가 느낌표를 화면 가득 뱉으며 접속을 끊는다. 그 사이에 원격 호스트의 신원이 바뀌었다는 문장이 보인다. 여기서 known_hosts 를 통째로 비우거나 검사를 꺼 버리면 접속은 되지만, 그 서버가 정말 그 서버인지 확인할 방법까지 같이 버리게 된다. 순서만 알면 명령 두어 개로 끝나는 일이다.
먼저 이 경고가 무엇을 알려 주는지 읽는다
경고문은 길지만 정작 쓸모 있는 줄은 두 개다. 서버가 내민 키의 지문 한 줄, 그리고 충돌한 항목이 어느 파일 몇 번째 줄에 있는지 알려 주는 Offending 줄. 나머지는 위험을 알리는 문구라 읽고 넘겨도 된다.
Offending 줄의 경로를 흘려보지 않는 게 중요하다. 내 홈 디렉터리의 파일이 아니라 관리자가 배포한 시스템 전역 파일이 찍히는 경우가 있다. 그때는 홈 디렉터리를 아무리 뒤져도 문제의 줄이 없다.
이 상태에서 그냥 다시 시도해 봐야 소용없다. StrictHostKeyChecking 의 기본값은 ask 인데, 처음 보는 호스트는 사람에게 물어보고 추가하지만 키가 바뀐 호스트는 묻지도 않고 거부한다.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
The fingerprint for the ED25519 key sent by the remote host is
SHA256:....
Offending ED25519 key in /home/me/.ssh/known_hosts:14
Host key verification failed.겁주는 문구 사이에서 챙길 것은 지문 한 줄과 Offending 줄이다
지우기 전에, 왜 바뀌었는지 답할 수 있어야 한다
같은 경고가 전혀 다른 두 상황에서 똑같이 뜬다. 하나는 정상이다. 서버를 다시 설치했거나 이미지를 새로 구웠거나, 쓰던 주소를 다른 장비가 물려받은 경우. 다른 하나는 중간에 누가 끼어든 경우다. 뒤에 나올 명령은 두 경우 모두 똑같이 동작하니, 판단은 사람이 해야 한다.
그래서 손대기 전에 필요한 건 명령이 아니라 경로다. ssh 가 아닌 다른 길로 서버의 지문을 얻을 수 있어야 한다. 클라우드라면 콘솔 화면이나 인스턴스를 만들 때 남은 로그, 사내 장비라면 직접 붙은 모니터, 자동화로 찍어 내는 환경이라면 프로비저닝 기록.
그 경로가 열렸다면 서버에서 지문을 뽑아 적어 둔다. 키 종류별로 파일이 따로 있으니, 경고에 ED25519 라고 찍혔으면 그 키를 본다.
# 서버 쪽에서 (ssh 가 아닌 경로로 들어가서)
ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub
# 256 SHA256:.... root@web01 (ED25519)여기서 나온 지문이 접속 때 내민 지문과 같아야 다음 단계로 넘어간다
명령이 판단해 주지 않는다. 키가 바뀐 이유를 설명할 수 있을 때만 지운다.
걸린 줄은 눈이 아니라 명령으로 찾는다
경고가 줄 번호까지 찍어 주니 편집기로 열어 지우고 싶어진다. 그런데 파일을 열었을 때 호스트 이름이 안 보이는 경우가 있다. 이름과 주소를 해시로 바꿔 저장하는 설정이 켜져 있으면 줄이 세로 막대로 시작하는 암호 같은 문자열로 들어간다. 이러면 grep 도 소용이 없다.
ssh-keygen 의 -F 는 해시된 줄까지 찾아 준다. 찾으면 몇 번째 줄인지와 함께 그 줄을 그대로 뱉고, 없으면 아무것도 출력하지 않고 1 로 끝난다. 스크립트에서 조건문으로 쓰기도 좋다.
포트를 22 가 아닌 값으로 바꿔 쓰는 서버라면 이름만으로는 걸리지 않는다. 비표준 포트는 호스트를 대괄호로 감싸고 뒤에 포트를 붙인 형태로 저장되기 때문이다. 찾을 때도 그 형태 그대로 넣어야 한다.
ssh-keygen -F web01.example.com
# Host web01.example.com found: line 14
# web01.example.com ssh-ed25519 AAAAC3Nza...
# 22번이 아닌 포트로 붙는 서버라면
ssh-keygen -F '[web01.example.com]:2222'해시가 켜진 파일에서는 이름으로 grep 해 봐야 한 줄도 안 나온다
파일을 비우지 말고 그 줄만 걷어낸다
-R 은 지정한 호스트에 딸린 줄을 전부 걷어낸다. 지우기 전 원본을 같은 자리에 .old 를 붙여 남겨 두니, 엉뚱한 걸 지웠어도 되돌릴 수 있다.
이 대목에서 파일을 통째로 지우고 싶은 유혹이 온다. 그러면 그동안 쌓인 서버 수십 대의 기록이 같이 사라진다. 그다음부터는 어디에 붙어도 처음 보는 호스트로 뜨고, 뜰 때마다 yes 를 누르는 습관이 붙는다. 그 습관이 붙는 순간 호스트 키 검사는 이름만 남는다.
경고가 시스템 전역 파일을 가리켰다면 홈 디렉터리 쪽을 지워 봐야 그대로다. -f 로 그 파일을 직접 지정한다. 관리자 권한이 필요하고, 여러 사람이 함께 쓰는 파일이라 지우기 전에 담당자와 맞춰 보는 편이 낫다.
ssh-keygen -R web01.example.com
# /home/me/.ssh/known_hosts updated.
# Original contents retained as /home/me/.ssh/known_hosts.old
# 경고가 전역 파일을 가리켰다면
sudo ssh-keygen -R web01.example.com -f /etc/ssh/ssh_known_hostsOffending 줄이 가리킨 파일을 그대로 지정하는 게 핵심이다
새 키는 대조가 끝난 다음에 등록한다
줄을 걷어냈으니 이제 다시 붙으면 새 키를 받아 등록할 수 있다. 순서를 뒤집지 않는 게 전부다. 먼저 대조하고, 그다음 등록한다.
ssh-keyscan 은 서버가 내미는 공개키를 받아 known_hosts 에 그대로 넣을 수 있는 형태로 뱉는다. 파일에 붙이기 전에 그 결과에서 지문부터 뽑아, 앞에서 서버 쪽에서 적어 둔 값과 글자 단위로 맞춰 본다. 앞 네 글자만 보고 넘어가지 않는다.
값이 같으면 그때 파일에 붙인다. 다르면 붙이지 않고 서버 담당자를 찾는다. 한 가지 더. keyscan 도 접속과 똑같은 네트워크 경로를 탄다. 대조할 원본이 서버 쪽에서 직접 온 값이 아니라면 이 절차는 형식만 갖춘 셈이 된다.
ssh-keyscan -t ed25519 web01.example.com > /tmp/newkey
ssh-keygen -l -f /tmp/newkey
# 256 SHA256:.... web01.example.com (ED25519)
# 지문이 서버에서 적어 둔 값과 같을 때만
cat /tmp/newkey >> ~/.ssh/known_hosts대조를 건너뛰면 마지막 줄은 아무거나 믿고 등록하는 명령이 된다
네트워크로 받아 온 지문을 같은 네트워크로 받아 온 지문과 비교하면, 검증한 것이 하나도 없다.
제대로 됐는지 확인하는 방법
확인할 것은 두 가지다. 그 호스트 줄이 하나만 남았는지, 그리고 검사를 가장 엄격하게 켠 상태에서 붙는지.
접속할 때 StrictHostKeyChecking 을 yes 로 주면 자동 추가가 완전히 막힌 채 검사만 돈다. 이 상태에서 아무 경고 없이 명령이 실행되면 등록이 제대로 된 것이다. 반대로 여기서 또 걸리면 아직 다른 줄이 남아 있다는 뜻이다.
남은 .old 파일은 확인이 끝난 다음에 지운다. 확인 전에 치워 버리면 되돌릴 자리가 없어진다.
ssh-keygen -F web01.example.com # 한 줄만 나와야 한다
ssh -o StrictHostKeyChecking=yes [email protected] 'echo ok'경고 없이 ok 만 찍히면 끝난 것이다
여기서 자주 어긋난다
가장 흔한 건 검사를 꺼서 넘어가는 방법이다. StrictHostKeyChecking 을 no 로 주면 키가 바뀐 호스트에도 붙는다. 급할 때 손이 가지만 이건 고친 게 아니라 경고를 끈 것이다. 한 번 스크립트나 CI 설정에 박히면 나중에 진짜 이상한 일이 생겨도 조용히 지나간다.
대안으로 accept-new 를 아는 경우가 많은데, 이번 상황에는 도움이 안 된다. 이 값은 처음 보는 호스트를 물어보지 않고 등록해 줄 뿐, 키가 바뀐 호스트는 여전히 거부한다.
서버가 알려 주는 추가 호스트 키를 클라이언트가 알아서 받아 적는 동작에 기대는 것도 안 된다. 그건 인증이 끝난 다음에 오가는 이야기다. 키가 어긋나 접속 자체가 끊긴 상태에서는 시작조차 못 한다.
마지막은 이름만 지우고 주소를 빠뜨리는 경우다. IP 를 known_hosts 에 함께 넣는 설정은 기본으로 꺼져 있지만, 예전에 주소로 직접 붙은 적이 있으면 그 주소로 된 줄이 따로 남는다. 이름으로만 정리하면 주소로 붙을 때 같은 경고를 다시 만난다. -F 로 주소도 한 번 찾아 보고, 나오면 같이 걷어낸다.
ssh -o StrictHostKeyChecking=no [email protected] # 이렇게 넘기지 않는다
ssh-keygen -F 203.0.113.10 # 주소로 된 줄이 남았는지
ssh-keygen -R 203.0.113.10위는 하지 않는 쪽, 아래는 빠뜨리기 쉬운 쪽이다
조치 후 확인할 것
- 경고의 Offending 줄이 가리킨 파일이 홈 디렉터리 것인지 시스템 전역 것인지 확인했다
- 서버 쪽에서 직접 얻은 지문과 접속 때 내민 지문을 끝까지 맞춰 봤다
- ssh-keygen -F 로 그 호스트 줄이 하나만 남은 것을 확인했다
- IP 로도 붙는 서버라면 주소로 된 줄도 함께 정리했다
- StrictHostKeyChecking=yes 를 붙인 접속이 경고 없이 통과했다
호스트 키가 바뀌는 일 자체는 드물지 않다. 서버를 다시 올리면 당연히 바뀐다. 진짜 문제는 그게 정상인지 아닌지 판단할 근거를 남겨 두느냐다. known_hosts 를 통째로 비우는 손버릇이 붙으면, 언젠가 정말 이상한 화면을 만나도 똑같이 지우고 넘어가게 된다. 지울 줄은 한 줄이면 충분하다.
