Fix Ansible Missing sudo password and Host key verification failed
Ansible Missing sudo password and Host key verification failed are separate trust-boundary failures that can appear in the same run. Host key verification happens before remote login and protects the server identity; SSH authentication proves the automation identity; become authentication authorizes privilege escalation after login. This Guide separates those layers, starts with read-only evidence, verifies changed host keys out of band, and restores interactive or noninteractive become without disabling security controls.
Prove the selected inventory host, address, SSH identity, host key, and sudo policy; repair only the failed boundary; then demonstrate an unprivileged Ansible ping, a bounded become check, check-mode execution, and a repeat run without weakening host verification or exposing credentials.
- Ansible control node ansible-core 2.18.x, ansible-core 2.19.x, ansible-core 2.20.x and current supported releases
- Managed node Linux with OpenSSH and sudo, systemd-based and non-systemd Linux distributions
- Controller operating system Linux, macOS, Windows through WSL 2; native Windows is not an Ansible control node
- Authoritative inventory Use the exact inventory source, limit expression, variables, and configuration revision from the failed run. Similar hostnames can point to different systems.
ansible-inventory -i {{inventoryPath}} --graph - Independent host-key evidence Have console, provider, CMDB, deployment record, or a trusted administrator available to confirm the target's current SSH host-key fingerprint. Network collection alone is not authentication.
Record the expected SHA256 fingerprint before changing known_hosts. - Unprivileged SSH identity Know the intended remote user and private-key or agent source. Do not start with sudo because become cannot work until normal SSH succeeds.
Confirm {{remoteUser}} and the approved identity file or agent. - Sudo policy owner Know who can approve changes to sudoers, automation credentials, or a secret manager. This Guide does not justify broad NOPASSWD privileges.
Identify the policy owner and maintenance window. - Secret-safe evidence location Verbose Ansible and SSH output can expose usernames, inventory paths, addresses, and secret locations. Store sanitized output outside public tickets and source control.
Confirm the evidence path is access controlled and excluded from Git.
OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.
Full guide
What you will build
- A layer-by-layer evidence bundle for Ansible Missing sudo password and Host key verification failed.
- An authenticated known_hosts repair process that never trusts ssh-keyscan by itself.
- Separate proofs for inventory selection, SSH identity, unprivileged module transport, sudo policy, and become authentication.
- An interactive recovery path for an operator-run play and a secret-safe noninteractive path for automation.
- A bounded sudo policy decision that avoids NOPASSWD: ALL and unsafe wildcard command rules.
- A canary, check-mode review, idempotency proof, rollback record, and prevention checklist.
- Explain why host verification happens before SSH user authentication and why become happens only after login.
- Classify merged multi-host output without applying one workaround to every host.
- Trace Ansible variable precedence to the effective host, address, user, key, and become configuration.
- Inspect hashed or port-specific known_hosts entries without deleting evidence.
- Authenticate a new SSH fingerprint through an independent channel.
- Keep host_key_checking and StrictHostKeyChecking enabled.
- Distinguish ansible_password from ansible_become_password.
- Use sudo -n -l to separate password requirement from missing authorization.
- Use -b and -K correctly for supervised work.
- Protect noninteractive become credentials with Vault or an approved broker.
- Design least-privilege sudo capability and verify idempotent recovery.
Architecture
How the parts fit together
Ansible reaches a managed Linux host through a chain of independent decisions. Inventory resolves the destination and remote identity. OpenSSH verifies the server host key, then authenticates the remote user and transports an Ansible module. Only after that can the become plugin ask sudo to run the module as another user. Credentials and errors from one boundary do not repair another.
- The operator or runner chooses an inventory source, playbook, limit, and configuration revision.
- Ansible resolves one logical inventory hostname to an address, port, remote user, connection plugin, and variables.
- OpenSSH receives the server's host key and compares it with known_hosts or a trusted host CA.
- If host identity cannot be verified, the connection stops with Host key verification failed before user authentication.
- After host trust succeeds, SSH proves the remote user with the configured identity.
- Ansible transfers and executes a module under that unprivileged remote account.
- A task with become asks the configured plugin, commonly sudo, to change to the become user.
- Sudo checks authorization and authentication policy for the remote account.
- If a password is required but not supplied, Ansible reports Missing sudo password.
- A safe repair changes only the failed boundary, then validates the full chain with a limited canary and repeat check.
Assumptions
- The managed node is Linux and privilege escalation uses sudo; network-device enable mode and Windows runas require separate procedures.
- The control node runs supported ansible-core on Linux, macOS, or WSL 2.
- The failure is reproduced from the authoritative inventory and project revision.
- An independent channel can confirm the current SSH host fingerprint.
- The responder has unprivileged SSH access or an identity owner who can restore it.
- Sudo changes require a policy owner and retained console access.
- Secrets are supplied through prompts, Vault, controller credentials, or an approved broker.
- No one will disable host-key checking or accept an unauthenticated key.
- The original playbook's application rollback is documented separately.
- Production rollout can begin with one canary host.
Key concepts
- inventory_hostname
- The logical name Ansible uses to select a host and find variables; it can differ from the connection address.
- ansible_host
- The actual DNS name or address used by the selected connection plugin.
- Host key verification
- OpenSSH server authentication using a pinned key or trusted host certificate before user credentials are accepted.
- known_hosts
- A user or system trust database mapping host tokens to approved public host keys or certificate authorities.
- ssh-keyscan
- A non-authenticating collector for public host keys; its output must be verified independently before trust.
- Remote user
- The account authenticated by SSH and used for the initial Ansible module session.
- become
- Ansible's privilege-escalation layer, commonly invoking sudo after a remote session exists.
- ansible_password
- A remote login password for the connection plugin; it is not the sudo password.
- ansible_become_password
- The credential for privilege escalation; it should be encrypted or brokered, never plaintext inventory.
- -K
- The --ask-become-pass option that prompts for a become password but does not itself enable become.
- -b
- The --become option that enables privilege escalation but does not itself supply a password.
- sudo -n
- Noninteractive sudo mode that fails immediately instead of prompting.
- Check mode
- A module-dependent prediction mode, not a universal sandbox or complete dry-run.
Fill these once. Every matching command and configuration block updates immediately; values stay in this page only.
Security and production boundaries
- Treat every changed host key as a possible interception until verified independently.
- Never use ssh-keyscan output as its own proof of authenticity.
- Do not disable host_key_checking, StrictHostKeyChecking, or known_hosts enforcement.
- Do not place SSH or become passwords in command-line arguments, plaintext inventory, Git, tickets, or logs.
- Keep ansible_password and ansible_become_password separate because they authenticate different boundaries.
- Ansible Vault protects ciphertext at rest but cannot prevent a task or callback from logging a decrypted value.
- Do not use NOPASSWD: ALL, unsafe wildcards, shells, interpreters, editors, or user-writable scripts in sudo rules.
- Limit diagnostics and repairs to one explicit host before scaling.
- Preserve old host keys and policy as incident evidence until the cause is understood.
- Rotate any credential that appeared in verbose output, diffs, shell history, or recordings.
- Keep console access while changing sudo policy.
- Review controller credential binding and runner filesystem persistence separately from an interactive shell.
Stop before continuing if
- The target inventory, hostname, address, port, or remote user is uncertain.
- The host fingerprint lacks an independent trusted source.
- DNS, provider console, CMDB, and SSH present conflicting identities.
- The fix requires disabling server authentication or automatically accepting keys.
- A password would be exposed in argv, plaintext inventory, Git, logs, or tickets.
- The account has unexpected broad sudo rights or the proposed rule includes NOPASSWD: ALL.
- No policy owner or console recovery path exists for sudo changes.
- The playbook limit selects more hosts than reviewed.
- The canary predicts destructive or unrelated changes.
- A third-party role or callback may print decrypted secrets.
- The control node and CI runner resolve different inventories or identities.
- Either literal error returns after the boundary-specific proof.
command
Capture the complete Ansible failure without leaking secrets
Re-run the smallest failing host and task with moderate verbosity. Preserve the exact ordering of messages. If Host key verification failed appears first, Ansible has not authenticated and sudo has not run. If Missing sudo password appears after a successful connection, SSH is working and the failure belongs to become. Avoid -vvvv unless a reviewer needs it because extreme verbosity can disclose paths, arguments, and environment details.
Why this step matters
The two literal errors identify different phases. Separating them prevents unsafe advice such as disabling host checking to solve a sudo password or changing sudoers to solve an SSH trust failure.
What to understand
Record the control-node hostname, UTC time, Ansible version, Git revision, inventory path, target pattern, limit, and task name.
Keep each target separate. One host can fail host verification while another reaches sudo and reports Missing sudo password.
Sanitize addresses, usernames, filesystem paths, and any variable values before sharing evidence.
If the playbook performs changes before the failing task, reproduce with a diagnostic play or check mode instead of replaying production mutations.
System changes
- No intended remote change; this step collects or validates evidence.
Syntax explained
-i {{inventoryPath}}- Selects the authoritative inventory source rather than an implicit default.
--limit {{targetHost}}- Restricts evidence collection to the reviewed host or group.
--start-at-task- Starts at the named task; use only when earlier tasks are not required to establish safe state.
-vv- Shows connection context and failure phase without the highest-volume secret exposure of -vvvv.
Values stay on this page and are never sent or saved.
ansible-playbook -i {{inventoryPath}} {{playbookPath}} --limit {{targetHost}} --start-at-task '{{taskName}}' -vvfatal: [web-01.example.net]: UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Host key verification failed.",
"unreachable": true
}
fatal: [web-02.example.net]: FAILED! => {
"msg": "Missing sudo password"
}Checkpoint: The failing boundary is classified
ansible-playbook -i {{inventoryPath}} {{playbookPath}} --limit {{targetHost}} --start-at-task '{{taskName}}' -vvContinue whenEach failed host is classified as inventory selection, host-key verification, SSH authentication, or become authentication.
Stop whenOutput includes unexpected targets, secret values, or an earlier state-changing task.
If this step fails
Both errors are copied into one generic incident
Likely causeOutput from several hosts or retries was merged.
Group messages by inventory_hostname and timestamp.Repeat against one explicit host.
ResolutionTreat every host and layer independently; do not apply one global workaround.
The run hangs at BECOME password
Likely causeThe play uses become and waits for interactive input.
Cancel with Ctrl+C.Confirm whether an operator-run -K flow is intended.
ResolutionUse the later interactive become step only after SSH identity and host trust are verified.
Security notes
- Never paste passwords into extra vars or the command line.
- Review callback output and CI artifacts for accidental secret disclosure.
Alternatives
- Use preserved CI output when reproducing the task would change production state.
Stop conditions
- The limit expands to more hosts than intended.
- The diagnostic requires replaying an unapproved write.
command
Prove the inventory host, address, user, port, and variable precedence
Inspect the inventory graph and the resolved host variables before touching SSH or sudo configuration. Ansible variables can come from inventory plugins, group_vars, host_vars, extra vars, environment, and configuration. The displayed host should match the intended machine, remote user, port, connection plugin, identity file, become method, and become user. Do not print encrypted or plaintext password values.
Why this step matters
A correct fix applied to the wrong host, user, port, identity, or environment is still an incident. Inventory proof anchors every later SSH and sudo conclusion.
What to understand
Confirm inventory_hostname separately from ansible_host; the first is Ansible's logical name and the second is the connection address.
Confirm the connection plugin is ssh for this procedure. Paramiko, network_cli, WinRM, and local connections have different host-key and privilege behavior.
Use ansible-config dump --only-changed to identify configuration overrides without dumping secrets.
Treat extra vars as highest-precedence inputs and inspect the original runner command or job template for hidden overrides.
System changes
- No intended remote change; this step collects or validates evidence.
Syntax explained
--graph- Shows groups and selected hosts without contacting managed nodes.
--host {{targetHost}}- Shows the merged variables Ansible resolved for one inventory hostname.
Values stay on this page and are never sent or saved.
ansible-inventory -i {{inventoryPath}} --graph && ansible-inventory -i {{inventoryPath}} --host {{targetHost}}@all:
|--@ungrouped:
|--@web:
| |--web-01.example.net
{
"ansible_host": "203.0.113.24",
"ansible_port": 22,
"ansible_user": "automation",
"ansible_become": true,
"ansible_become_method": "sudo",
"ansible_become_user": "root"
}Checkpoint: Connection identity is explicit
ansible-inventory -i {{inventoryPath}} --host {{targetHost}}Continue whenHost address, port, remote user, connection plugin, become method, and become user match the approved target.
Stop whenThe output selects an unexpected address, user, plugin, group, or environment.
If this step fails
Host pattern matches nothing
Likely causeWrong inventory source, group name, or limit expression.
Run --graph.Compare the failed runner's inventory argument.
ResolutionCorrect selection before any connection test.
Resolved variables differ from the inventory file
Likely causegroup_vars, host_vars, plugin data, configuration, or extra vars overrides the visible entry.
Use ansible-config dump --only-changed.Review the runner command and job template.
ResolutionRemove or document the unintended higher-precedence value.
Security notes
- Do not commit passwords such as ansible_password or ansible_become_password in inventory.
- Sanitize inventory addresses and usernames in public evidence.
Alternatives
- Use the automation controller inventory preview when local inventory plugins require unavailable credentials.
Stop conditions
- The inventory, target host, SSH user, control node, or known_hosts file is not proven.
- The presented SSH host-key fingerprint cannot be verified through an independent trusted channel.
- A proposed fix disables host-key checking, writes a password in plaintext, or grants unrestricted passwordless sudo.
- The target is production and another automation run is changing the same inventory or sudo policy.
command
Inspect the existing known_hosts decision before replacing anything
Query the exact known_hosts file used by the automation identity. A missing entry, a changed entry, and an entry for the wrong port are different cases. Do not delete an old key simply because a new scan differs. A changed fingerprint can indicate a legitimate rebuild, DNS or load-balancer change, stale inventory, or interception.
Why this step matters
Reading the file first preserves evidence and identifies hostname, address, and nonstandard-port forms that OpenSSH hashes or records separately.
What to understand
Run as the same operating-system account and HOME used by the Ansible process; service accounts and CI containers often have different known_hosts files.
Check system-wide known_hosts and SSH config only if the runner is configured to use them.
For hashed entries, ssh-keygen -F can search without revealing every hostname.
Record the old fingerprint with ssh-keygen -lf on a protected copy when incident policy requires comparison.
System changes
- No intended remote change; this step collects or validates evidence.
Syntax explained
-F- Searches known_hosts for a hostname or bracketed host-and-port token.
-f {{knownHostsPath}}- Selects the exact file used by the automation process instead of assuming the interactive user's default.
Values stay on this page and are never sent or saved.
ssh-keygen -F '{{targetHost}}' -f '{{knownHostsPath}}' && ssh-keygen -F '[{{targetHost}}]:{{sshPort}}' -f '{{knownHostsPath}}'# Host web-01.example.net found: line 18 web-01.example.net ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOldKeyMaterial # Host [web-01.example.net]:2222 found: line 27 [web-01.example.net]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICurrentKeyMaterial
Checkpoint: Existing trust evidence is preserved
ssh-keygen -F '{{targetHost}}' -f '{{knownHostsPath}}'Continue whenThe relevant entry is identified as present, absent, or mismatched without changing it.
Stop whenThe automation uses another HOME, container, account, or SSH UserKnownHostsFile.
If this step fails
No entry is found but Ansible reports a mismatch
Likely causeSSH uses an IP address, alias, bracketed port, system file, certificate authority, or different HOME.
Inspect the -vv SSH command in sanitized Ansible output.Run ssh -G to inspect effective UserKnownHostsFile.
ResolutionLocate the actual trust file and token before remediation.
Several keys exist for the host
Likely causeThe host offers several algorithms or stale entries remain.
Compare each fingerprint with authoritative records.Check address and port mappings.
ResolutionKeep verified active keys and remove only entries proven obsolete.
Security notes
- A host-key mismatch is an identity incident until independently explained.
- Do not post public keys, host mappings, or old fingerprints in a public ticket when they expose infrastructure topology.
Alternatives
- When OpenSSH host certificates are used, inspect the trusted CA marker and certificate principals instead of pinning one raw host key.
Stop conditions
- The inventory, target host, SSH user, control node, or known_hosts file is not proven.
- The presented SSH host-key fingerprint cannot be verified through an independent trusted channel.
- A proposed fix disables host-key checking, writes a password in plaintext, or grants unrestricted passwordless sudo.
- The target is production and another automation run is changing the same inventory or sudo policy.
command
Collect the offered key and authenticate its fingerprint out of band
Collect the current public host key with a short timeout, convert it to a SHA256 fingerprint, and compare that fingerprint with a trusted console, provider record, CMDB, provisioning log, or administrator who can inspect the host locally. ssh-keyscan is transport-only collection: its own manual warns that an unverified scan can enable a man-in-the-middle attack.
Why this step matters
The network can show which key a server presents, but only an independent channel can establish that the key belongs to the intended server.
What to understand
Prefer the modern Ed25519 fingerprint when supported, while retaining any approved fallback algorithm required by policy.
Compare exact SHA256 fingerprints character for character and record who verified them, by which channel, and at what UTC time.
If the host was rebuilt, confirm the change ticket, instance identity, IP assignment, and first-boot host-key generation record.
If DNS points to several legitimate nodes, manage each identity explicitly or use host certificates rather than accepting whichever key answers first.
System changes
- No intended remote change; this step collects or validates evidence.
Syntax explained
-T 5- Limits collection time so a stale or filtered address does not stall the procedure.
-p {{sshPort}}- Contacts the inventory-selected SSH port.
-t ed25519,rsa- Requests explicit supported host-key algorithms for fingerprint comparison.
ssh-keygen -lf -- Reads collected public keys from standard input and prints SHA256 fingerprints.
Values stay on this page and are never sent or saved.
ssh-keyscan -T 5 -p {{sshPort}} -t ed25519,rsa {{targetHost}} 2>/dev/null | tee /tmp/{{targetHost}}.hostkeys | ssh-keygen -lf -256 SHA256:3m2Uc9gJbrxQh5hN4poQJVYuKjMGFRb8oK60D0QvQHU web-01.example.net (ED25519) 3072 SHA256:c1D8nWJfEtQj2y6x7F4b0v9B4zjVxNl5y4YH7Jm8J4U web-01.example.net (RSA)
Checkpoint: The offered key is independently authenticated
ssh-keyscan -T 5 -p {{sshPort}} -t ed25519,rsa {{targetHost}} 2>/dev/null | ssh-keygen -lf -Continue whenAt least one offered fingerprint exactly matches an authoritative record.
Stop whenNo independent record exists, fingerprints differ, or DNS and console identify different systems.
If this step fails
The scan returns no key
Likely causeWrong address or port, routing, firewall, or sshd availability.
Confirm inventory and TCP reachability.Use console status without weakening SSH.
ResolutionResolve reachability as a separate incident.
The fingerprint differs after an alleged rebuild
Likely causeThe record is stale, the wrong instance answered, or the connection may be intercepted.
Inspect the server locally with ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub.Verify instance ID and DNS.
ResolutionStop until ownership is proven; do not replace known_hosts.
Security notes
- Never append ssh-keyscan output directly to known_hosts before independent verification.
- A public host key is not secret, but its association with a hostname is security-sensitive evidence.
Alternatives
- Use SSH host certificates signed by an approved CA to avoid per-host key distribution when the organization supports that model.
Stop conditions
- The fingerprint cannot be verified out of band.
- The same hostname resolves to unexpected or changing systems.
command
Replace or add only the independently verified host key
After the fingerprint is authenticated, make a protected backup, remove only the obsolete token, and install the verified key with the Ansible known_hosts module or a reviewed configuration-management path. Keep host_key_checking enabled. Do not use StrictHostKeyChecking=no, UserKnownHostsFile=/dev/null, ANSIBLE_HOST_KEY_CHECKING=False, or automatic acceptance as a shortcut.
Why this step matters
A narrow, reversible update restores trust while preserving proof of the previous key and leaving global verification controls intact.
What to understand
Use the bracketed host:port form for nonstandard ports and the plain hostname only for port 22.
Install the exact verified line from the protected collection artifact; do not perform a fresh unauthenticated scan during the write.
Preserve file owner, mode, and parent directory permissions for the automation account.
For fleets, prefer ansible.builtin.known_hosts with reviewed key material or an OpenSSH host CA rather than editing runner images manually.
System changes
- Creates a timestamped backup and removes one reviewed known_hosts token; adding the new key changes the automation account's trust store.
Syntax explained
ssh-keygen -R- Removes entries for exactly the supplied hostname token.
-f {{knownHostsPath}}- Updates the reviewed trust file rather than an implicit user file.
Values stay on this page and are never sent or saved.
cp '{{knownHostsPath}}' '{{knownHostsPath}}.bak-$(date +%Y%m%d%H%M%S)' && ssh-keygen -R '[{{targetHost}}]:{{sshPort}}' -f '{{knownHostsPath}}'# Host [web-01.example.net]:2222 found: line 27 /home/automation/.ssh/known_hosts updated. Original contents retained as /home/automation/.ssh/known_hosts.old
Checkpoint: Host verification succeeds without bypass
ssh -o BatchMode=yes -o StrictHostKeyChecking=yes -p {{sshPort}} {{remoteUser}}@{{targetHost}} trueContinue whenSSH accepts the independently verified host key; it may still report a separate user-authentication error.
Stop whenThe key changes again, an unexpected key type appears, or the command uses a bypass setting.
If this step fails
The mismatch remains after updating the hostname entry
Likely causeAnsible connects by IP, alias, jump host, or another port token.
Inspect the effective SSH destination with ssh -G.Compare ansible_host and ansible_port.
ResolutionVerify and manage the exact destination token; do not add every observed key.
CI works only with host checking disabled
Likely causeThe runner lacks a persistent trust bootstrap.
Inspect runner image and known_hosts mount.Confirm host CA or verified key distribution.
ResolutionProvision trust as a versioned runner dependency.
Security notes
- Keep StrictHostKeyChecking enabled.
- Restore the backup if the narrow edit targeted the wrong token, then investigate before retrying.
Alternatives
- Configure an OpenSSH @cert-authority entry and signed host certificates for scalable trust.
Stop conditions
- The approved fingerprint is not the key being installed.
- File permissions or ownership cannot be preserved.
command
Prove unprivileged Ansible connectivity before testing sudo
Run the built-in ping module without become. This validates inventory resolution, host trust, remote-user authentication, Python discovery, and module transport. It deliberately does not prove sudo. If it fails, keep the incident in the SSH layer and use the dedicated Permission denied (publickey) Guide rather than changing become settings.
Why this step matters
Privilege escalation is downstream from transport. A clean unprivileged proof prevents sudo troubleshooting from masking wrong keys, users, inventory, Python, or connection plugins.
What to understand
The ping module is not ICMP; it transfers and executes a small Python module on the managed node.
Use -e ansible_become=false only for this diagnostic override and remove it from normal runs.
If SSH reports Permission denied (publickey), follow /guides/troubleshoot-ssh-permission-denied-publickey.
If Python is absent, bootstrap or select the supported interpreter through a separate reviewed procedure.
System changes
- No intended remote change; this step collects or validates evidence.
Syntax explained
-m ansible.builtin.ping- Runs Ansible's read-only connection and Python capability test.
-e ansible_become=false- Disables become only for this diagnostic so SSH can be proven independently.
-vv- Shows connection selection without maximum debug output.
Values stay on this page and are never sent or saved.
ansible {{targetHost}} -i {{inventoryPath}} -m ansible.builtin.ping -e ansible_become=false -vvweb-01.example.net | SUCCESS => {
"ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" },
"changed": false,
"ping": "pong"
}Checkpoint: SSH and module transport work without become
ansible {{targetHost}} -i {{inventoryPath}} -m ansible.builtin.ping -e ansible_become=falseContinue whenThe target returns SUCCESS, changed false, and pong.
Stop whenAuthentication, host verification, interpreter, or unexpected target selection still fails.
If this step fails
Permission denied (publickey)
Likely causeWrong remote user, identity, agent, authorized_keys policy, or file permissions.
Inspect ansible_user and identity selection.Use the dedicated SSH public-key Guide.
ResolutionRepair SSH authentication before returning to become.
Python interpreter not found
Likely causeThe managed node lacks Python or inventory chooses the wrong interpreter.
Confirm distribution support and /usr/bin/python3 through approved access.Inspect ansible_python_interpreter.
ResolutionBootstrap a supported interpreter separately.
Security notes
- Do not switch to password SSH solely to make the test pass.
- Do not expose private-key paths or agent contents in shared logs.
Alternatives
- Use ansible.builtin.raw only in a dedicated Python-bootstrap procedure because it executes shell text directly.
Stop conditions
- The inventory, target host, SSH user, control node, or known_hosts file is not proven.
- The presented SSH host-key fingerprint cannot be verified through an independent trusted channel.
- A proposed fix disables host-key checking, writes a password in plaintext, or grants unrestricted passwordless sudo.
- The target is production and another automation run is changing the same inventory or sudo policy.
command
Inspect the remote sudo policy noninteractively
With unprivileged SSH proven, ask sudo to list the automation user's policy without prompting. The -n flag makes password requirements fail immediately instead of hanging an unattended job. Distinguish three outcomes: an allowed passwordless command set, a password requirement, or no authorization. Do not edit sudoers until the expected operational model is agreed.
Why this step matters
sudo -n -l reveals whether the account is authorized and whether a password is expected without consuming or exposing a password.
What to understand
A password-required result can be correct policy for interactive operator runs and incompatible with unattended CI unless a secret broker is configured.
A user-not-allowed result is authorization failure, not a missing credential; adding a password will not grant rights.
Review command aliases, Runas targets, environment restrictions, requiretty, and included sudoers files through the policy owner.
Test the exact remote user from inventory; sudo policy is account-specific.
System changes
- No intended remote change; this step collects or validates evidence.
Syntax explained
ansible.builtin.command- Executes argv without an extra shell on the managed node.
sudo -n- Uses noninteractive mode and fails rather than prompting.
sudo -l- Lists the invoking user's permitted commands and authentication requirements.
ansible_become=false- Prevents Ansible from trying privilege escalation around the diagnostic.
Values stay on this page and are never sent or saved.
ansible {{targetHost}} -i {{inventoryPath}} -m ansible.builtin.command -a 'sudo -n -l' -e ansible_become=falseweb-01.example.net | FAILED | rc=1 >>
sudo: a password is required
Alternative authorized output:
User automation may run the following commands on web-01:
(root) NOPASSWD: /usr/bin/systemctl status *, /usr/bin/journalctl --no-pager *Checkpoint: The expected sudo model is known
sudo -n -lContinue whenPolicy shows either an approved bounded NOPASSWD set or a documented password-required rule for the intended become user.
Stop whenThe account has unexpected ALL privileges, is not authorized, or policy ownership is unclear.
If this step fails
sudo: a password is required
Likely causeSudo policy requires authentication and the run is noninteractive.
Confirm operator-run versus automation-run design.Inspect approved credential delivery.
ResolutionUse -K interactively or an encrypted secret source for automation; do not place the password in inventory plaintext.
user is not allowed to execute sudo
Likely causeThe account lacks authorization or the wrong remote user is selected.
Reconfirm ansible_user.Ask the sudo policy owner to inspect included rules.
ResolutionGrant only the commands and Runas identity required by the playbook, after review.
Security notes
- Do not respond with NOPASSWD: ALL.
- Treat sudo -l output as sensitive authorization metadata.
Alternatives
- Split privileged operations into a tightly controlled deployment service when direct sudo from the automation account is not acceptable.
Stop conditions
- The inventory, target host, SSH user, control node, or known_hosts file is not proven.
- The presented SSH host-key fingerprint cannot be verified through an independent trusted channel.
- A proposed fix disables host-key checking, writes a password in plaintext, or grants unrestricted passwordless sudo.
- The target is production and another automation run is changing the same inventory or sudo policy.
command
Recover an operator-run play with an interactive become prompt
For a human-supervised run whose sudo policy intentionally requires a password, enable become and ask Ansible to prompt with --ask-become-pass (-K). The password is used for become on selected hosts; it is not the SSH login password and -K does not itself enable become. Keep the target limit narrow and verify the sudo user before running the original play.
Why this step matters
An interactive prompt keeps the become password out of command history and plaintext inventory while proving the expected sudo path.
What to understand
Use -b to enable become and -K to request its password; they are independent flags.
Ansible uses one prompted become password for all hosts in the run, so do not mix accounts with different credentials.
Limit the test to id -u and expect the numeric identity of the approved become user.
Cancel if the prompt or target differs from expectations; never type the password into a recorded terminal.
System changes
- sudo authentication is attempted and may update normal authentication logs; the id command does not change managed state.
Syntax explained
-b / --become- Enables privilege escalation for the module execution.
-K / --ask-become-pass- Prompts locally for the privilege-escalation password; it does not enable become by itself.
-a 'id -u'- Runs a bounded identity check rather than the original state-changing play.
Values stay on this page and are never sent or saved.
ansible {{targetHost}} -i {{inventoryPath}} -b -K -m ansible.builtin.command -a 'id -u'BECOME password: web-01.example.net | CHANGED | rc=0 >> 0
Checkpoint: Interactive become reaches only the approved identity
ansible {{targetHost}} -i {{inventoryPath}} -b -K -m ansible.builtin.command -a 'id -u'Continue whenThe target returns uid 0, or the documented uid for {{becomeUser}}, with no additional hosts.
Stop whenA different account, host, or privilege method is used, or the password is rejected repeatedly.
If this step fails
Missing sudo password remains
Likely cause-K was omitted, the runner cannot prompt, or become variables override the expected method.
Run from an interactive terminal.Inspect ansible_become_method and ansible_become_user.
ResolutionRestore the intended interactive context or use the automation-secret branch.
Incorrect sudo password
Likely causeThe SSH and become credentials were confused, the password rotated, or sudo uses another authentication policy.
Confirm the secret source with the account owner.Do not retry repeatedly and trigger lockout.
ResolutionUse the current become credential or approved recovery process.
Security notes
- Never supply the password with -e ansible_become_password=... on the command line.
- Avoid recordings, shared terminals, and debug callbacks while entering credentials.
Alternatives
- Use a controller credential object or approved secret broker for supervised jobs that should not expose a terminal prompt.
Stop conditions
- The inventory, target host, SSH user, control node, or known_hosts file is not proven.
- The presented SSH host-key fingerprint cannot be verified through an independent trusted channel.
- A proposed fix disables host-key checking, writes a password in plaintext, or grants unrestricted passwordless sudo.
- The target is production and another automation run is changing the same inventory or sudo policy.
command
Provide noninteractive become credentials through Vault or a secret broker
For unattended automation where policy requires a sudo password, store ansible_become_password as encrypted Vault content or inject it from an approved credential system at runtime. Vault protects data at rest only; callbacks, tasks, and tools must still prevent disclosure after decryption. Keep SSH login credentials separate from become credentials.
Why this step matters
Encrypted variables or controller-managed credentials support noninteractive jobs without committing the cleartext sudo password.
What to understand
Enter the secret through standard input or an approved prompt, not as a positional shell argument.
Store vaulted variables in a restricted group_vars or host_vars file appropriate to the smallest host set.
Supply the Vault unlock secret through an approved prompt, credential manager, or executable client; a plaintext password file merely moves the secret.
Use no_log on tasks that could echo secret-bearing parameters, and review third-party roles and callback plugins.
System changes
- Creates encrypted Vault ciphertext for a variable; no managed-node policy is changed.
Syntax explained
encrypt_string- Encrypts one variable value for inclusion in structured Ansible data.
--vault-id {{vaultId}}@prompt- Labels the ciphertext and prompts for the Vault encryption password.
--stdin-name ansible_become_password- Reads the cleartext from standard input and names the encrypted variable without putting the secret in argv.
Values stay on this page and are never sent or saved.
ansible-vault encrypt_string --vault-id {{vaultId}}@prompt --stdin-name ansible_become_passwordNew vault password (default):
Confirm new vault password (default):
Reading plaintext input from stdin. (ctrl-d to end input)
ansible_become_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;prod
663864356663396364663139653032...Checkpoint: Automation can decrypt without exposing the become password
ansible-playbook -i {{inventoryPath}} {{playbookPath}} --limit {{targetHost}} --check --diff --vault-id {{vaultId}}@promptContinue whenThe run unlocks the approved Vault, reaches become, and reports no Missing sudo password without printing the credential.
Stop whenThe cleartext appears in Git, argv, logs, facts, callback output, or an unprotected file.
If this step fails
Decryption failed (no vault secrets were found)
Likely causeThe runner lacks the required Vault ID or password source.
Inspect Vault labels without decrypting values.Compare the runner credential mapping.
ResolutionAttach the correct approved Vault identity; do not copy ciphertext or passwords between environments.
The password is vaulted but appears in task output
Likely causeA task or plugin logs decrypted data in use.
Review no_log and callback configuration.Inspect only protected artifacts.
ResolutionStop the run, rotate the exposed secret, and fix output handling.
Security notes
- Ansible Vault is not a runtime access-control system and protects only data at rest.
- Never use ansible_password when ansible_become_password is intended, or vice versa.
Alternatives
- Use AWX/Automation Controller machine credentials or an approved external secret manager with short-lived retrieval.
Stop conditions
- The inventory, target host, SSH user, control node, or known_hosts file is not proven.
- The presented SSH host-key fingerprint cannot be verified through an independent trusted channel.
- A proposed fix disables host-key checking, writes a password in plaintext, or grants unrestricted passwordless sudo.
- The target is production and another automation run is changing the same inventory or sudo policy.
config
If policy must change, grant the smallest reviewed sudo capability
When the automation account is not authorized, derive the exact privileged executables from the playbook and have the sudo policy owner create a narrow rule. Prefer root-owned deployment helpers with fixed arguments where wildcard sudo rules would be unsafe. Validate syntax with visudo before installation. Never grant NOPASSWD: ALL merely to make Ansible green.
Why this step matters
A command-scoped rule can support a defined automation responsibility while limiting blast radius. Sudo command matching, arguments, shells, editors, interpreters, wildcards, and writable scripts require specialist review because many can escape the intended boundary.
What to understand
List every privileged task and determine whether it can be redesigned to avoid root or delegated to a root-owned helper.
Use absolute executable paths and fixed safe arguments. Do not allow shells, interpreters, editors, package managers, or writable scripts without a security design.
Keep the file root-owned and mode 0440; validate with visudo -cf before activation.
Document owner, purpose, expiration or review date, affected hosts, and rollback path.
System changes
- Changes sudo authorization and may allow noninteractive root execution of the explicitly listed commands.
Syntax explained
Cmnd_Alias- Names a reviewed set of exact privileged command paths and arguments.
(root)- Restricts the Runas target to the approved account.
NOPASSWD- Removes interactive authentication only for the matched command set; it must not be applied broadly.
/etc/sudoers.d/ansible-deploy# Managed by the sudo policy owner
Cmnd_Alias ANSIBLE_SERVICE_STATUS = /usr/bin/systemctl status nginx, /usr/bin/systemctl is-active nginx
automation ALL=(root) NOPASSWD: ANSIBLE_SERVICE_STATUS/etc/sudoers.d/ansible-deploy: parsed OK
User automation may run the following commands on web-01:
(root) NOPASSWD: /usr/bin/systemctl status nginx, /usr/bin/systemctl is-active nginxCheckpoint: Sudo policy is valid and bounded
visudo -cf /etc/sudoers.d/ansible-deploy && sudo -n -l -U {{remoteUser}}Continue whenSyntax is valid and the account receives only the approved command set.
Stop whenThe rule contains ALL commands, unsafe wildcards, writable executables, shells, interpreters, or an unexpected Runas target.
If this step fails
visudo reports a syntax error
Likely causeInvalid sudoers grammar, alias, path, or line continuation.
Validate the candidate file separately.Keep recovery console access.
ResolutionDo not install the file until visudo parses it.
The play still needs commands outside the rule
Likely causeThe role's privilege surface was not inventoried or uses temporary module commands.
Review exact module behavior in nonproduction.Avoid progressively widening wildcards.
ResolutionRedesign with a safe helper, password-authenticated become, or a different deployment boundary.
Security notes
- NOPASSWD: ALL is prohibited by this procedure.
- A sudo rule invoking a user-writable script grants the ability to replace that script and escalate.
Alternatives
- Keep password-authenticated become with a secret broker, or move the operation to a root-owned service API.
Stop conditions
- No sudo policy owner approves the rule.
- The required privilege cannot be expressed safely as a bounded interface.
command
Run the original play in check mode with the repaired trust path
After host verification, SSH authentication, and become are independently proven, run the original playbook against one host in check mode with diff enabled where modules support it. The purpose is to reveal variable, policy, and scope mistakes before making changes. Check mode is not a universal dry-run; modules can skip, contact services, or behave differently, so review every task and module.
Why this step matters
A convergent limited check demonstrates that the repaired credentials and policy support the intended role without immediately changing the fleet.
What to understand
Read each changed prediction and diff; redact configuration secrets before attaching output.
Confirm unreachable and failed are zero and no host outside the explicit limit appears.
Investigate skipped tasks whose check-mode behavior hides a required privileged command.
Use serial execution and normal approvals for the later real run.
System changes
- No intended remote change; this step collects or validates evidence.
Syntax explained
--check- Asks supporting modules to predict changes without applying them.
--diff- Shows supported before/after content; output may be sensitive.
--limit {{targetHost}}- Constrains validation to the reviewed canary.
--vault-id- Supplies the approved Vault identity without placing the secret on the command line.
Values stay on this page and are never sent or saved.
ansible-playbook -i {{inventoryPath}} {{playbookPath}} --limit {{targetHost}} --check --diff --vault-id {{vaultId}}@promptPLAY [web] ******************************************************************* TASK [Gathering Facts] ******************************************************* ok: [web-01.example.net] TASK [Validate nginx configuration] ***************************************** ok: [web-01.example.net] PLAY RECAP ******************************************************************* web-01.example.net : ok=8 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Checkpoint: The canary check converges safely
ansible-playbook -i {{inventoryPath}} {{playbookPath}} --limit {{targetHost}} --check --diff --vault-id {{vaultId}}@promptContinue whenNo host is unreachable or failed, the target set is exact, and every predicted change is understood.
Stop whenAn unexpected host, destructive task, secret-bearing diff, or unexplained change appears.
If this step fails
Check mode reports success but real runs fail
Likely causeOne or more modules do not implement check mode or depend on earlier changes.
Review module check_mode support.Test in a disposable environment.
ResolutionDo not treat check mode as complete proof; use a reviewed canary execution.
Diff exposes credentials or private configuration
Likely causeThe task lacks no_log or manages secret-bearing files.
Restrict artifact access.Identify the emitting task.
ResolutionDelete unsafe copies, rotate exposed secrets if necessary, and fix logging before continuing.
Security notes
- Check mode can still query remote systems and create audit events.
- Treat --diff output as potentially secret.
Alternatives
- Use a dedicated staging host with the same sudo and SSH policies when production check-mode contact is not approved.
Stop conditions
- The inventory, target host, SSH user, control node, or known_hosts file is not proven.
- The presented SSH host-key fingerprint cannot be verified through an independent trusted channel.
- A proposed fix disables host-key checking, writes a password in plaintext, or grants unrestricted passwordless sudo.
- The target is production and another automation run is changing the same inventory or sudo policy.
command
Verify the repaired run and prevent recurrence
Run one approved canary through the normal automation path, then repeat in check mode. Record the effective inventory, host fingerprint source, remote user, become method, credential source, sudo policy checksum, playbook revision, and recap. A healthy closure has no Host key verification failed, no Missing sudo password, no plaintext credential, and an understood zero-change repeat.
Why this step matters
The first run proves the intended operation and the second establishes idempotency. Capturing trust and privilege metadata makes a future rotation or rebuild diagnosable.
What to understand
Scale beyond the canary only through the normal serial, approval, monitoring, and rollback policy.
Version verified host keys or host CA trust in the runner image or configuration system.
Test credential retrieval and sudo policy before deployment windows without printing the secret.
Alert separately on host-key changes, SSH authentication failures, and become failures because they have different owners and severity.
System changes
- The approved playbook may apply its reviewed changes to the canary; the repeat check should predict none.
Syntax explained
first playbook run- Executes the approved canary operation through the normal credential and trust path.
repeat --check --diff- Verifies convergence and captures evidence of unexpected drift.
Values stay on this page and are never sent or saved.
ansible-playbook -i {{inventoryPath}} {{playbookPath}} --limit {{targetHost}} --vault-id {{vaultId}}@prompt && ansible-playbook -i {{inventoryPath}} {{playbookPath}} --limit {{targetHost}} --check --diff --vault-id {{vaultId}}@promptPLAY RECAP ******************************************************************* web-01.example.net : ok=12 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Repeat check: web-01.example.net : ok=12 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Checkpoint: The automation path is healthy and repeatable
ansible-playbook -i {{inventoryPath}} {{playbookPath}} --limit {{targetHost}} --check --diff --vault-id {{vaultId}}@promptContinue whenRecap shows failed=0, unreachable=0, and changed=0 or only documented check-mode limitations.
Stop whenEither literal error returns, another host is selected, a secret appears, or the repeat predicts unexplained changes.
If this step fails
Host key verification fails again after a runner restart
Likely causeThe runner trust store is ephemeral or not included in its image/mount.
Inspect runner lifecycle and known_hosts provisioning.Compare the independently verified fingerprint.
ResolutionMake trust bootstrap a versioned runner dependency or adopt host certificates.
Missing sudo password returns only in CI
Likely causeThe interactive shell and CI use different inventory, Vault ID, credential binding, environment, or service account.
Compare sanitized effective configuration and job template.Confirm CI secret access.
ResolutionAlign the authoritative noninteractive path; never add plaintext fallback credentials.
Security notes
- Rotate any credential exposed during the incident.
- Do not keep temporary bypasses, broad sudo rules, or unreviewed known_hosts entries after closure.
Alternatives
- If a production canary is not possible, validate in an environment with identical runner, inventory plugin, SSH trust, and sudo policy before scheduling a controlled production window.
Stop conditions
- The inventory, target host, SSH user, control node, or known_hosts file is not proven.
- The presented SSH host-key fingerprint cannot be verified through an independent trusted channel.
- A proposed fix disables host-key checking, writes a password in plaintext, or grants unrestricted passwordless sudo.
- The target is production and another automation run is changing the same inventory or sudo policy.
Finish line
Verification checklist
ansible-inventory -i {{inventoryPath}} --host {{targetHost}}The selected address, port, user, connection plugin, become method, and become user match the approved host.ssh-keyscan -T 5 -p {{sshPort}} -t ed25519 {{targetHost}} 2>/dev/null | ssh-keygen -lf -The SHA256 fingerprint matches an independent trusted record.ansible {{targetHost}} -i {{inventoryPath}} -m ansible.builtin.ping -e ansible_become=falseSUCCESS, changed false, and pong without a host-key bypass.ansible {{targetHost}} -i {{inventoryPath}} -b -K -m ansible.builtin.command -a 'id -u'The documented become user's uid is returned.ansible-playbook -i {{inventoryPath}} {{playbookPath}} --limit {{targetHost}} --check --diff --vault-id {{vaultId}}@promptfailed=0, unreachable=0, and no unexplained changes.Recovery guidance
Common problems and safe checks
Host key verification failed on a rebuilt host
Likely causeThe instance legitimately generated a new host key, but the old key remains pinned.
Verify instance identity and fingerprint through console or provisioning records.Compare the local public key on the host.
ResolutionBack up known_hosts and replace only the independently verified token.
Host key verification failed only in CI
Likely causeThe runner uses an empty or different HOME, container image, service account, or UserKnownHostsFile.
Inspect the runner account and SSH effective configuration.Compare verified fingerprints without disabling checks.
ResolutionProvision the trust store as a versioned runner dependency.
Host key verification failed after DNS change
Likely causeThe name points to a different node or load-balanced set with unmanaged identities.
Compare DNS answers with inventory and CMDB.Verify every intended node independently.
ResolutionCorrect naming or deploy host certificates; do not accept arbitrary answers.
Missing sudo password in an interactive terminal
Likely causeBecome is enabled but -K was not supplied and no encrypted variable is available.
Confirm unprivileged ping first.Inspect become method and user.
ResolutionRun with -b -K for a supervised job or bind an approved secret.
Missing sudo password only in CI
Likely causeThe job has no become credential binding or wrong Vault ID.
Compare sanitized job template and inventory.Test credential retrieval without printing it.
ResolutionAttach the approved controller or Vault credential.
Incorrect sudo password
Likely causeThe login password and become password were confused, the secret rotated, or the wrong user is selected.
Confirm ansible_user and ansible_become_user.Verify current secret ownership.
ResolutionUse the correct current become credential; avoid repeated lockout attempts.
sudo: a terminal is required
Likely causeSudo policy requires a TTY or interaction incompatible with the runner.
Review sudoers with the policy owner.Confirm distribution and sudo version.
ResolutionCreate a narrowly reviewed automation policy; do not allocate a fake TTY as a blanket workaround.
user is not in the sudoers file
Likely causeThe remote account has no authorization for the requested become action.
Reconfirm remote user.Have the owner inspect included sudo rules.
ResolutionGrant only the exact required capability or redesign the operation.
Unprivileged ping fails with Permission denied (publickey)
Likely causeSSH identity or authorized_keys is wrong; become has not been reached.
Inspect identity and user selection.Use the dedicated public-key Guide.
ResolutionRepair SSH login before troubleshooting sudo.
Unprivileged ping reports Python missing
Likely causeThe host lacks a supported interpreter or inventory points to the wrong path.
Inspect supported distribution and interpreter path.Avoid mixing this with credential changes.
ResolutionBootstrap Python through a separate approved procedure.
The verified key changes again immediately
Likely causeDNS points to several hosts, an autoscaling group lacks common trust, or interception is possible.
Compare every backend identity.Inspect host-certificate design.
ResolutionStop per-host acceptance and fix the trust architecture.
Vault decrypts locally but not in the controller
Likely causeDifferent Vault IDs, unlock sources, project revisions, or credential permissions.
Compare labels and job bindings.Do not copy plaintext secrets.
ResolutionAlign the authoritative controller credential mapping.
Check mode succeeds but normal run asks for another privilege
Likely causeSkipped tasks or modules require commands absent from the sudo policy.
Review module check-mode support.Test a canary in a safe environment.
ResolutionRedesign or explicitly approve the additional narrow capability.
The second run still reports changes
Likely causeThe playbook is not idempotent, volatile data is templated, or external actors create drift.
Inspect task diffs without exposing secrets.Separate credential recovery from convergence defects.
ResolutionFix idempotency before fleet rollout.
Reference
Frequently asked questions
What does Ansible Missing sudo password mean?
Ansible reached the managed node and attempted a task with become, but the selected privilege-escalation method required a password and none was available. It does not mean SSH host authentication failed. Verify unprivileged ping, the remote user, become method, become user, and sudo policy before supplying a credential.
What does Ansible Host key verification failed mean?
OpenSSH could not authenticate the server identity against known_hosts or a trusted host CA. This happens before SSH user authentication and before sudo. Verify the offered fingerprint through an independent channel; never disable checking to bypass it.
Can I fix Host key verification failed with ANSIBLE_HOST_KEY_CHECKING=False?
No. That removes server authentication and can expose credentials and automation to an intercepted host. Correct the inventory destination and install only an independently verified key, or use signed host certificates.
Is ssh-keyscan safe for adding a host key automatically?
ssh-keyscan is useful for collecting public keys, but its manual warns that an unverified result can enable a man-in-the-middle attack. Convert the result to a fingerprint and compare it with console, CMDB, provisioning, or another trusted channel before installation.
Are ansible_password and ansible_become_password the same?
No. ansible_password authenticates the remote login for a connection plugin. ansible_become_password authenticates privilege escalation after login. Using one in place of the other confuses trust boundaries and often exposes unnecessary secrets.
What is the difference between -b and -K?
-b or --become enables privilege escalation. -K or --ask-become-pass prompts for its password. -K does not imply become, and -b does not supply a password.
Should I put ansible_become_password in inventory?
Not in plaintext. Encrypt the variable with Ansible Vault or use a controller credential or approved secret broker. Remember that Vault protects data at rest, so tasks and callbacks must also avoid logging the decrypted value.
Can I use NOPASSWD to avoid a become credential?
Only for a narrowly reviewed command set when policy allows it. Never use NOPASSWD: ALL as a troubleshooting shortcut. Prefer root-owned helpers or fixed command paths and arguments, validated with visudo and monitored.
Why does the error happen only in CI?
CI may use another service account, HOME, known_hosts file, inventory source, Vault ID, SSH agent, container image, or credential binding. Compare sanitized effective configuration with the interactive shell rather than copying local files blindly.
What if the host was legitimately rebuilt?
Confirm the instance and new fingerprint through an independent provider or console record. Preserve the old key as evidence, remove only the exact obsolete token, add the verified key, and document the rotation. A rebuild claim alone is not authentication.
Does check mode guarantee the real play is safe?
No. Check-mode support is module-specific; tasks can skip, contact services, or depend on prior changes. Review module behavior and run an approved single-host canary before scaling.
How often should this Guide be reviewed?
Review within 90 days and whenever supported ansible-core versions, SSH connection behavior, Vault handling, become plugins, sudo policy, runner images, or OpenSSH host-key standards change.
Recovery
Rollback
Rollback trust-store or sudo-policy changes independently. Restore the timestamped known_hosts backup only if it contains the still-authoritative key; never restore a key that was rotated for a verified rebuild. Remove or revert the exact sudoers file through console-safe access after validating the previous policy. Revoke and rotate any exposed Vault, SSH, or become credential. Stop playbook execution and use the playbook's own service rollback for application changes.
- Stop automation for the affected host or group and preserve the failed run, current fingerprint, sudo policy, and runner identity.
- If the wrong known_hosts token was edited, restore the protected backup with original owner and mode, then verify the authoritative fingerprint before reconnecting.
- If a sudoers change widened access, use retained console access to restore the last reviewed file and validate it with visudo -cf before ending the session.
- Remove temporary inventory overrides, extra vars, environment variables, or runner mounts introduced during diagnosis.
- Rotate any SSH, Vault, or become secret exposed in argv, logs, diffs, tickets, or recordings.
- Run unprivileged ping and sudo -n -l again; do not resume the original play until trust and authorization are both understood.
Evidence