As an additional datapoint, here’s a brief summary of how I deal with this. To complicate matters, my machines are split across various labs in different locations which each have their own bastion/jumpbox. I use ssh keys sometimes, and hard coded passwords for some other machines:
ansible.cfg:
`
[defaults]
hostfile = hosts
error_on_undefined_vars = True
host_key_checking = False
transport = ssh
jinja2_extensions = jinja2.ext.do
[ssh_connection]
ssh_args = -F ssh.config
pipelining = True
`
ssh.config (referenced in ansible.cfg). Note that if you have a new enough version of ssh, you can use the -W flag instead of nc:
`
#jumpboxes first (most specific hosts first)
Host jumpbox01 10.1.0.10
ControlMaster yes
ControlPath ~/.ssh/master-%r@jumpbox01:%p
StrictHostkeyChecking no
ProxyCommand none
Host jumpbox02 10.2.0.10
ControlMaster yes
ControlPath ~/.ssh/master-%r@jumpbox02:%p
StrictHostkeyChecking no
ProxyCommand none
Host jumpbox03 10.3.0.10
ControlMaster yes
ControlPath ~/.ssh/master-%r@jumpbox03:%p
StrictHostkeyChecking no
ProxyCommand none
groups of machines that can be accessed by the above jumpboxes
Host .west.domain.com 10.1.0.
ControlMaster no
ProxyCommand ssh -S ~/.ssh/master-*@jumpbox01:%p remote nc %h %p
Host .central.domain.com 10.2.0.
ControlMaster no
ProxyCommand ssh -S ~/.ssh/master-*@jumpbox02:%p remote nc %h %p
Host .east.domain.com 10.3.0.
ControlMaster no
ProxyCommand ssh -S ~/.ssh/master-*@jumpbox03:%p remote nc %h %p
this makes ansible faster by reusing connections
Host *
ControlMaster auto
ControlPersist 300s
ControlPath ~/.ssh/ansible-%r@%h:%p
`
Once I have those configs setup, I have to run the following to establish a tunnel to a jumpbox/bastion before I can run ansible:
`
$ ssh -F ssh.config -fN user@jumpbox01
`
When I run the above, it asks for the password (or uses my SSH key), then SSH goes into the background and then the tunnel is established.
I do all of my deployments this way by creating a Jenkins job that establishes the tunnel, runs ansible, then tears down the tunnel using something like:
ssh -O exit -TS ~/.ssh/path-to-socket
Some of the jumpboxes use dumb passwords, some of them use keys, and one of them requires an RSA token (2-factor auth). For the RSA machine, my Jenkins job presents the user with a form that has 2 fields: 2-Factor Username, and 2-Factor Passcode. The passcode is generated by an RSA token keyfob (or smartphone app). In order to make this work, I had to write a custom expect script because the SSH prompt for the RSA token reads “Enter PASSCODE” instead of “Password” which is what sshpass is hardcoded to look for. Here’s my expect script to catch all the variations:
`
#!/usr/bin/env expect
set timeout 30
set userhost [lindex $argv 0]
spawn ssh -fN -F ssh.config $userhost
expect {
“Enter PASSCODE:” {
send “$env(SSH_PASSWORD)\n”
send “\n”
}
“Password:” {
send “$env(SSH_PASSWORD)\n”
send “\n”
}
“password:” {
send “$env(SSH_PASSWORD)\n”
send “\n”
}
}
sleep 5
`
I need the sleep 5 at the end of the script as a hack because the jumpbox with RSA token don’t establish the control socket until a few seconds after the login happens. If my script exits too soon, then the tunnel won’t get established. I’m not sure how to properly deal with this.
Like I said, some of the machines that I run ansible on have hardcoded passwords and it works fine when ssh_user and ssh_pass is set as facts for your host.