Skip to main content
RunBook Academy

AnsibleVI · Configuration and PrecedenceConfiguration and precedence

The settings you will be tempted to weaken

Advanced⏱ ~20 minansible-coreopenssh-client

What you'll learn

  • State exactly what host_key_checking = False disables and what it does not
  • Replace it with a managed known_hosts, an SSH certificate authority, or provisioning-time key capture
  • Detect a StrictHostKeyChecking=no override that HOST_KEY_CHECKING still reports as True
  • Judge deprecation warnings and retry files by what turning them off costs later

Prerequisites

Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11

Not yet marked complete on this device.

Four settings account for most of the security debt in Ansible configurations, and all four get changed for the same reason: something was in the way and turning it off made the run work.

This lesson takes each one, says precisely what it disables, names the incident it enables, and gives the compensating control — because “do not turn it off” is not useful advice to somebody whose run is failing right now.

host_key_checking = False

Read-only / Safethe default is on
$ ansible-config dump | grep '^HOST_KEY_CHECKING'
HOST_KEY_CHECKING(default) = True

What it actually disables

It disables authentication of the server. Nothing else.

Your traffic is still encrypted. Your key still authenticates you to the far end. What stops is the check that the machine answering on that address is the machine whose host key you recorded previously.

That distinction matters, because “it is still encrypted” is the argument people reach for, and it is true and irrelevant. An encrypted session with an attacker is an encrypted session with an attacker.

The incident it enables

The classic scenario is not a hypothetical MITM on the wire. It is this:

A production database host is re-provisioned. It comes up with a new host key. Ansible fails on it, because the key does not match known_hosts. Somebody sets host_key_checking = False in the project config, the run succeeds, and the setting stays.

Now consider the same symptom with a different cause. An attacker who can influence DNS, ARP or a jump host redirects that hostname to a machine they control. Ansible connects, sees a key that does not match, and — with checking disabled — proceeds. It authenticates with your key, runs your playbook, and hands the attacker whatever the playbook carries: vault contents, deployment credentials, sudo access on a machine they now control, and a confirmed inventory of your fleet.

Host key checking is the only mechanism that distinguishes those two cases. Disabling it means you have decided never to be able to tell them apart, permanently, for the convenience of a re-provisioning event that happens a few times a year.

StrictHostKeyChecking=no in ssh_args

This is the same weakening through a different door, and it is worse in two specific ways.

The default ssh_args on 2.21.3 is:

Read-only / Safewhat you are replacing
$ ansible-config dump -t connection | grep '^ssh_args'
ssh_args(default) = -C -o ControlMaster=auto -o ControlPersist=60s

Set ssh_args = -o StrictHostKeyChecking=no in [ssh_connection] and two things happen.

ssh_args replaces rather than appends. Verified on 2.21.3: the effective value becomes exactly -o StrictHostKeyChecking=no, and compression plus connection multiplexing are silently gone. Losing ControlMaster=auto and ControlPersist=60s means a fresh SSH handshake for every task on every host, which on a large fleet is a substantial and mystifying slowdown that arrives bundled with the security regression.

No audit sees it. This is the part worth remembering:

Read-only / Safethe setting that says everything is fine
$ ansible-config dump | grep '^HOST_KEY_CHECKING'
HOST_KEY_CHECKING(default) = True
Read-only / Safeand the usual review command shows nothing
$ ansible-config dump --only-changed
CONFIG_FILE() = /srv/estate/ansible.cfg

GALAXY_SERVERS:

Illustrative output

Both confirmed on 2.21.3. An estate carrying this override passes a review that greps for HOST_KEY_CHECKING, and passes a review that runs ansible-config dump --only-changed, while host key verification is off.

deprecation_warnings = False

Read-only / Safeon by default
$ ansible-config dump | grep '^DEPRECATION_WARNINGS'
DEPRECATION_WARNINGS(default) = True

Deprecation warnings are the only advance notice you get that an upgrade will break something. Each one names a construct that still works today and will not in some future release, which is precisely the information you need to plan an upgrade rather than to discover one.

Turning them off does not remove the deprecation. It removes the warning, and converts a planned migration into an outage on the day somebody upgrades ansible-core.

The temptation is real: one noisy deprecation from one third-party collection can produce hundreds of lines per run and drown output you need to read.

The compensating control is to fix or ticket the specific warning rather than to silence the category. If you must silence it while the ticket is open, do it per invocation — ANSIBLE_DEPRECATION_WARNINGS=False in the one job that is drowning — not in the project config, and record the expiry the same way an inventory exception carries one. A silenced warning with no ticket and no date is an upgrade you have decided to be surprised by.

retry_files_enabled

This one is worth correcting, because the advice you will read online predates the current default:

Read-only / Safeoff by default now
$ ansible-config dump | grep -E '^RETRY_FILES'
RETRY_FILES_ENABLED(default) = False
RETRY_FILES_SAVE_PATH(default) = None

So this is not a setting you are tempted to weaken — it is one you are tempted to enable, usually after a partial failure, so that a rerun can target only the hosts that failed with --limit @site.retry.

Two reasons to be careful.

It writes a fleet host list to disk. The .retry file contains the names of the hosts that failed, written next to the playbook by default — which for a shared controller may be a directory other people can read. That is an inventory disclosure, small on its own and useful to anybody enumerating your estate.

A retry file is a stale target set. It records which hosts failed at the moment the run ended. By the time you rerun, some may have recovered, others may have been fixed by hand, and the set may no longer correspond to anything meaningful. Re-running against a saved list is a change with a blast radius nobody re-derived.

If you enable it, set retry_files_save_path to a directory only the automation account can read, and treat the file as evidence — a record of which hosts failed, to be read — rather than as an input to be piped back into --limit. When you do rerun, re-derive the target set from the current state of the fleet.

Knowledge check

Knowledge check · 4 questions

  1. Q1. What exactly does host_key_checking = False disable?

  2. Q2. Setting ssh_args = -o StrictHostKeyChecking=no in [ssh_connection] on 2.21.3 has which effects? Select all that apply.

  3. Q3. An SSH certificate authority removes the operational problem that motivates disabling host key checking, rather than trading the check away for convenience.

  4. Q4. On ansible-core 2.21.3, what is the correct statement about retry_files_enabled?

Passing score: 75%. Answers are checked in this browser.