Search code examples
sshansibleansible-2.xssh-agent

playbook to add ssh-key having passphrase and reuse SSH_AUTH_SOCK, SSH_AGENT_PID


What do I want to achieve? Using a single Ansible playbook,

  1. start ssh-agent if not running (on localhost).
  2. add ssh-key (that is passphrase protected).
  3. connect to remote machines to run tasks
    ---
    - name: setup ssh-agent
      hosts: localhost
      gather_facts: false
      vars_files:
        - ../vars.yml
      vars:
        ansible_ssh_private_key_file: "{{ lookup('env', 'HOME') }}/.ssh/{{sshkeyname}}"
        ansible_ssh_pass: "{{sshpassphrs}}"
      tasks:
        - name: Start ssh-agent and Retrieve the SSH_AUTH_SOCK and SSH_AGENT_PID environment variables
          shell: |
              eval $(ssh-agent -s) > /dev/null
              echo '{"SSH_AUTH_SOCK":"'$SSH_AUTH_SOCK'","SSH_AGENT_PID":"'$SSH_AGENT_PID'"}'
          register: env_vars_stdout
        - name: save to env_vars
          set_fact:
            env_vars: "{{ env_vars_stdout.stdout }}"
        - name: add {{ansible_ssh_private_key_file}} to ssh-agent
          environment: "{{ env_vars }}"
          expect:
            command: ssh-add {{ ansible_ssh_private_key_file }}
            responses:
              passphrase: "{{ ansible_ssh_pass }}"
    - name: main jobs
      hosts: all
      gather_facts: false
      environment: "{{ hostvars['localhost']['env_vars'] }}"
      vars_files:
        - ../vars.yml
      vars:
        ansible_user: "{{username}}"
      tasks:
        - name: Do a ping test
          shell: ping -c 20 -w 50 fb.me

It throws an error for the ping test, as destination hosts are unreachable with msg "Failed to connect to the host via ssh".

I know this is quite a hacky way, adding the key through ssh-agent in the terminal session would have been straightforward. But anyway to achieve this?


Solution

  • I think the issue here is that, the enviroment keyword, as per the doc, helps in setting the remote environment. i.e. by then the SSH connection to the remote host should have been made.

    In order to change the arguments, which are used to connect to the remote_host, we need to use the ansible_ssh_common_args variable. You can use the IdentityAgent option, in the ssh_config, to specify the SSH Agent socket.

    I have added a couple of statements to your solution.

     ---
        - name: setup ssh-agent
          hosts: localhost
          gather_facts: false
          vars_files:
            - ../vars.yml
          vars:
            ansible_ssh_private_key_file: "{{ lookup('env', 'HOME') }}/.ssh/{{sshkeyname}}"
            ansible_ssh_pass: "{{sshpassphrs}}"
          tasks:
            - name: Start ssh-agent and Retrieve the SSH_AUTH_SOCK and SSH_AGENT_PID environment variables
              shell: |
                  eval $(ssh-agent -s) > /dev/null
                  echo '{"SSH_AUTH_SOCK":"'$SSH_AUTH_SOCK'","SSH_AGENT_PID":"'$SSH_AGENT_PID'"}'
              register: env_vars_stdout
            - name: save to env_vars
              set_fact:
                env_vars: "{{ env_vars_stdout.stdout }}"
                ssh_auth_sock: "{{ env_vars_stdout.stdout | from_json | json_query('SSH_AUTH_SOCK') }}" # install jmespath if not already present
            - name: add {{ansible_ssh_private_key_file}} to ssh-agent
              environment: "{{ env_vars }}"
              expect:
                command: ssh-add {{ ansible_ssh_private_key_file }}
                responses:
                  passphrase: "{{ ansible_ssh_pass }}"
        - name: main jobs
          hosts: all
          gather_facts: false
          environment: "{{ hostvars['localhost']['env_vars'] }}"
          vars_files:
            - ../vars.yml
          vars:
            ansible_user: "{{username}}"
            ansible_ssh_common_args: "-o 'IdentityAgent={{ hostvars['localhost']['ssh_auth_sock'] }}'"
          tasks:
            - name: Do a ping test
              shell: ping -c 20 -w 50 fb.me
    

    When you run the job, we can pass the -vvv option to, see that, our agent is actually being used when making a connection to the remote_host

    I saw something like below, in the output

    <remote.host> SSH: EXEC ssh -C -o IdentityAgent=/var/folders/1s/rwv4vb6n01ld3sxfzqp5svtm0000gn/T//ssh-KGeKsXlC8bGO/agent.14769 -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PasswordAuthentication=no -o 'User="phanirajgoutham"' -o ConnectTimeout=10 remote.host
    

    You could see the IdentityAgent option being passed along with the other options, when making a connection to the remote_host.

    Please let me know if this does not work for you.