Deploying SSL private keys with Ansible

When you’re using Ansible or any other configuration management tool, you might come in contact with deploying SSL certificates sooner or later. While deploying public SSL certificates isn’t a security issue at all, the deployment of private keys become more critical – at least if you want to deploy them securely.

The problem with private keys

The word private already explains a lot, because private keys should be held private – in any case. But if you’re working in a new fancy environment with a lot of automation and configuration management in place, you might ask yourself:

How can I deploy and configure my SSL encrypted application in a most automated way?

Fortunately in Ansible there are several options available.

Option 1: Check only for file permissions

The most simple option is to skip the whole deployment of the certificates and private keys via Ansible. Instead of deploying the certificate, just make sure the files are in place and the permissions are correct:

---
- name: make sure SSL certificate is existing and secured
  file:
    state:  file
    path:   '{{ item.path }}'
    owner:  root
    group:  root
    mode:   '{{ item.mode }}'
  with_items:
    - path: /path/to/my/cert.pem
      mode: '0644'
    - path: /path/to/my/key.pem
      mode: '0600'

You’ll benefit of an error message when the certificate isn’t installed while you’re deploying your application. Of course Ansible will also correct the file permissions, if you’ve installed your certificate manually and forgot to set the permissions.

Unfortunately you’ve to install the certificate manually, because it isn’t stored in the Ansible SCM repository at all.

Option 2: Copy the certificate with Ansible on-demand

However it can be annoying if you’ve to deploy several servers and everything is automated, except for the freaking certificates. So there’s a workaround with an “on-demand” task via ansible command:

# Copy certificate
ansible -m copy -a "src=certificate.pem dest=/path/to/my/certificate.pem owner=root group=root mode=0644" webservers

# Copy private key
ansible -m copy -a "src=key.pem dest=/path/to/my/key.pem owner=root group=root mode=0600" webservers

With the commands above you can simply copy your certificate and private key to all the webservers out there. Depending on your ansible configuration, you might also have to use the -i (inventory) and  -s (sudo) flag.

Unfortunately you still have to copy the certificates to your Ansible control node and execute the commands manually before you’re running your application deployment play. Just make sure you delete the certificate after the deployment from your control node, or at least set the correct permissions so only authorised users can read it.

Option 3: Deploy certificates after prompting for vars

However there’s another alternative which is a mix of the options above. You still use the task from option 1, but instead of the file module you’re using the copy module from option 2. Then instead of defining a fix src, we’re using a variable:

---
- name: make sure SSL certificate is existing and secured
  copy:
    src:    '{{ item.src }}'
    path:   '{{ item.path }}'
    owner:  root
    group:  root
    mode:   '{{ item.mode }}'
  no_log: true
  with_items:
    - src:  '{{ ssl_certificate }}'
      path: /path/to/my/cert.pem
      mode: '0644'
    - src:  '{{ ssl_private_key }}'
      path: /path/to/my/key.pem
      mode: '0600'

Then prompt the user for the certificate and private key paths in the playbook.

---
- hosts: 
  roles:
    - my_app
  vars_prompt:
    ssl_certificate: Enter path to SSL certificate
    ssl_private_key: Enter path to SSL private key

This is the most secure option if you’re removing the certs from your control node after the deployment. Unfortunately it’s not fully automated because you’ve to prompt the user for the paths.

Option 4: Deploy certificates via Ansible play

Of course the automated deployment of credentials or private keys would be really nice, though security is really important. You shouldn’t even think about storing credentials or private keys in an unprotected format in your config management SCM repository out there like other did. Just don’t even think about it – don’t do it – never – seriously, forget it!

Anyway if you want to use automated deployments of certificates and private keys, you might have to store them in your Ansible SCM repository as well. As I already mentioned, don’t store the unprotected! Ansible introduced the Vault in version 1.5, which is a nice piece to keep your sensitive data private. Vaults are like variable files, but they’re encrypted.

First of all, create a new vault or edit an existing one:

# Create a new vault
ansible-vault create certificate.yml

# Edit an existing vault
ansible-vault edit certificate.yml

Now add the certificate and private keys as variables:

---
ssl_certificate: |
  -----BEGIN CERTIFICATE-----
  ...
  -----END CERTIFICATE-----

ssl_private_key: |
  -----BEGIN PRIVATE KEY-----
  ...
  -----END PRIVATE KEY-----

Make sure the vault is loaded, either via vars_files directive in your playbook or via include_vars statement in your role/tasks:

---
- hosts: 
  roles:
    - my_app
  vars_files:
    - vars/certificate.yml
---
- include_vars: certificate.yml

Install the certificate and private key via copy module:

---
- name: make sure SSL certificate is installed
  copy:
    content: '{{ ssl_certificate }}'
    dest: /path/to/my/cert.pem
    owner: root
    group: root
    mode: 0644

- name: make sure SSL private key is installed
  copy:
    content: '{{ ssl_private_key }}'
    dest: /path/to/my/key.pem
    owner: root
    group: root
    mode: 0600
  no_log: true

When you now run the ansible-playbook command, you’ve to set the –ask-vault-pass or –vault-password-file FILE CLI argument:

ansible-playbook play.yml --ask-vault-pass

With the setup you can deploy certificates within your playbook. The nice thing about variable files is, that they can be stored anywhere, so you’re not forced to store everything in your Ansible SCM repository. Of course if you need your Ansible installation to be portable (i.e. for multiple control nodes) you’ve to store your vault in the repository or any other shared storage. But if you’re using only one single control node, you can store your certificate vault outside of your repository as well.

Conclusion

The most secure way is to keep your key really private, so options 1, 2 or 3 might be the “most secure” options. Though it can be annoying.

If you’re looking forward to deploy sensitive data like private keys via Ansible in an automated way, I’ll go with option 3. You might store your data in the Ansible SCM repository, but at least everything is encrypted.

 

13 Comments

  • Thomas Kerpe

    The problem with private keys is … they are private.

    Another option is not to deploy them at all but create them and do the rest on the server.
    This approach is for sure not possible on all environments but it is sometimes a very good solution.

  • Thomas

    copy:
    src: ‘{{ itme.src }}’

    should be ‘{{ item.src }}’ I guess

    • Pascal Stauffer

      Thanks for the hint. I’ve fixed the typo.

  • JamesP

    You can now ansible-vault encrypt arbitrary files so there is no need to embed the contents in variables.

  • Len

    In step 4 content of certificate.yml is wrong. You need spaces:


    ssl_certificate: |
    —–BEGIN CERTIFICATE—–

    —–END CERTIFICATE—–

    ssl_private_key: |
    —–BEGIN PRIVATE KEY—–

    —–END PRIVATE KEY—–

    • Len

      Wrr..


      ssl_certificate: |
      SS—–BEGIN CERTIFICATE—–
      SS…
      SS—–END CERTIFICATE—–

      ssl_private_key: |
      SS—–BEGIN PRIVATE KEY—–
      SS…
      SS—–END PRIVATE KEY—–

      where S = space.

  • Pisces

    Wouldn’t it be better for the certificate itself to have mode ‘0444’ set and keys to have mode ‘0400’?

    The reasoning behind this being, that anyone should be able to read the public certificate (no write, no execute) and the key, well … since it’s immutable and private, only the owner should have read (and only read) access to it.

  • skribbl io

    The information in this article is very important and I want to find out more information related to entertainment games

  • doula

    Lent you have shared, good stuff.