2

I have been trying to figure this out for the past 2 days and am getting no where

What I am trying to do is from my home server (Centos7) run ansible playbooks to configure AWS linux (centos7) servers on a private subnet in AWS.

If I run:

sshpass -p "PASSWORD" ssh -i "/aws_ssh/KEY_PAIR.pem" -o "StrictHostKeyChecking=no" -t ansibleuser@PUBLICBASTIONHOSTIP 'sshpass -p "PASSWORD" ssh -i "/aws_ssh/KEY_PAIR.pem" -o "StrictHostKeyChecking=no" ansibleuser@PRIVATEHOSTIP'

I can connect to from my home to my AWS server on the private subnet

/etc/ansible/hosts
[webservers:vars]
ansible_user=ec2-user
ansible_become=yes
ansible_become_method=sudo
ansible_become_pass='PASSWORD'
ansible_ssh_common_args: '-o ProxyCommand="ssh ssh -i /aws_ssh/_KEY_PAIR.pem -t -W %h:%p -q ec2-user@IPADDRESS"'
[webservers]
10.150.2.15
10.150.4.15

test.yml
---
- name: This sets up an httpd webserver
  hosts: webservers
  tasks:
  - name: example
    command: hostname

What I am trying to achieve is when I run:

   [ec2-user@terraform playbook]$ ansible-playbook test.yml -vvvv
ansible-playbook 2.9.6
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/ec2-user/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible-playbook
  python version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
Using /etc/ansible/ansible.cfg as config file
setting up inventory plugins
host_list declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
script declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
auto declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Parsed /etc/ansible/hosts inventory source with ini plugin
Loading callback plugin default of type stdout, v2.0 from /usr/lib/python2.7/site-packages/ansible/plugins/callback/default.pyc

PLAYBOOK: test.yml **************************************************************************************************************************************************************************************************************************
Positional arguments: test.yml
become_method: sudo
inventory: (u'/etc/ansible/hosts',)
forks: 5
tags: (u'all',)
verbosity: 4
connection: smart
timeout: 10
1 plays in test.yml

PLAY [This sets up an httpd webserver] ******************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
task path: /playbook/test.yml:2
<10.150.2.15> ESTABLISH SSH CONNECTION FOR USER: ec2-user
<10.150.2.15> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="ec2-user"' -o ConnectTimeout=10 -o ControlPath=/home/ec2-user/.ansible/cp/f72ac484f5 10.150.2.15 '/bin/sh -c '"'"'echo ~ec2-user && sleep 0'"'"''
<10.150.4.15> ESTABLISH SSH CONNECTION FOR USER: ec2-user
<10.150.4.15> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="ec2-user"' -o ConnectTimeout=10 -o ControlPath=/home/ec2-user/.ansible/cp/a6bd87bb6f 10.150.4.15 '/bin/sh -c '"'"'echo ~ec2-user && sleep 0'"'"''
<10.150.2.15> (255, '', 'OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 58: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: Control socket "/home/ec2-user/.ansible/cp/f72ac484f5" does not exist\r\ndebug2: resolving "10.150.2.15" port 22\r\ndebug2: ssh_connect_direct: needpriv 0\r\ndebug1: Connecting to 10.150.2.15 [10.150.2.15] port 22.\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug1: connect to address 10.150.2.15 port 22: Connection timed out\r\nssh: connect to host 10.150.2.15 port 22: Connection timed out\r\n')
fatal: [10.150.2.15]: UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 58: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: Control socket \"/home/ec2-user/.ansible/cp/f72ac484f5\" does not exist\r\ndebug2: resolving \"10.150.2.15\" port 22\r\ndebug2: ssh_connect_direct: needpriv 0\r\ndebug1: Connecting to 10.150.2.15 [10.150.2.15] port 22.\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug1: connect to address 10.150.2.15 port 22: Connection timed out\r\nssh: connect to host 10.150.2.15 port 22: Connection timed out",
    "unreachable": true
}
<10.150.4.15> (255, '', 'OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 58: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: Control socket "/home/ec2-user/.ansible/cp/a6bd87bb6f" does not exist\r\ndebug2: resolving "10.150.4.15" port 22\r\ndebug2: ssh_connect_direct: needpriv 0\r\ndebug1: Connecting to 10.150.4.15 [10.150.4.15] port 22.\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug1: connect to address 10.150.4.15 port 22: Connection timed out\r\nssh: connect to host 10.150.4.15 port 22: Connection timed out\r\n')
fatal: [10.150.4.15]: UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 58: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: Control socket \"/home/ec2-user/.ansible/cp/a6bd87bb6f\" does not exist\r\ndebug2: resolving \"10.150.4.15\" port 22\r\ndebug2: ssh_connect_direct: needpriv 0\r\ndebug1: Connecting to 10.150.4.15 [10.150.4.15] port 22.\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug1: connect to address 10.150.4.15 port 22: Connection timed out\r\nssh: connect to host 10.150.4.15 port 22: Connection timed out",
    "unreachable": true
}

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
10.150.2.15               : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0
10.150.4.15               : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0

What am I doing wrong?

What I do not want to do is log into the bastion host and then run ansible commands.

  • what errors are you getting? – kenlukas Mar 26 at 19:08
1

This can be done with some clever use of Ansible arguments. First create an inventory file with host groups that you can run individual tasks/plays against. At least one of these groups should be your Bastion and one in your private subnet.

Record the IP Address of your Bastion and create a new Jinja2 variable in your Play that targets the server in the private subnet. After that, create a new Play in your Playbook that passes a vars option to your private subnet host group referencing this variable and more.

- hosts: private_subnet
  vars:
    - ansible_ssh_user: "example"
    - ansible_ssh_common_args: >
          -o ProxyCommand="ssh -W %h:%p -q {{ ansible_ssh_user }}@{{ bastion_ip }}" \
          -o ServerAliveInterval=5 \
          -o StrictHostKeyChecking=no
  tasks:
    - name: example
      debug:
        msg: Hello World from {{ inventory_hostname }}!

You should be able to follow this with any tasks you want and Ansible will tunnel all traffic through the Bastion.

I've successfully done this a handful of times. One good enhancement is automatically discovering the IP Address of your Bastion server with a role that performs and API call to AWS EC2 instead of manually creating and manipulating the bastion_ip variable.

|improve this answer|||||
  • Thank you for the attempt, however did not work, I have updated my question – Rick James Mar 30 at 18:50
  • As mentioned I have successfully applied this method in production a handful of times. If you provide some Infrastructure as Code to provision an environment to replicate yours, I'd be happy to continue testing. But as of now, if I look at the solution I have live in production and look at the solution I propose for you they are the same. My workstation uses Ansible to deploy software and configure systems on servers that are on a private subnet behind a Bastion. In fact, they are entirely air gapped from outer networks as well. The Bastion is their only means of communication to the outside. – TJ Zimmerman Mar 30 at 18:59
  • Can you provide your inventory file? – Rick James Mar 30 at 19:36
  • If you look at the first SSH argument you can see that I am passing bastion_ip. This is a reference to a variable that is dynamically set by a task that runs in another play. The Bastion, in this case, is not present in the inventory file. Playbooks initially target localhost which performs API calls to AWS to determine the IP Addresses of target machines. The IPs are registered as variables and then accessed using hostvars by tasks in other plays. For example: {{ hostvars['localhost'].bastion_ip.stdout }} – TJ Zimmerman Mar 30 at 20:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.