7

I'm trying to re-generate ssh host keys on a handful of remote servers via ansible (and ssh-keygen), but the files don't seem to be showing up. The playbook runs OK, but the files on the remote are not altered.

I need to resort to the echo -e hackery since these remotes are running Ubuntu 14.04 and haven't the correct version of the python-pexpect available (according to ansible).

What am I missing? My playbook and output are below:

playbook

---
- hosts: all
  become: true
  gather_facts: false

  tasks:
    - name: Generate /etc/ssh/ RSA host key
      command : echo -e 'y\n'|ssh-keygen -q -t rsa -f /etc/ssh/ssh_host_rsa_key -C "" -N ""
      register: output
    - debug: var=output.stdout_lines

    - name: Generate /etc/ssh/ DSA host key
      command : echo -e 'y\n'|ssh-keygen -q -t dsa -f /etc/ssh/ssh_host_dsa_key -C "" -N ""
      register: output
    - debug: var=output.stdout_lines

    - name: Generate /etc/ssh/ ECDSA host key
      command : echo -e 'y\n'|ssh-keygen -q -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -C "" -N ""
      register: output
    - debug: var=output.stdout_lines

output

$ ansible-playbook ./playbooks/ssh-hostkeys.yml -l myhost.mydom.com, 
SUDO password: 

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

TASK [Generate /etc/ssh/ RSA host key] ******************************************************************
changed: [myhost.mydom.com]

TASK [debug] ********************************************************************************************
ok: [myhost.mydom.com] => {
    "output.stdout_lines": [
        "y", 
        "|ssh-keygen -q -t rsa -f /etc/ssh/ssh_host_rsa_key -C  -N "
    ]
}

TASK [Generate /etc/ssh/ DSA host key] ******************************************************************
changed: [myhost.mydom.com]

TASK [debug] ********************************************************************************************
ok: [myhost.mydom.com] => {
    "output.stdout_lines": [
        "y", 
        "|ssh-keygen -q -t dsa -f /etc/ssh/ssh_host_dsa_key -C  -N "
    ]
}

TASK [Generate /etc/ssh/ ECDSA host key] ****************************************************************
changed: [myhost.mydom.com]

TASK [debug] ********************************************************************************************
ok: [myhost.mydom.com] => {
    "output.stdout_lines": [
        "y", 
        "|ssh-keygen -q -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -C  -N "
    ]
}

PLAY RECAP **********************************************************************************************
myhost.mydom.com : ok=6    changed=3    unreachable=0    failed=0  
9

As far as I know the only reason why you would need to pipe a 'y' to ssh-keygen, is if your command is replacing an existing file. In my opinion this is not a good way to do something from a configuration management tool.

You should adjust your tasks to make them idempotent. Specifically if you add the creates: filename to your command, then the new keys will only be created when they don't already exist, instead of being replaced each time you run that playbook.

---
- hosts: all
  become: true
  gather_facts: false

  tasks:
  - name: Generate /etc/ssh/ RSA host key
    command : ssh-keygen -q -t rsa -f /etc/ssh/ssh_host_rsa_key -C "" -N ""
    args:
      creates: /etc/ssh/ssh_host_rsa_key

  - name: Generate /etc/ssh/ DSA host key
    command : ssh-keygen -q -t dsa -f /etc/ssh/ssh_host_dsa_key -C "" -N ""
    args:
      creates: /etc/ssh/ssh_host_dsa_key

  - name: Generate /etc/ssh/ ECDSA host key
    command : ssh-keygen -q -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -C "" -N ""
    args:
      creates: /etc/ssh/ssh_host_ecdsa_key

If for some reason you wanted to replace those keys for example if they were too old or something you might want to add another task to remove them. Here is a simple delete

- file:
    state: absent:
    path: "{{item}}"
  loop:
  - /etc/ssh/ssh_host_rsa_key
  - /etc/ssh/ssh_host_dsa_key
  - /etc/ssh/ssh_host_ecdsa_key

If you wanted to delete files generated before a certain time, you could use the stat module to retrieve details about this files, and setup when conditions to selectively remove them if they were older then a certain date or something.

  • Great example and thanks for the thoughts on maintaining idempotence. For my use case, I'm provisioning cloned Virtual Machines so there will always be keys to overwrite. I kind of need the nuclear option to just remove and replace. – Server Fault May 3 '18 at 18:04
  • If you always want it to remove, then I would probably do the file: state:absent ... approach over piping stuff to ssh-keygen. Though there probably isn't that much of a difference. – Zoredache May 3 '18 at 19:45
  • Ah, ok. that makes more sense. I didn't know about absent a couple days ago. Effectively that will delete the file prior to re-generating the key. It's a much clearer approach. Thanks. – Server Fault May 3 '18 at 20:09
  • 1
    ansible 2.7.2 i believe fails on creates:. it should be under args: docs.ansible.com/ansible/latest/modules/command_module.html – activedecay Jan 3 at 18:17
  • 1
    @activedecay, Thanks. You are right. I have updated the post. That was almost certainly me making a mistake. I hadn't actually tested the play, I was just making something really quick for this post to demonstrate the general idea. – Zoredache Jan 3 at 19:01
6

The ansible command module does not pass commands through a shell. This means you can't use shell operators such as the pipe, and that is why you are seeing the pipe symbol in the output. As far as ansible is concerned, it has executed the command echo with all of the rest of the line as arguments to echo.

If you need the command line processed by a shell, use shell instead of command.

And, there ought to be a better way to regenerate ssh host keys, but I can't find one right now...

  • Thanks for the shell vs. command tip (works fine now) I didn't know - still pretty new to ansible – Server Fault Apr 30 '18 at 19:24
  • Regarding the last statement (at least on CentOS/RHEL) if you remove the old keys and restart the daemon host keys are regenerated for you. You'll need to restart the service anyway so this definitely seems somewhat better. – Aaron Copley Apr 30 '18 at 19:25
  • @AaronCopley I was more referring to an Ansible role than the distro service. I'm aware that most major distros have a systemd service that generates ssh host keys. Unfortunately that service has subtle distro-specific differences (it's even different between CentOS and Fedora). A role would be a nice way to encapsulate all that, but I can't find one offhand. – Michael Hampton Apr 30 '18 at 19:27
  • No worries, just thought I'd mention it. (You may know, but OP might not.) – Aaron Copley Apr 30 '18 at 19:31
  • @AaronCopley - incidentally, this is what I ended up doing. the echo ... bit didn't work after a second run (I was testing in /tmp/ which the keys did not exist the first time around). I resorted to removing the host keys first, as you mention, and the generating new ones. As far as having the keys automatically regenerated, this is dependent on your distribution, correct? Not all Linux distros use systemd. – Server Fault Apr 30 '18 at 19:40
1

sorry, but the i could not use "creates" in a task. i obtained the following error:

ERROR! 'creates' is not a valid attribute for a Task

consquently, i use the following tasks:

- name: remove existing ssh_host keys
  file: path={{ item }} state=absent
  with_items:
    - "/etc/ssh/ssh_host_rsa_key"
    - "/etc/ssh/ssh_host_dsa_key"
    - "/etc/ssh/ssh_host_ecdsa_key"

- name: Generate /etc/ssh/ RSA host key
  command : ssh-keygen -q -t rsa -f /etc/ssh/ssh_host_rsa_key -C "" -N ""

- name: Generate /etc/ssh/ DSA host key
  command : ssh-keygen -q -t dsa -f /etc/ssh/ssh_host_dsa_key -C "" -N ""

- name: Generate /etc/ssh/ ECDSA host key
  command : ssh-keygen -q -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -C "" -N ""
  • 2
    Use a current version of Ansible. – Michael Hampton Jul 10 '18 at 18:40
  • You are right, my Ansible version is a bit old : 2.0.0.2... (On Ubuntu 16.04). I have to change ! – MaxiReglisse Jul 11 '18 at 6:22
1

@Zoredache has the correct answer but it fails (noted by @MaxiReglisse) for recent versions of Ansible. Use the following code instead:

---
- hosts: all
  become: true
  gather_facts: false

  tasks:
  - name: Generate /etc/ssh/ RSA host key
    command : ssh-keygen -q -t rsa -f /etc/ssh/ssh_host_rsa_key -C "" -N ""
    args:
      creates: /etc/ssh/ssh_host_rsa_key
1

Another option is to use user module. Positive side of this is that you'll get an idempotent task. Here is an example how to generate ssh keys on localhost:

- name: Generate ssh keys
  local_action:
    module: "user"
    name: "{{ lookup('env','USER') }}"
    generate_ssh_key: true
    ssh_key_type: "{{ item.0 }}"
    ssh_key_bits: "{{ item.1 }}"
    ssh_key_file: "{{ playbook_dir }}/{{ item.0 }}_{{ item.1 }}_key"
  with_together:
  - [ 'rsa', 'dsa' ]
  - [ 2048, 1024 ]
  loop_control:
    label: "{{ item.0 }}_{{ item.1 }}_key"

- name: Copy generated ssh keys to remote machine
  copy:
    src: "{{ playbook_dir }}/{{ item.0 }}_{{ item.1 }}_key"
    dest: "/etc/ssh/ssh_host_{{ item.0 }}_key{{ item.1 }}"
  with_nested:
  - [ 'rsa', 'dsa' ]
  - [ '', '.pub' ]
  notify:
  - Restart sshd
  loop_control:
    label: "/etc/ssh/ssh_host_{{ item.0 }}_key{{ item.1 }}"
  • 1
    Isn't that for user keys, not host keys? – MadHatter Feb 15 at 8:17
  • You can use it for host keys too because it's the same thing actually. Just don't forget to restore selinux context if you use SELinux in enforcing mode – HeroFromEarth Feb 15 at 8:34
  • It's not clear to me from the documentation that you can. If you'd rewrite your answer so it explicitly shows host keys being created, I'd remove my downvote. – MadHatter Feb 15 at 8:51
  • Okay, maybe it was unclear. I've added another task to explain how to copy these keys at a remote machine. And of course it's only my case (I need the same keys on a few machines for a cluster so I need to generate them on localhost) and I'm pretty sure that you can use 'user' module to generate keys for ssh server on the remote machine (look at 'ssh_key_file') – HeroFromEarth Feb 15 at 9:12
  • I'm still not sure it's anything other than a hack (not least because it leaves one user with a copy of the host private key!) but at least it's something that will now answer the question as asked, so I've removed my downvote. – MadHatter Feb 15 at 9:17

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.