Ошибка Host key verification failed в Ansible почти всегда связана не с самим Ansible, а с SSH-проверкой подлинности удалённого узла. На Debian и Ubuntu это особенно часто всплывает после пересоздания сервера, смены IP, перевыпуска инстанса из шаблона или повторного использования адреса, который раньше принадлежал другой машине.
В этот момент Ansible пытается подключиться по SSH, сверяет host key с локальным файлом known_hosts и останавливает выполнение. Для администратора это выглядит как внезапная поломка автоматизации, хотя на деле срабатывает штатная защита OpenSSH.
Отдельно пугает сообщение REMOTE HOST IDENTIFICATION HAS CHANGED. И пугать оно должно: SSH специально блокирует соединение, если ключ узла не совпадает с тем, который уже был сохранён раньше. Это защита от подмены сервера и атак посредника. Но в реальной эксплуатации причина часто вполне штатная — сервер переустановили, а запись в ~/.ssh/known_hosts осталась старой.
Особенно неудобно, когда Ansible работает по IP из inventory, а не по имени хоста. Тогда замена содержимого узла по тому же адресу мгновенно ломает деплой, bootstrap, обновления и любые playbook, которые полагаются на SSH.
Ниже разберём, как быстро найти конфликт, безопасно очистить старую запись, заново принять корректный ключ и в каких случаях можно временно смягчить host key checking, а в каких это плохая идея.
Почему Ansible показывает Host key verification failed
Когда Ansible подключается к удалённому узлу, он обычно использует системный клиент OpenSSH. Тот проверяет host key сервера — публичный ключ SSH-демона. Локально этот ключ хранится в ~/.ssh/known_hosts у пользователя, под которым запускается Ansible.
Если для конкретного хоста в known_hosts уже есть запись, SSH ожидает увидеть тот же самый ключ. Если сервер присылает другой ключ, клиент считает это потенциальной угрозой и завершает подключение.
Host key verification failed.
Или так:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Обычно причина одна из следующих:
- сервер переустановили, и у него появились новые SSH host keys;
- IP-адрес переиспользован для другой машины;
- вы раньше подключались по имени, а теперь по IP, или наоборот;
- в
known_hostsнакопились конфликтующие записи; - в inventory указан один адрес, а фактически трафик идёт на другой узел через NAT, floating IP или bastion;
- ключ действительно изменился нештатно, и это надо отдельно проверить.
Если ключ на боевом сервере сменился неожиданно, не удаляйте запись автоматически, пока не убедитесь, что это действительно ваш узел после пересоздания, миграции или reinstall.
Как понять, где именно конфликт: inventory, IP или known_hosts
Первый шаг — выяснить, к какому адресу Ansible реально подключается. Это не всегда очевидно: имя хоста в inventory и фактический адрес соединения могут отличаться. Например, узел называется web-01, а реальный адрес задан через ansible_host.
Проверьте inventory:
ansible-inventory --graph
ansible-inventory --host web-01
В первую очередь смотрите на ansible_host, ansible_user и, если используется нестандартный SSH-порт, на ansible_port. Если в inventory хранится IP, конфликт чаще всего привязан именно к нему.
Следом полезно повторить вход вручную от того же пользователя, под которым запускается Ansible:
ssh admin@203.0.113.10
Обычный SSH часто показывает более понятную ошибку, чем Ansible, включая строку в known_hosts, где лежит конфликтующая запись.
Для подробной диагностики используйте отладку:
ssh -vvv admin@203.0.113.10
Так вы увидите, какой именно файл known_hosts читается, какие типы ключей предлагает сервер и какая запись считается конфликтной.
Если вы часто пересоздаёте тестовые и боевые машины, удобнее держать их на отдельных VDS, где проще контролировать IP, DNS и жизненный цикл узлов. Для динамичной инфраструктуры это заметно снижает число сюрпризов с SSH-ключами.
Где Ansible берёт known_hosts на Debian и Ubuntu
В типичном случае используется файл ~/.ssh/known_hosts текущего пользователя. Если вы запускаете playbook вручную, проблема, скорее всего, будет именно в вашей домашней директории. Если же запуск идёт из CI, systemd unit, cron или через другой аккаунт, конфликтующий файл может лежать уже в другом $HOME.
Проверить это можно так:
whoami
echo $HOME
ls -la ~/.ssh
ls -la ~/.ssh/known_hosts
На Debian и Ubuntu этого обычно достаточно, чтобы быстро понять, тот ли файл вы вообще чините. Очень частая ошибка — удалить запись у своего пользователя, а затем снова получить ту же проблему из CI под сервисным аккаунтом.
Ещё один нюанс: современные версии OpenSSH часто хэшируют имена хостов в known_hosts. Поэтому запись может выглядеть не как IP или имя, а как непонятная строка. Это нормально. В таких случаях лучше пользоваться ssh-keygen, а не редактировать файл вручную.

Как безопасно удалить старую запись из known_hosts
Если вы уверены, что узел действительно был пересоздан или IP теперь принадлежит вашему новому серверу, удалите старую запись штатной утилитой ssh-keygen. Это самый надёжный способ, особенно если хосты в файле захэшированы.
ssh-keygen -R 203.0.113.10
ssh-keygen -R web-01.example.internal
Если SSH работает на нестандартном порту, указывайте адрес в квадратных скобках:
ssh-keygen -R [203.0.113.10]:2222
После удаления полезно убедиться, что старая запись действительно исчезла:
ssh-keygen -F 203.0.113.10
Если команда ничего не вернула, запись удалена. После этого можно либо принять новый ключ вручную при первом входе, либо заранее добавить его в known_hosts.
Как заново добавить корректный host key
Вариант 1. Подтвердить ключ вручную через SSH
Самый простой способ — один раз подключиться вручную:
ssh admin@203.0.113.10
SSH покажет fingerprint нового ключа и попросит подтвердить доверие. Но подтверждать его лучше не вслепую: сначала сверяйте fingerprint с тем, что видите на самом сервере через консоль провайдера, out-of-band доступ или другой доверенный канал.
На сервере отпечатки обычно смотрят так:
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub
После подтверждения запись появится в known_hosts, и Ansible снова сможет подключаться.
Вариант 2. Предзаполнить known_hosts через ssh-keyscan
Для массовой подготовки инфраструктуры удобен ssh-keyscan. Он считывает публичный host key без интерактивного входа:
ssh-keyscan -H 203.0.113.10 >> ~/.ssh/known_hosts
Если используется нестандартный порт:
ssh-keyscan -p 2222 -H 203.0.113.10 >> ~/.ssh/known_hosts
Но тут есть важный нюанс: ssh-keyscan сам по себе не доказывает, что перед вами именно нужный сервер. Поэтому в чувствительных средах сначала подтверждайте fingerprint доверенным способом и только потом добавляйте ключ автоматически.
ssh-keyscanотлично подходит для bootstrap и автоматизации, но безопасен только тогда, когда источник ключа дополнительно подтверждён.
Что делать в Ansible: inventory, ansible.cfg и host key checking
По умолчанию Ansible уважает SSH host key checking, и это правильное поведение. Параметр можно отключить, но делать это постоянно для всех окружений обычно не стоит.
Для разовой проверки временно отключить контроль можно так:
ANSIBLE_HOST_KEY_CHECKING=False ansible all -m ping
Либо через ansible.cfg:
[defaults]
host_key_checking = False
Такой подход иногда выручает при первичном bootstrap тестовой среды, но для production его лучше не оставлять. Вы перестаёте замечать подмену узла, а это уже не удобство, а потеря важной защиты.
Гораздо полезнее поддерживать inventory в аккуратном состоянии. Пример:
[web]
web-01 ansible_host=203.0.113.10 ansible_user=admin
web-02 ansible_host=203.0.113.11 ansible_user=admin
Если вы используете IP как ansible_host, то и записи в known_hosts будут завязаны на IP. При частой смене адресов или пересоздании серверов иногда удобнее использовать стабильные DNS-имена. Если вы как раз приводите в порядок адресацию, может пригодиться и регистрация доменов для предсказуемых имён хостов и сервисов.
Компромиссный вариант для контролируемой среды — автоматически принимать только новые хосты:
[all:vars]
ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new'
Режим accept-new лучше полного отключения проверок. Он автоматически принимает ключ только для ранее неизвестных хостов, но по-прежнему блокирует подключение, если уже известный ключ внезапно изменился.
Однако этот режим не решает проблему переиспользованного IP, если в known_hosts уже лежит старая запись. В таком случае её всё равно придётся удалить или обновить.
Автоматизация known_hosts в playbook и вне его
Если вы часто поднимаете новые узлы, лучше управлять известными ключами централизованно. В Ansible для этого есть модуль работы с known_hosts, но тут важно помнить ограничение: чтобы задача выполнилась на удалённой машине, к ней ещё надо сначала подключиться.
Именно поэтому проблему первого входа чаще решают не в основном playbook, а на этапе bootstrap: отдельным скриптом, CI-job, подготовкой control node или облачной инициализацией.
Практически рабочая схема выглядит так:
- получаете актуальный список адресов из inventory;
- собираете host keys для новых машин через доверенный канал или через
ssh-keyscanс валидацией; - обновляете
~/.ssh/known_hostsу пользователя, который запускает Ansible; - только после этого запускаете playbook.
Если у вас много меняющихся узлов, дополнительно полезно стандартизировать bootstrap и хранение секретов. На практике это хорошо сочетается с подходами, описанными в материале про безопасное хранение секретов для деплоя.
Если среда полностью динамическая, а IP часто переиспользуются, стоит подумать уже не только о файле known_hosts, а о более формализованной схеме доверия: SSH-сертификатах, стабильных DNS-именах, инвентаре из единого источника истины и чётком процессе пересоздания узлов.

Частые ошибки и ловушки
Ansible запускается не от того пользователя
Это одна из самых частых ситуаций: вручную ssh работает, а Ansible из CI — нет. Причина обычно в том, что CI-процесс использует другой $HOME и другой known_hosts. Всегда проверяйте, кто именно запускает playbook.
Конфликтуют имя и IP
Вы могли раньше подключаться к узлу по DNS-имени, а теперь Ansible ходит по IP. Или наоборот. Тогда в known_hosts могут существовать разные записи для одного и того же сервера.
ssh-keygen -F 203.0.113.10
ssh-keygen -F web-01.example.internal
Используется нестандартный порт
Для SSH на порту, отличном от 22, формат записи в known_hosts отличается. Искать и удалять её тоже надо с указанием порта:
ssh-keygen -F [203.0.113.10]:2222
ssh-keygen -R [203.0.113.10]:2222
DNS указывает не туда
Если в inventory указано имя, а DNS-кэш или запись ещё не обновились, вы можете получать ключ совсем другого сервера. Сначала проверьте, куда реально резолвится имя, и только потом чистите known_hosts.
Практический runbook для Debian и Ubuntu
Если нужен короткий рабочий сценарий без лишней теории, используйте такой порядок:
- Проверьте, какой адрес использует Ansible:
ansible-inventory --host ИМЯ_ХОСТА. - Попробуйте ручной вход:
ssh user@IP. - Убедитесь, что сервер действительно пересоздан или заменён.
- Сверьте fingerprint нового host key через консоль сервера или другой доверенный канал.
- Удалите старую запись:
ssh-keygen -R IP. - Добавьте новый ключ вручную через
sshили заранее черезssh-keyscan. - Повторите проверку:
ansible all -m ping.
Минимальный набор команд может выглядеть так:
ansible-inventory --host web-01
ssh-keygen -F 203.0.113.10
ssh-keygen -R 203.0.113.10
ssh admin@203.0.113.10
ansible web-01 -m ping
Когда можно отключить host key checking, а когда нельзя
Для одноразового тестового стенда временное отключение проверок можно считать инженерным компромиссом. Но даже в таком случае обычно лучше использовать StrictHostKeyChecking=accept-new, а не полное отключение.
Если речь идёт о production, административных серверах, bastion-хостах, доступе к базам и CI/CD с секретами, постоянное отключение проверок — плохая идея. Эта ошибка раздражает, но именно она помогает заметить подмену хоста до того, как проблема станет серьёзной.
Полезно относиться к known_hosts как к элементу инфраструктуры наравне с inventory, SSH-ключами доступа и правилами bootstrap. Тогда ошибка перестаёт быть случайной неприятностью и превращается в управляемый процесс.
Если вы параллельно пересобираете инфраструктуру или переносите сервисы между узлами, полезно заранее продумать и миграцию без простоя. В этом контексте может пригодиться материал про перенос сайта без даунтайма.
Итог
Ошибка Host key verification failed в Ansible на Debian и Ubuntu чаще всего появляется после смены IP, пересоздания сервера или конфликта записей в known_hosts. Самый безопасный путь — не отключать защиту, а корректно обновить информацию о host key: понять, к какому адресу подключается Ansible, проверить fingerprint, удалить старую запись через ssh-keygen -R и заново принять или добавить новый ключ.
Если проблема повторяется регулярно, решайте её не вручную, а через нормальную автоматизацию: аккуратный inventory, предзаполнение known_hosts, режим accept-new там, где он уместен, и понятный bootstrap для новых узлов. В такой схеме и SSH остаётся безопасным, и Ansible не ломается при каждом пересоздании хоста.


