I have the following inventory file:
[all]
192.168.1.107
192.168.1.108
192.168.1.109
I want to add fingerprints for these hosts to known_hosts file on local machine.
I know that I can use the ansible.builtin.known_hosts but based on the docs:
Name parameter must match with "hostname" or "ip" present in key
attribute.
it seems like I must already have keys generated and I must have three sets of keys - one set per host. I would like to have just one key for all my hosts.
Right now I can use this:
- name: accept new remote host ssh fingerprints at the local host
shell: "ssh-keyscan -t 'ecdsa' {{item}} >> {{ssh_dir}}known_hosts"
with_inventory_hostnames:
- all
but the problem with this approach is that it is not idempotent - if I run it three times it will add three similar lines in the known_hosts file.
Another solution would be to check the known_hosts file for presence of a host ip and add it only if it is not present, but I could not figure out how to use variables in when condition to check for more than one host.
So the question is how can I add hosts fingerprints to local known_hosts file before generating a set of private/public keys in idempotent manner?
Here in my answer to "How to include all host keys from all hosts in group" I created a small Ansible look-up module host_ssh_keys to extract public SSH keys from the host inventory. Adding all hosts' public ssh keys to /etc/ssh/ssh_known_hosts is then as simple as this, thanks to Ansible's integration of loops with look-up plugins:
- name: Add public keys of all inventory hosts to known_hosts
ansible.builtin.known_hosts:
path: /etc/ssh/ssh_known_hosts
name: "{{ item.host }}"
key: "{{ item.known_hosts }}"
with_host_ssh_keys: "{{ ansible_play_hosts }}"
For public SSH-Keys I use this one:
- hosts: localhost
tasks:
- set_fact:
linuxkey: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
check_mode: no
- hosts: all
tasks:
- shell:
cmd: "sudo su - {{ application_user }}"
stdin: "[[ ! `grep \"{{ hostvars['localhost']['linuxkey'] }}\" ~/.ssh/authorized_keys` ]] && echo '{{ hostvars['localhost']['linuxkey'] }}' >> ~/.ssh/authorized_keys"
warn: no
executable: /bin/bash
register: results
failed_when: results.rc not in [0,1]
I think you can easy adapt it for known_hosts file
Related
I am adding a host using add_host
- hosts: localhost
tasks:
- set_fact:
ssh_key: "{{ lookup('file','jenkins.pem')| replace('\n','') }}"
- add_host:
hostname: tower
ansible_ssh_host: x.x.x.x
ansible_ssh_user: jenkins
ansible_ssh_private_key_file: "{{ ssh_key }}"
- command: ls /tmp
delegate_to: tower
The above works fine, if I specify a file path for ansible_ssh_private_key_file. But I want to read the ansible_ssh_private_key_file from a variable, it is not working. Is there any way to achieve this.
You have a misunderstanding: lookup("file") returns the contents of a file, but -- as its name is a giveaway -- ansible_ssh_private_key_file wants the path on the controller node to the ssh private key file, not its contents.
You can use the lookup("fileglob", "jenkins.pem", wantlist=True) | first to obtain the path to that jenkins.pem file, if you don't already have it, since I believe ansible wants that ssh_private_key_file path as fully qualified.
Ansible version 2.7.9
I'm writing an ansible playbook to deploy an piece of software to a linux environment. SSH access to these systems is protected by a CPM (Cyberark), used as an ssh key manager.
I've got most of the logic figured out, save for one piece. The playbook needs to loop through hosts in an inventory group, lookup the ssh private key in Cyberark for each host and then use each key to ssh into each host in the inventory group to install the software. I'm struggling with how to make that work in ansible.
I've read through the add_host and cyberarkpassword documentation, as well about 4 hours worth of searching stackoverflow and blogs, and couldn't find a single example even close to what I'm trying to do.
As far as how I think it should work:
Using the cyberarkpassword lookup module, loop through hosts in inventory group specified by {{ env }}. Value for this will be passed in through --extra-args.
Retrieve the ssh private key for each host.
Register the output from the lookup, and copy to disk, again looping through each host, and naming the file with {{ inventory_hostname }}.pem
Finally, to consume it in the next play, set a variable ansible_ssh_common_args: "-o StrictHostKeyChecking=no -i {{ deploy_temp_dir}}/keys/{{ inventory_hostname }}.pem"
But I can't figure out how to put the loop-lookup-write to disk piece together.
Sample inventory file
[local]
localhost
[local:vars]
ansible_connection=local
[corp:children]
corp-onprem-dev
corp-onprem-stage
corp-onprem-prod
corp-cloud-dev
corp-cloud-stage
corp-cloud-dev
[corp-onprem-dev]
host1
host2
host3
[corp-onprem-stage]
host1
host2
host3
[corp-onprem-prod]
host1
host2
host3
[corp-cloud-dev]
[corp-cloud-stage]
[corp-cloud-prod]
deploy.yml -- this code does not work, just my attempt at figuring it out.
- name: retrieve ssh keys for hosts in the specified group, and write them to disk
hosts: local
gather_facts: no
tasks:
- name: lookup ssh private key for each host
debug: msg={{ lookup("cyberarkpassword", cyquery)}}
vars:
cyquery:
appid: 'myapp'
query: 'Safe=mysafe;Folder=Root;Object={{ env[0] }}'
output: 'Password'
loop: groups['{{ env }}']
register: sshkeys
- name: Copy ssh key to disk
copy:
content: "{{ sshkeys }}"
dest: "{{ deploy_temp_dir }}/keys/{{ env[0] }}.pem"
mode: 0600
loop: groups['{{ env }}']
It is not clear how to "use each (private) key to ssh into each host".
To loop through hosts in an inventory group, lookup the ssh private key in Cyberark for each host and then use each key to ssh into each host in the inventory group.
Let's assume localhost (controller) is able to connect the hosts.
Take a look at the content of the variable sshkeys
- debug:
var: sshkeys
Among the lists, you'll probably see the 2 items that you're looking for. (Fit the code to what you get.)
sshkeys.results[].item ...... inventory_hostname
sshkeys.results[].password ... ssh private key
Use template to store the keys in the files. Because the play is running at the localhost delegate_to shall be used to store the files at hosts.
- template:
src: hostname.pem.j2
dest: "{{ deploy_temp_dir }}/keys/{{ item.item }}.pem"
loop: "{{ sshkeys.results }}"
delegate_to: "{{ item.item }}"
.
$ cat hostname.pem.j2
{{ item.password }}
(Not tested. I don't have CyberArk. Storing passwords in disk files may violate security standards.)
I wonder how to copy my SSH public key to many hosts using Ansible.
First attempt:
ansible all -i inventory -m local_action -a "ssh-copy-id {{ inventory_hostname }}" --ask-pass
But I have the error The module local_action was not found in configured module paths.
Second attempt using a playbook:
- hosts: all
become: no
tasks:
- local_action: command ssh-copy-id {{ inventory_hostname }}
Finally I have entered my password for each managed host:
ansible all -i inventory --list-hosts | while read h ; do ssh-copy-id "$h" ; done
How to fill password only once while deploying public SSH key to many hosts?
EDIT: I have succeeded to copy my SSH public key to multiple remote hosts using the following playbook from the Konstantin Suvorov's answer.
- hosts: all
tasks:
- authorized_key:
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
The field user should be mandatory according to the documentation but it seems to work without. Therefore the above generic playbook may be used for any user when used with this command line:
ansible-playbook -i inventory authorized_key.yml -u "$USER" -k
Why don't you use authorized_key module?
- hosts: all
tasks:
- authorized_key:
user: remote_user_name
state: present
key: "{{ lookup('file', '/local/path/.ssh/id_rsa.pub') }}"
and run playbook with -u remote_user_name -k
I need to make sure, that all my servers per default trust each other, so I don't have to be prompted to trust the hosts when I setup ssh tunnels between my servers. How can I do that with ansible?
All my servers are stop with host names which can be accessed via: "{{ groups['all'] }}"
You've got a couple of options here.
You can ignore known_hosts entirely by adding
StrictHostKeyChecking no
To each servers' /etc/ssh/ssh_config.
Or you could use ssh-keyscan to add all of the hosts to each servers' known hosts with the equivalent of:
ssh-keyscan -H {host_name} >> path/to/known_hosts
This might look something like:
name: Add all hosts in inventory to known_hosts
shell: ssh-keyscan -H {{ item }} >> path/to/known_hosts
with_items: "{{ groups['all'] }}"
Here in my answer to "How to include all host keys from all hosts in group" I created a small Ansible look-up module host_ssh_keys to extract public SSH keys from the host inventory, which I believe addresses your use-case.
Adding all hosts' public ssh keys to /etc/ssh/ssh_known_hosts is then as simple as this, thanks to Ansible's integration of loops with look-up plugins:
- name: Add public keys of inventory hosts to known_hosts
ansible.builtin.known_hosts:
path: /etc/ssh/ssh_known_hosts
name: "{{ item.host }}"
key: "{{ item.known_hosts }}"
with_host_ssh_keys: "{{ groups['mygroup'] }}"
I want to add the ssh key for my private git server to the known_hosts file with ansible 1.9.3 but it doesn't work.
I have the following entry in my playbook:
- name: add SSH host key
known_hosts: name='myhost.com'
key="{{ lookup('file', 'host_key.pub') }}"
I have copied /etc/ssh/ssh_host_rsa_key.pub to host_key.pub and the file looks like:
ssh-rsa AAAAB3NzaC1... root#myhost.com
If I run my playbook I always get the following error message:
TASK: [add SSH host key]
******************************************************
failed: [default] => {"cmd": "/usr/bin/ssh-keygen -F myhost.com -f /tmp/tmpe5KNIW", "failed": true, "rc": 1}
What I am doing wrong?
You can directly use ssh-keyscan within the ansible task:
- name: Ensure servers are present in known_hosts file
known_hosts:
name: "{{ hostvars[item].ansible_host }}"
state: present
key: "{{ lookup('pipe', 'ssh-keyscan {{ hostvars[item].ansible_host }}') }}"
hash_host: true
with_items: "{{ groups.servers }}"
In the above snipped, we iterate over all hosts in the group "servers" defined in your inventory, use ssh-keyscan on them, read the result with pipe and add it using known_hosts.
If you have only one host that you want to add, it's even simpler:
- name: Ensure server is present in known_hosts file
known_hosts:
name: "myhost.com"
state: present
key: "{{ lookup('pipe', 'ssh-keyscan myhost.com') }}"
hash_host: true
Whether you need hash_host or not depends on your system.
Your copy of the remote host public key needs a name, that name needs to match what you specify for your known hosts.
In your case, prepend "myhost.com " to your host_key.pub key file as follows:
myhost.com ssh-rsa AAAAB3NzaC1... root#myhost.com
Reference:
Ansible known_hosts module, specifically the name parameter
Use ssh-keyscan to generate host_key.pub is another way.
ssh-keyscan myhost.com > host_key.pub
This command will generate the format like this.
$ ssh-keyscan github.com > github.com.pub
# github.com SSH-2.0-libssh-0.7.0
# github.com SSH-2.0-libssh-0.7.0
$ cat github.com.pub
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==