Ansible Setting through Multiple Bastion Hosts

Bastion Host Architecture

The Problem

In many enterprise environments, the Ansible control node cannot reach target devices directly due to network segmentation, firewall policies, or air-gapped networks. Instead, traffic must traverse one or more bastion hosts (jump hosts).

A bastion host is a special-purpose computer on a network specifically designed and configured to withstand attacks. It is hardened and serves as a single point of entry into a protected network.

Common challenges include:

  • Ansible control node has no direct routing to target devices
  • Intermediate bastion hosts lack Python (or other Ansible dependencies)
  • Need to tunnel through SSH with dynamic port binding
Multi-Hop Connection Flow

Solution: SSH ProxyJump with Local Port Binding

The solution combines SSH's ProxyJump (-J) feature with local port forwarding (-L) to create a tunnel chain, bypassing intermediate hops that don't need SSH.

Step 1: Establish SSH Tunnel through the Transient Host(s)

Create an SSH tunnel passing through the transient host(s) to the target host, with a local port binding created via -L:

ssh  -J user_1@transient_host1:port_1 -p port_2 user_2@transient_host2  -L LOCAL_PORT:TARGET_HOST_IP:TARGET_HOST_PORT

Step 2: Connect via Local Loopback

You can then directly enter the target host using the local binding:

ssh user_target_host@localhost -p LOCAL_PORT

Step 3: Configure Ansible Variables

In this way, you can run Ansible playbooks on the local host by configuring Ansible variables accordingly:

ansible_host: localhost
ansible_user: user_target_host
ansible_port: LOCAL_PORT
ansible_password: password_target_host

By setting up the SSH tunnel in this manner, the tunnel is built through each transient host to the target. The local port binding on the Ansible master machine ensures traffic is forwarded correctly through the chain of bastion hosts to the target host.

Practical Workflow

  1. Start the SSH tunnel in the background (or in a screen/tmux session)
  2. Run your Ansible playbooks targeting localhost:9022
  3. All traffic flows through the bastion chain transparently
  4. Close the tunnel when done with Ctrl+C or kill the SSH process
Best Practice: Automate tunnel establishment in your CI/CD pipeline or use Ansible's local_action to dynamically create tunnels before running network tasks.

Key Takeaways

  • SSH ProxyJump eliminates the need for Python on intermediate hosts
  • Local port binding makes targeting transparent to Ansible
  • Scalable to multiple bastion hops with chained -J parameters
  • Combine with SSH key authentication for seamless automation
#ansible #automation #networking