Ansible ssh key connection

Refer to https://nicodevlog.com/2022/05/21/public-key-authentication-on-linux-best-practices-a-more-secure-way-to-connect-to-your-hosts-without-login-and-password/

The advantages of an ssh key over a basic authentication are:

  • no raw password (but you can encrypt passwords using ansible vault)
  • one unique private key to connect to several nodes (but of course if somebody steal your private key… that’s the reason why you must use a pass phrase to encrypt your private key)

Ansible ssh key through bastion (ProxyJump / Jump host)

The best practice since OpenSSH 7.3 (appearance of the -J option)

ProxyJump / Jump host => bastion

When you have some nodes that are behind a bastion, you have to define only once this bastion and not several times in your inventory. Because if the characteristics of the bastion change, you’ll have to change your inventory at multiple places (and then multiple risks of manual errors when modifying these characteristics).

Then the best way is to define the bastion in the ~/.ssh/config file, for example:

# ~/.ssh/config
#
Host *
    #Port 22 => don't specify the port here, because it's the first match that overwrite all the others
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null
    ServerAliveInterval 60
    ServerAliveCountMax 30

# Define your bastion
Host bastion
    Port 2222
    Hostname hostname.ofyourbastion.com
    User ansibleuser
    IdentityFile ~/.ssh/mykey

# Define which nodes go through the bastion here not in your inventory
Host host-nodeA
    Hostname 192.168.100.30
    ProxyJump bastion:2222
Host host-nodeB
    Hostname 192.168.100.31
    ProxyJump bastion:2222
Host host-nodeC
    Hostname 192.168.100.33
    ProxyJump bastion:2222

Now you can ssh to your bastion in the CLI like this:

ssh bastion

# ssh to the nodes behind the bastion:
# (note: it will ask for the user password because you don't specify an ssh key in the following lines
ssh someuser@host-nodeA
ssh someuser@host-nodeB
ssh someuser@host-nodeC

In your ansible inventory you’ll have this:

nodeA ansible_host=host-nodeA ansible_connection=ssh ansible_user=someuser ansible_ssh_private_key_file=~/.ssh/mykey
nodeB ansible_host=host-nodeA ansible_connection=ssh ansible_user=someuser ansible_ssh_private_key_file=~/.ssh/mykey
nodeC ansible_host=host-nodeA ansible_connection=ssh ansible_user=someuser ansible_ssh_private_key_file=~/.ssh/mykey

Or you can extract the node part from ~/.ssh/config and add it into the inventory like this. Then you have this ~/.ssh/config file:

# ~/.ssh/config
#
Host *
    #Port 22 => don't specify the port here, because it's the first match that overwrite all the others
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null
    ServerAliveInterval 60
    ServerAliveCountMax 30

# Define your bastion
Host bastion
    Port 2222
    Hostname hostname.ofyourbastion.com
    User ansibleuser
    IdentityFile ~/.ssh/mykey
[group1:vars]
ansible_ssh_common_args='-J bastion'

[group1]
nodeA ansible_host=192.168.100.30 ansible_connection=ssh ansible_user=someuser ansible_ssh_private_key_file=~/.ssh/mykey
nodeB ansible_host=192.168.100.31 ansible_connection=ssh ansible_user=someuser ansible_ssh_private_key_file=~/.ssh/mykey
nodeC ansible_host=192.168.100.32 ansible_connection=ssh ansible_user=someuser ansible_ssh_private_key_file=~/.ssh/mykey

As you can see you loose the advantage of naming your IP.

Copy you public key to your target host through the bastion

It is as simple as explained in this post.

For instance here (no risk to copy private key, ssh-copy-id prevent this):

ssh-copy-id -i ~/.ssh/mykey someuser@nodeC