Secure Shell (SSH) public-key authentication is an asymmetric cryptographic mechanism that allows a user on a control node to authenticate securely with remote target machines without interactively transmitting passwords across the network. It relies on a key pair consisting of:
id_rsa): Stored securely on the client machine (Jump Host / Control Node) with strictly restricted permissions (0600). The private key must never be shared or exposed.id_rsa.pub): Transferred to and appended to the ~/.ssh/authorized_keys file on remote target hosts (Managed Nodes).When an SSH connection or Ansible playbook is initiated, the control node proves possession of the private key via a cryptographic challenge-response mechanism. If the public key on the remote target matches, access is granted instantly without requiring user password prompts.
Ansible uses an agentless architecture, meaning no specialized agent daemons run on remote managed nodes. Instead, Ansible relies entirely on standard SSH connections (via Python’s paramiko or OpenSSH) to execute playbooks and ad-hoc commands.
Without password-less SSH key authentication:
--ask-pass or -k), preventing automated scheduled runs (e.g., via Jenkins, Cron, or CI/CD pipelines).ansible_ssh_pass) creates significant security vulnerabilities and violates credential management best practices.graph TD
subgraph ControlNode ["Jump Host (Ansible Controller)"]
ThorUser["User: thor"]
KeyGen["1. ssh-keygen<br/>Generate RSA Keypair"]
PrivKey["Private Key: ~/.ssh/id_rsa"]
PubKey["Public Key: ~/.ssh/id_rsa.pub"]
InvFile["Inventory File<br/>/home/thor/ansible/inventory"]
AnsibleCLI["Ansible Engine<br/>ansible -m ping"]
end
subgraph ManagedNode ["App Server 2 (Stratos DC)"]
App2["stapp02<br/>User: steve"]
AuthKeys["Target File:<br/>~/.ssh/authorized_keys"]
end
KeyGen --> PrivKey
KeyGen --> PubKey
PubKey -->|"2. ssh-copy-id steve@stapp02"| AuthKeys
InvFile -->|"Supply Host Specs"| AnsibleCLI
AnsibleCLI -->|"3. Password-less SSH Authentication"| App2
App2 -->|"4. Return ping: pong response"| AnsibleCLI
ssh-keygen)The ssh-keygen utility creates public and private authentication key pairs. Passing -t rsa -b 4096 specifies 4096-bit RSA encryption, while -N "" creates a key without a passphrase to allow non-interactive authentication.
ssh-keygen -t rsa -b 4096 -N "" -f ~/.ssh/id_rsa
ssh-copy-id)The ssh-copy-id script automates installing a local public key into a remote machine’s ~/.ssh/authorized_keys file. It automatically creates the ~/.ssh directory if missing and sets recommended permissions (0700 for .ssh and 0600 for authorized_keys).
ssh-copy-id steve@stapp02
ansible -m ping)Unlike the ICMP ping network command, Ansible’s ping module validates end-to-end SSH connectivity, remote execution rights, and Python availability on managed nodes.
ansible -i /home/thor/ansible/inventory stapp02 -m ping
| Host Role | Hostname / Alias | SSH User | User Password | Function |
|---|---|---|---|---|
| Ansible Controller | jump_host |
thor |
Native | Ansible Control Node |
| App Server 1 | stapp01 |
tony |
Ir0nM@n |
Managed Node 1 |
| App Server 2 | stapp02 |
steve |
Am3ric@ |
Managed Node 2 (Target) |
| App Server 3 | stapp03 |
banner |
BigGr33n |
Managed Node 3 |
thor user session)stapp02)steve/home/thor/ansible/inventoryansible -i /home/thor/ansible/inventory stapp02 -m ping returning ping: pong.Log in to the Jump Host as user thor:
ssh thor@jump_host
Verify that the inventory file exists at /home/thor/ansible/inventory and check the target host definition:
cat /home/thor/ansible/inventory
Sample Inventory Content:
[app_servers]
stapp01 ansible_host=stapp01 ansible_user=tony
stapp02 ansible_host=stapp02 ansible_user=steve
stapp03 ansible_host=stapp03 ansible_user=banner
Generate a pair of RSA keys for the thor user on the Jump Host without a passphrase:
ssh-keygen -t rsa -b 4096 -N "" -f ~/.ssh/id_rsa
Verification: Confirm the generated key pair in ~/.ssh/:
ls -la ~/.ssh/
Output should contain:
id_rsa (Private Key)id_rsa.pub (Public Key)Transfer the public key from the Jump Host to App Server 2 (stapp02) for target user steve:
ssh-copy-id steve@stapp02
(Alternatively, use IP address ssh-copy-id steve@stapp02 if hostname resolution is not in /etc/hosts)
When prompted:
yes to accept the SSH host authenticity fingerprint (if connecting for the first time).steve on App Server 2: Am3ric@./usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
steve@stapp02's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'steve@stapp02'"
and check to make sure that only the key(s) you wanted were added.
Verify that thor can log in to stapp02 as user steve directly via SSH without entering a password:
ssh steve@stapp02 "hostname; id"
Expected Output:
stapp02
uid=1001(steve) gid=1001(steve) groups=1001(steve)
Execute Ansible ping module from the Jump Host targeting stapp02 using the inventory located at /home/thor/ansible/inventory:
ansible -i /home/thor/ansible/inventory stapp02 -m ping
Or ping all host entries defined under app_servers if keys were distributed across all app servers:
ansible -i /home/thor/ansible/inventory app_servers -m ping
Execute the ping verification command and ensure a successful JSON result is returned:
ansible -i /home/thor/ansible/inventory stapp02 -m ping
stapp02 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
Key Success Indicators:
SUCCESS status indicator in green."ping": "pong" payload returned from remote Python interpreter.| Issue / Error | Cause | Resolution |
|---|---|---|
Permission denied (publickey,password) |
Public key not added to ~/.ssh/authorized_keys on target server. |
Re-run ssh-copy-id steve@stapp02 or manually paste id_rsa.pub into ~/.ssh/authorized_keys. |
Bad permissions on .ssh directory |
Remote ~/.ssh directory or authorized_keys file has insecure permissions (777 or group writable). |
SSH into target host and fix permissions: chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys. |
Host key verification failed |
OpenSSH strict host key checking rejects unverified target fingerprints during automated Ansible runs. | Connect manually via SSH once to accept fingerprint or configure host_key_checking = False in ansible.cfg. |
UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh"} |
Network interface issue or incorrect username specified in inventory (ansible_user). |
Verify target host IP and ensure inventory specifies correct SSH user (ansible_user=steve). |
ansible_ssh_pass or plaintext user passwords inside inventory files or playbooks committed to version control repositories.0600).ssh-keygen -t ed25519) key pairs for optimal cryptographic security.