Using Ansible through a bastion host
Are you trying to use Ansible through a bastion host? Me too.
We use bastion hosts to access servers which are part of controlled network segments, seperated from our corporate network to meet a particular compliance regime.
Alex Bilbie wrote a good piece about how to get Ansible working with a bastion host. The only problem with his piece is that it mandates that all hosts in your inventory file have to use the same bastion host.
We have a mix of hosts under our support. Some that need to go through the bastion host, some that definitely don’t. I needed a solution that let me specify which servers I wanted to route through our bastion host and which ones I didn’t. So, here’s one way to do it.
- First up, make sure you have nc (netcat) on your bastion host.
- Create an ssh configuration file which contains:
Host bastion+*
Port 22
ProxyCommand ssh jump "nc $(echo %h | sed 's/^bastion+//g') %p"
This assumes that your bastion host’s name is jump.
Save this configuration as /etc/ansible/ssh.config, or something.
This configuration file kicks into action when ssh is fed a hostname which starts with bastion+. For those particular hostnames, we connect first to the bastion host and then pass straight through to the hostname represented by *.
3. Add the following directive under the [ssh_connection] configuration area in /etc/ansible/ansible.cfg:
ssh_args = -F /etc/ansible/ssh.config
Here, we tell Ansible that we always want to use a special ssh configuration file when we connect to hosts. The important thing is that the configuration file is benign enough to allow “normal” connections to pass through!
4. Populate your /etc/ansible/hosts file appropriately:
# cat /etc/ansible/hosts
[no_bastion_host]
host1
host2
[via_bastion_host]
host3 ansible_ssh_host="bastion+host3"
host4 ansible_ssh_host="bastion+host4"
Update — I’ve since been informed that the ansible_ssh_host directive also works on a per-hostgroup basis, too! So don’t worry if you have hundreds of hosts!
5. Test it out! I set up authentication with ssh-agent first.
My playbook connects to each server in turn and returns its hostname. The very fact that I can connect successfully to host3 and host4 means that I’ve succeeded. On my network, these hosts live in a separate network, firewalled off from everything else.
Anyways, I hope that this helps somebody out.