1

Context: I'm trying to automate the provision of a fresh new server, but when a new machine is spawned and my ansible playbook is played against it from my provisioning server the usual message pops out:

The authenticity of host '192.168.1.25 (192.168.1.25)' can't be established.
ECDSA key fingerprint is SHA256:QF/AyFhYXaz5bjZ1O+kvceoOjBzmI8M1PYmg3lukYmE.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

I am aware this question has been answered a couple times already but, I do not want to add this line to my .cfg file or give the relative argument when I launch an ansible-playbook command.

Problem: So this answer came to my attention https://stackoverflow.com/a/54735937/18647199

I copy pasted the two tasks in my playbook and if they're by themselves the script runs properly. Skipping the aforementioned prompt (even though it skips it on one server that I still have to made the first connection) see:


TASK [Check known_hosts for 192.168.1.14] **************************************
ok: [192.168.1.16 -> localhost]
ok: [192.168.1.14 -> localhost]
ok: [192.168.1.25 -> localhost]

TASK [Ignore host key for 192.168.1.14 on first run] ***************************
skipping: [192.168.1.14]
skipping: [192.168.1.16]
skipping: [192.168.1.25]

PLAY RECAP *********************************************************************
192.168.1.14               : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
192.168.1.16               : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
192.168.1.25               : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0  

But if I add just one more task to it, it asks again for the auth prompt that I'm trying to skip. p.s. using OpenSSH, latest current version.

What I'm trying to run:

---

#all

- hosts: all
  #connection: local
  become: true
  gather_facts: false #otherwise ssh prompt appears
  tasks:

  - name: Check known_hosts
    local_action: shell ssh-keygen -F "{{ inventory_hostname }}"
    register: is_known
    failed_when: false
    changed_when: false
    ignore_errors: yes

  - name: debug message
    debug:
      msg: the "{{ inventory_hostname }}"" was tested with output "{{ is_known }}"


  - name: Ignore host key for "{{ inventory_hostname }}" on first run
    when: is_known.rc == 1
    set_fact:
      ansible_ssh_common_args: '-o StrictHostKeyChecking=no'

 
 
  - name: Bootstrap check
    stat:
      path: /home/bot/bootstrapped-ok
    register: bootstrap_result

[..] more code

Debug output:

    ansible-playbook debug-bootstrap.yml 

PLAY [all] *********************************************************************

TASK [Check known_hosts] *******************************************************
ok: [192.168.1.16 -> localhost]
ok: [192.168.1.14 -> localhost]
ok: [192.168.1.25 -> localhost]

TASK [debug message] ***********************************************************
ok: [192.168.1.14] => {
    "msg": "the \"192.168.1.14\"\" was tested with output \"{'msg': 'non-zero return code', 'cmd': 'ssh-keygen -F \"192.168.1.14\"', 'stdout': '', 'stderr': 'do_known_hosts: hostkeys_foreach failed: No such file or directory', 'rc': 255, 'start': '2022-04-02 12:30:50.940041', 'end': '2022-04-02 12:30:50.943287', 'delta': '0:00:00.003246', 'changed': False, 'failed': False, 'stdout_lines': [], 'stderr_lines': ['do_known_hosts: hostkeys_foreach failed: No such file or directory'], 'failed_when_result': False}\""
}
ok: [192.168.1.16] => {
    "msg": "the \"192.168.1.16\"\" was tested with output \"{'msg': 'non-zero return code', 'cmd': 'ssh-keygen -F \"192.168.1.16\"', 'stdout': '', 'stderr': 'do_known_hosts: hostkeys_foreach failed: No such file or directory', 'rc': 255, 'start': '2022-04-02 12:30:50.937097', 'end': '2022-04-02 12:30:50.941015', 'delta': '0:00:00.003918', 'changed': False, 'failed': False, 'stdout_lines': [], 'stderr_lines': ['do_known_hosts: hostkeys_foreach failed: No such file or directory'], 'failed_when_result': False}\""
}
ok: [192.168.1.25] => {
    "msg": "the \"192.168.1.25\"\" was tested with output \"{'msg': 'non-zero return code', 'cmd': 'ssh-keygen -F \"192.168.1.25\"', 'stdout': '', 'stderr': 'do_known_hosts: hostkeys_foreach failed: No such file or directory', 'rc': 255, 'start': '2022-04-02 12:30:50.978944', 'end': '2022-04-02 12:30:50.982119', 'delta': '0:00:00.003175', 'changed': False, 'failed': False, 'stdout_lines': [], 'stderr_lines': ['do_known_hosts: hostkeys_foreach failed: No such file or directory'], 'failed_when_result': False}\""
}

TASK [Ignore host key for "192.168.1.14" on first run] *************************
skipping: [192.168.1.14]
skipping: [192.168.1.16]
skipping: [192.168.1.25]

TASK [Bootstrap check] *********************************************************
The authenticity of host '192.168.1.25 (192.168.1.25)' can't be established.
ECDSA key fingerprint is SHA256:QF/AyFhYXaz5bjZ1O+kvceoOjBzmI8M1PYmg3lukYmE.
Are you sure you want to continue connecting (yes/no/[fingerprint])? ok: [192.168.1.16]
ok: [192.168.1.14]

So it seems like the command shell ssh-keygen -F "{{ inventory_hostname }}" isn't doing what it's supposed to do as if we had to launch that via terminal.

Question: Does anyone know how to implement that "one-time skip" or has a better way to do this for a fully automated provisioning / deploy?

(I tried to create an unique .yml file with scarce results, I hit a wall and have not many ideas left on how to continue a fully automated provisioning)

  • 3
    Hi simone.benati welcome to SO. That question you linked to is absolutely **jam packed** with solutions, so what makes your circumstance worth a new question other than "I don't want to use any of those answers?" That aside, this is a programming forum, so please [edit your question](https://stackoverflow.com/posts/71712244/edit) and include your code so we can give you actionable feedback upon it. Good luck – mdaniel Apr 02 '22 at 02:51
  • Thank you @mdaniel for helping me out. I provided more informations now. I thought I was clear of why I don't want to use those answers because it would compromise the security.. And to me it just doesn't feel right, that's why I picked and linked that answer that caught my attention, but I am not able to make it work. – simone.benati Apr 02 '22 at 10:36
  • `... a better way to do this for a fully automated provisioning / deploy` <= yes, simply do it the way you don't want to: disable strict host key checking. What you are currently trying to do will not add much extra security as you will accept a new host key anyway when you make a connection from a virgin environment, which will be more or less on every run if you use a CI with on demand slave/runners. If you really want full extra security, gather all your target keys before hand, put them in your inventory, and reconstruct you known_hosts file prior to making any remote connection. – Zeitounator Apr 02 '22 at 13:49
  • @Zeitounator thank you for your input. Regarding the second part of your answer, you'd say that I should manually connect on each target host DIRECTLY, and copy its key and paste it in my inventory? – simone.benati Apr 02 '22 at 15:32
  • If you want 100% security on systems starting from scratch, you need to get all host keys and reconstruct the `known_hosts` file to make sure you trust the targets. There are other options (like user and host key signing) which are going far beyond this question and are off topic on SO (better suited for https://superuser.com or https://serverfault.com). About the current issue and said differently, automatically accepting a host key on first connection on a CI system running from scratch each time will only make sure the host does not change during one single run, not between runs. – Zeitounator Apr 02 '22 at 17:03

1 Answers1

0

Just added mine answer to How to ignore ansible SSH authenticity checking? which list lots of options.

This is what we are using for stable hosts (when running the playbook from Jenkins and you simply want to accept the host key when connecting to the host for the first time) in inventory file:

[all:vars]
ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new'

And this is what we have for temporary hosts (in the end this will ignore they host key at all):

[all:vars]
ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'

There is also environment variable or you can add it into group/host variables file. No need to have it in the inventory - it was just convenient in our case.

Maybe this could help?

jhutar
  • 1,369
  • 2
  • 17
  • 32
  • Thank you very much for your input. So basically line 1 is for all the new hosts that are going to be in production (for example) and therefore it will autoaccept the warning "Are you sure you want to continue connecting .... " Line 2 is to ignore the same warning and therefore not save the key in the host. Did i get this right? – simone.benati Sep 16 '22 at 09:57
  • Hello @simone.benati. Exactly. Option 1 automatically accepts key but only on first run (so if somebody forges it later, it will trigger connection error) and option 2 just does not care at all. – jhutar Sep 17 '22 at 18:30