BlueByte
Host key verification failedFixed

SSH: Host key verification failed

작성 Haneul Seo2026년 9월 7일 업데이트8 min

안녕하세요, BlueByte입니다. 서버에 ssh로 접속하려는데 — 또는 SSH로 git push하려는데 — 프롬프트 대신 Host key verification failed가, 때로는 원격 신원이 바뀌었다는 해시 경고 아래에 뜹니다. 기기가 망가진 것도, 반드시 공격인 것도 아닙니다. SSH가 제 일을 하는 것뿐입니다 — 서버가 내민 key가 지난번에 기록해 둔 것과 다른 것입니다. 오늘은 이 메시지의 두 형태, key가 더 이상 맞지 않는 이유, 새 key를 믿어도 되는지 확인하는 법, 오래된 항목을 지우는 법, 그리고 재발을 막는 법까지 하나씩 짚어보겠습니다.

둘 다 "Host key verification failed"로 끝나는 두 메시지

두 가지 다른 상황이 이 오류를 냅니다. 첫째는 바뀐 key입니다:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
...
Offending ECDSA key in /home/you/.ssh/known_hosts:12
Host key verification failed.

중요한 줄은 Offending ... key in ~/.ssh/known_hosts:12입니다 — 더 이상 맞지 않는, 기록된 key의 정확한 파일과 줄 번호를 알려줍니다. 둘째는 엄격 검사 상태에서의 첫 접속으로, 스크립트와 CI에서 흔합니다:

No ECDSA host key is known for github.com and you have requested strict checking.
Host key verification failed.

여기엔 충돌이 없습니다 — SSH가 그 호스트 기록이 없을 뿐이고, StrictHostKeyChecking이 스스로 검증할 수 없는 key를 받아들이지 못하게 막는 것입니다.

known_hosts가 막아 주는 것

처음 접속할 때 SSH는 서버의 공개 host key를 ~/.ssh/known_hosts에 저장합니다(최초 신뢰, trust on first use). 이후 접속마다 서버가 내미는 key를 그 기록과 비교합니다. 불일치는 트래픽이 가로채질 때 나타날 바로 그 조건이므로, SSH는 새 key를 조용히 믿는 대신 거부합니다. 그래서 해결책은 "검사를 끄는 것"이 절대 아닙니다 — 새 key가 진짜임을 확인한 뒤 기록하는 것입니다.

key가 더 이상 맞지 않는 이유

대부분은 악의가 아니라 평범한 이유입니다.

  • 서버 재구축·재프로비저닝 — OS를 새로 깔면 host key가 새로 생성되어, 같은 이름이나 IP가 이제 다른 key로 응답함.
  • IP·호스트명 재사용 — 클라우드 인스턴스가 반납됐다 내게 다시 할당되거나, DHCP가 그 주소를 다른 기기에 줌.
  • 제공자의 key 교체 — 예를 들어 GitHub은 2023년에 RSA host key를 교체했고, 옛 key를 캐시해 둔 모두가 이 오류를 만남.
  • 생각한 것과 다른 호스트에 닿음 — 이름 앞의 로드밸런서·bastion·jump host.

엄격 검사 형태는 그저 기록한 적 없는 호스트인데, 대화형 "정말 접속할까요?" 프롬프트가 금지된 환경이라는 뜻입니다.

먼저, 문제의 항목을 찾기

파일 전체를 지우지 마세요. ssh-keygen -F로 저장된 key를 정확히 찾습니다:

ssh-keygen -F github.com

known_hosts에서 일치하는 줄을 출력합니다. 오류에 Offending ... :12 줄이 이미 있다면 같은 정보입니다 — ~/.ssh/known_hosts의 12번째 줄입니다.

믿기 전에 새 key를 검증하기

이건 건너뛰면 안 되는 단계입니다. 서버의 현재 지문을 읽어, 서버 자신이 아니라 믿을 수 있는 출처와 대조합니다:

ssh-keyscan github.com | ssh-keygen -lf -

호스트가 지금 내미는 SHA256 지문을 출력합니다. 이를 제공자가 공표한 값과 비교하세요 — 예를 들어 GitHub은 자사 지문(Ed25519 key는 SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU)을 문서에 실어 둡니다. 직접 재구축한 서버라면 그 장비에서 ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub로 지문을 읽으세요. 지문이 맞으면 새 key는 진짜입니다.

오래된 key를 지우고 다시 접속하기

검증했으면 옛 항목을 지우고 SSH가 새 key를 기록하게 둡니다:

ssh-keygen -R github.com

-R은 그 호스트의 모든 key를 known_hosts에서 제거하고 known_hosts.old로 백업을 남깁니다. 다시 접속하면 SSH가 새 key 수락을 묻습니다. 엄격 검사 상황이라면 검증한 key를 직접 추가하세요:

ssh-keyscan github.com >> ~/.ssh/known_hosts

지문을 확인한 key만 덧붙이세요 — ssh-keyscan만으로는 응답하는 무엇이든 그대로 믿습니다.

실제 사례: 같은 IP에 재구축된 서버

10.0.4.20의 스테이징 장비를 다시 이미징했습니다. 다음 ssh deploy@10.0.4.20에서 REMOTE HOST IDENTIFICATION HAS CHANGED 블록이 Offending ED25519 key in ~/.ssh/known_hosts:8과 함께 떴습니다. 재구축을 제가 직접 했기에, 콘솔에서 ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub로 새 지문을 읽고, 노트북에서 ssh-keygen -R 10.0.4.20을 실행하고, 다시 접속해, 내밀어진 지문을 콘솔의 것과 비교한 뒤 수락했습니다. 1분도 안 걸렸고, 검사는 제 할 일을 정확히 했습니다 — 신원이 바뀌었음을 알리고, 그 변경이 제 것임을 확인하게 만든 것이죠.

"Permission denied (publickey)"·"Connection refused"와 어떻게 다른가

Host key verification failed는 서버의 신원에 관한 것으로, 인증 이전에 SSH가 서버가 누구인지 확인할 때 일어납니다. Permission denied (publickey)는 반대 방향입니다: 믿는 서버에는 닿았지만 로그인에 내 key가 받아들여지지 않은 것입니다. Connection refused는 그보다 더 앞입니다 — SSH 포트에서 아무도 응답하지 않아, 확인할 key 자체가 없었던 것입니다. 셋 중 무엇을 받았는지 읽으면 핸드셰이크의 어디를 봐야 할지 알 수 있습니다.

관련 질문

그냥 ~/.ssh/known_hosts를 지우면 안 되나요?

지울 수는 있지만, 그러면 모든 호스트의 기록된 신원을 버리고 그들 전체의 보호를 끄게 됩니다. ssh-keygen -R hostname으로 문제의 호스트만 제거하세요.

이 오류는 항상 공격인가요?

거의 아닙니다. 재구축된 서버, 재사용된 IP, 벤더의 key 교체가 훨씬 흔한 원인입니다. 지문을 확인하며 진지하게 다루되, 최악을 단정하지는 마세요.

known_hosts 항목이 임의의 해시처럼 보입니다 — 맞는 줄을 어떻게 찾나요?

그건 해시된 호스트명입니다(HashKnownHosts yes). ssh-keygen -F hostname은 해시된 항목도 검색해 일치하는 것을 출력하고, ssh-keygen -R hostname은 그것을 제거하므로, 해시를 직접 읽을 필요가 없습니다.

CI가 엄격 검사 형태에 걸리지 않게 하려면?

StrictHostKeyChecking을 끄는 대신, 파이프라인 준비 단계에서 검증된 key로 known_hosts를 미리 채우세요 — 지문을 한 번 확인한 뒤 ssh-keyscan host >> ~/.ssh/known_hosts.

StrictHostKeyChecking no로 설정해 오류를 없애도 되나요?

안 됩니다 — 그러면 어떤 key든 조용히 수락해, 이 오류가 주는 바로 그 보호를 없앱니다. 대신 올바른 key를 검증해 기록하세요.

참고 자료

Haneul Seo

Infrastructure engineer · 10+ years running Linux fleets

같은 카테고리 다른 글

Start request repeated too quicklyFixed

systemd: Start request repeated too quickly

systemd는 StartLimitIntervalSec(기본 10초) 안에 StartLimitBurst(기본 5회)보다 많이 시작된 유닛의 시작을 거부하며, Restart=도 이 제한에 포함됩니다. 기본 RestartSec 100ms로는 크래시하는 서비스가 1초도 안 되어 다섯 번을 소진합니다. 저널에서 진짜 크래시를 찾아 고치고, reset-failed를 실행한 뒤, RestartSec로 재시작에 여유를 주세요.

systemd
Too many authentication failuresFixed

SSH: Received disconnect ... Too many authentication failures

agent가 서버가 허용하는 것보다 많은 키를 내밀고 있습니다. sshd가 들여다보는 공개키 하나하나가 MaxAuthTries 시도를 한 번씩 소모하고 — 기본 6회, 하드닝된 호스트에서는 3회인 경우가 많습니다 — 정작 맞는 키는 차례가 오기 전에 연결이 끊깁니다. IdentitiesOnly=yes와 명시적 IdentityFile로 키 하나를 고정하면 시도 횟수가 1로 떨어집니다.

OpenSSH
1722Fixed

Active Directory: 복제가 error 1722(The RPC server is unavailable)로 실패

RPC는 하위 계층이 연결에 실패했을 때 1722(0x6ba, RPC_S_SERVER_UNAVAILABLE)를 보고합니다. 따라서 진짜 원인은 RPC 자신이 아니라 DNS, 차단된 포트, 두 도메인 컨트롤러 중 한쪽의 호스트 설정입니다. repadmin이 어느 파트너가 실패하는지 알려주고, dcdiag /test:dns가 이름 해석을 배제하며, Test-NetConnection과 동적 포트 범위가 방화벽 문제를 가릅니다. 가장 흔한 실수는 TCP 135만 허용하고 49152–65535는 막아 둔 규칙입니다.

Windows Server Active Directory
Windows Server RDSFixed

Windows Server RDS: The remote session was disconnected because there are no Remote Desktop License Servers available to provide a license

120일 RD Licensing 유예 기간이 끝났는데 세션 호스트가 쓸 수 있는 라이선스 서버가 없어 세션을 거부하는 상황입니다. GetGracePeriodDays 의 DaysLeft 가 0 이고 SpecifiedLSList 가 비어 있으면 몇 초 만에 확인됩니다. 해결은 활성화된 라이선스 서버와, 호스트 버전을 감당할 만큼 새 CAL 입니다. 2019 CAL 은 2022 세션 호스트를 서비스하지 못합니다. 여기에 배포 또는 Licensing 정책 설정과 두 서버 사이의 RPC 포트 개방이 따라옵니다.

Windows Server RDS
0x80070035 / Event ID 31017Workaround

Windows 11: NAS 공유 폴더를 열 때 "조직의 보안 정책이 인증되지 않은 게스트 액세스를 차단" (0x80070035)

Windows 10 Enterprise/Education/Pro for Workstations, Windows 11 Pro, Windows Server 2019 이후의 SMB 클라이언트는 기본적으로 게스트 로그온을 거부하고, Windows 11 24H2 Enterprise/Pro/Education 은 게스트 세션이 할 수 없는 SMB 서명까지 요구합니다. 그래서 게스트 접근만 제공하는 NAS 공유는 'block unauthenticated guest access' 대화상자, Error code 0x80070035, 또는 System error 3227320323 으로 실패하고, SmbClient/Security 로그에 Event ID 31017 'Rejected an insecure guest logon' 이 남습니다. Microsoft 가 권하는 해결은 NAS 에 실제 계정을 만들고 펌웨어에서 서명을 지원하게 하는 것입니다. Set-SmbClientConfiguration -EnableInsecureGuestLogons $true(24H2 는 -RequireSecuritySignature $false 까지)는 비상구이며, 그 클라이언트의 서명과 암호화를 포기하는 대가가 있습니다.

Windows (SMB client)
E: Could not get lock /var/lib/dpkg/lock-frontendFixed

Ubuntu/Debian: E: Could not get lock /var/lib/dpkg/lock-frontend — 누가 쥐고 있고 어떻게 기다리는지

다른 패키지 관리자, 대개 부팅 때 persistent systemd 타이머로 발화한 Ubuntu 의 unattended-upgrades 가 여러분의 apt-get 이 도는 동안 dpkg 프런트엔드 락을 쥐고 있는 것입니다. Ubuntu 는 apt 바이너리에만 binary::apt::DPkg::Lock::Timeout "120" 을 실어 두어서 apt-get 은 즉시 포기하고 apt 는 기다립니다. 메시지의 PID 를 읽고, 실행이 끝나게 두거나 apt-get 에 -o DPkg::Lock::Timeout=<초> 를 주고, dpkg --configure -a 는 실제로 중단된 실행 뒤에만 쓰고, 락 파일은 절대 지우지 마세요. 보유자가 종료하면 커널이 푸는 fcntl 락입니다.

APT (Ubuntu/Debian)