How to upload encrypted file using ansible vault?

2019-02-03 01:19发布

问题:

Does anyone have an example of decrypting and uploading a file using ansible-vault.

I am thinking about keeping my ssl certificates encrypted in source control.

It seems something like the following should work.

---
  - name: upload ssl crt
    copy: src=../../vault/encrypted.crt dest=/usr/local/etc/ssl/domain.crt

回答1:

That's not going to work. What you will get is your encrypted.crt (with Ansible Vault) uploaded literally as domain.crt

What you need to do is make your playbook part of a "Vault" and add a variable that contains your certificate content. Something like this:

---
- name: My cool playbook
  hosts: all

  vars:
    mycert: |
       aasfasdfasfas
       sdafasdfasdfasdfsa
       asfasfasfddasfasdfa


  tasks:
    # Apparently this causes new lines on newer ansible versions
    # - name: Put uncrypted cert in a file
    #   shell: echo '{{ mycert }}' > mydecrypted.pem

    # You can try this as per
    # https://github.com/ansible/ansible/issues/9172
    - copy:
      content: "{{ mycert }}"
      dest: /mydecrypted.pem

    - name: Upload Cert
      copy: src=/home/ubuntu/mydecrypted.pem dest=/home/ubuntu/mydecrypteddest.pem

    - name: Delete decrypted cert
      file: path=/home/ubuntu/mydecrypted.pem state=absent

You can choose to put your mycert variable in a separate variable file using Ansible Vault too.

The copy module has been updated in Ansible 2.1. From the changelog: "copy module can now transparently use a vaulted file as source, if vault passwords were provided it will decrypt and copy on the fly." Noting it here, since some people will inevitably not look past the accepted answer. – JK Laiho



回答2:

The copy module now does this seamlessly as of Ansible 2.1.x. Just encrypt your file with Ansible Vault and then issue the copy task on the file.

(For reference, here's the feature that added this: https://github.com/ansible/ansible/pull/15417)



回答3:

There is a feature request to support this natively in the copy module. But until that is implemented, here is the workaround (similar to @dave1010's answer, but repeating common parts for completeness):

Create a secrets.yml file encrypted with ansible vault which contains your secrets, for example:

---
private_ssl_key: |
  -----BEGIN PRIVATE KEY-----
  abcabcabcabcabcabcabcabcabc
  -----END PRIVATE KEY-----

private_crt: |
  -----BEGIN CERTIFICATE-----
  abcabcabcabcabcabcabcabcabc
  -----END CERTIFICATE-----

In your playbook, include it:

vars_files:
  - secrets.yml

Then you can use the variables in tasks:

- name: Copy private kay
  copy: content="{{ private_ssl_key }}" dest=/some/path/ssl.key

However, this doesn't work if the file that you are trying to copy is a binary file. In that case, you need to first encode the content with base64:

cat your_secret_file | /usr/bin/base64

Then put the base64 encoded value in your secrets.yml file, e.g.:


crt_b64: |
  ndQbmFQSmxrK2IwOFZnZHNJa0sKICAxdDhFRUdmVzhMM...

Then you can create the remote file in two steps:

- name: Copy certificate (base64 encoded)
  copy: content="{{ crt_b64 }}" dest=/some/path/cert.b64

- name: Decode certificate
  shell: "base64 -d /some/path/cert.b64 > /some/path/cert.txt"
  args:
    creates: /some/path/cert.txt

Note that you could delete the temporary cert.b64 file on the remote host. But then re-running the playbook will re-create it instead of skipping this task. So, I prefer to leave it there.

UPDATE: This feature has been implemented in Ansible 2.1.

copy module can now transparently use a vaulted file as source, if vault passwords were provided it will decrypt and copy on the fly.



回答4:

I used a template and a vars_file to do it:

In your top-level playbook:

vars_files:
  - secretvars.yml

In a task:

- name: Private ssl key
  template: src=etc-ssl-private-site.key dest=/etc/ssl/private/site.key

In the template (etc-ssl-private-site.key) all you need is the variable:

{{ private_ssl_key }}

In the encrypted secretvars.yml (encrypt this with ansible-vault):

---

private_ssl_key: |
  -----BEGIN PRIVATE KEY-----
  abcabcabcabcabcabcabcabcabc
  -----END PRIVATE KEY-----


回答5:

Update: As of April 2016 my Github PR has been merged and is available in Ansible 2.1 and later. The below was an interim solution until the PR was merged.

Wanting to do the same thing I created an action plugin to implement the feature. This is available via github. The plugin is exactly the copy action plugin as shipped with ansible, but with support for vault decryption.

You can use it like this:

- name: Copy Some Secret File
  copyv: src="secret.txt" dest="/tmp/"

if secret.txt is encrypted (and the vault password is supplied) then it will be decrypted and copied.



回答6:

I think, you have a simpler way to do this.

If you use certificate+key in one file in some format (like pkcs12 or just concatenated), you can use generic openssl (or gpg, or something else) encryption. It will look like this:

openssl enc -e -aes-256-ctr -in original.pem -out encrypted.aes -k <pass-vault>

After that you can just copy encrypted.aes to remote host and decrypt it in-place:

- name: copy encrypted cert and key
  copy: src=encrypted.aes dest=/root/ansible-files/ mode=0600

- name: decrypt cert and key
  command: openssl enc -aes-256-ctr -d -in /root/ansible-files/encrypted.aes -out <dest> -k {{ pass-vault }}

If you have separate key file in pem or der format, you can use

openssl rsa -in original.pem -out encrypted.pem -aes256 -passout pass:<pass-vault>


回答7:

Until the 'copy' module has been extended to automatically decrypt vault files, here's a simple workaround:

When stdout is not a tty, ansible-vault view <file> prints cleartext to stdout without invoking a pager.

In combination with a 'pipe' lookup, this behavior can be used with a vault password file to feed into the copy module's 'content' option:

- name: "install host key"
  copy: content="{{ lookup('pipe', 'ansible-vault view ' + src_key_file) }}"
        dest={{ dest_key_file }}


回答8:

You can also use local_action to temporairly decrypt your file as part of the playbook:

- name: "temporairly decrypt the twpol.enc"
  sudo: False
  local_action: shell ansible-vault view --vault-password-file {{ lookup('env', 'ANSIBLE_VAULT_PASS_FILE') }} ./roles/copykey/files/key.enc > ./roles/copykey/files/key.txt 

- name: "copy the key to the target machine."
  copy: src=key.txt dest=/tmp

- name: "remove decrypted key.txt file"
  sudo: False
  local_action: rm ./roles/copykey/files/key.txt


回答9:

Ansible 2.5 added the parameter decrypt to the copy module.

For example, if you encrypted your file using something like:

$ ansible-vault encrypt vault/encrypted.crt

Now you can use copy + decrypt:

---
  - name: upload ssl crt
    copy:
      src: path/to/encrypted-with-vault.crt
      dest: /usr/local/etc/ssl/domain.crt
      decrypt: yes
      mode: 0600


回答10:

+1 for the copy: content= {{ private_ssl_key }}" method suggested by @utapyngo above.

If you are doing your key distribution as a role, rather than just in a playbook (and why not, since key distribution is something you might need again later), keep in mind the following:

  • You only get one file for your vars, so all the keys (say you have different bundles based on host machines or whatever) have to go together in <role>/vars/main.yml
  • Variables in this <role>/vars/main.yml are referenceable without any paths (that's nice!)
  • Remember that whenever you want the content of the variable, you need quotes AND curlies, i.e., "{{ your_variable_name }}"
  • If you want to copy more than one file per task, you need a with_items: loop
  • If you want to keep your sensitive data that you've gone through so much trouble to encrypt in the first place off the screen, a neat trick is to next your key variables inside a dictionary; that way, in your with_items loop you are feeding it the dictionary key rather than the contents of the variable itself.