5

I have a file on my local machine that I want to upload to a remote server, it contains confidential information that I don't want exposed in my VCS. It also has some text I need to replace dynamically in it (at the moment as Jinja2 placeholders "{{}}").

If I use the copy module, then the file is un-vaulted when I upload it, but obviously the placeholders are note replaced.

If i use the template module, then it doesn't un-vault the file and hence it is uploaded in its encrypted format (and also doesn't replace placeholders because they are obfuscated by the encryption).

How can I both template and un-vault a file (using ansible) to the remote server?

James
  • 1,237
  • 4
  • 22
  • 43
  • Why not create variables for that parts that you want to keep secret and then just use the template module after vaulting the secret vars? – ydaetskcoR Jun 07 '16 at 16:40
  • @ydaetskcoR I think that in some edge cases you might not want to reveal your configuration files - and putting the whole config template into a secret var string is hard to maintain. – fishi0x01 Jun 07 '16 at 17:07

4 Answers4

10

As already mentioned in the comments, you could set your secrets in variables and render them into the templates during provision, but if for some reason you want to keep your whole template a secret, there are some workarounds to also do that.

Handling encrypted templates

As a workaround you could temporarily decrypt the template locally and after the rollout delete the decrypted file with the local_action module. Lets assume your encrypted template resides as template.enc in your roles templates directory.

---

- name: Decrypt template
  local_action: "shell {{ view_encrypted_file_cmd }} {{ role_path }}/templates/template.enc > {{ role_path }}/templates/template"
  changed_when: False

- name: Deploy template
  template:
    src=templates/template
    dest=/home/user/file

- name: Remove decrypted template
  local_action: "file path={{ role_path }}/templates/template state=absent"
  changed_when: False

Please note the changed_when: False. This is important in order to run idempotence tests with your ansible roles - otherwise each time you run the playbook a change is signaled. In group_vars/all.yml you could set a global decrypt command for reuse, e.g., as view_encrypted_file_cmd.

group_vars/all.yml

---

view_encrypted_file_cmd: "ansible-vault --vault-password-file {{ lookup('env', 'ANSIBLE_VAULT_PASSWORD_FILE') }} view"

Handling encrypted static files

One way: as template

You could set the content of your secret static file (e.g., a private key) as a variable in ansible and provision it as a template.

var.yml

---

my_private_key: |
  YOUR KEY
  asfdlsafkj
  asdlkfjasf

templates/private_key.j2

{{ private_key }}

tasks/main.yml

---

template: 
  src=templates/private_key.j2
  dest=/home/user/.ssh/id_rsa
  vars:
    private_key: "{{ my_private_key }}"

Another way: via lookup pipe

Another way would be to use the lookup module with pipe to set the content property within the copy module - that way you do not need an extra variable.

---

- copy:
    dest=/your/dest
    content=lookup('pipe', 'VAULT_PASSWORD_FILE=path/to/pass_file ansible-vault view path/to/file.enc')
fishi0x01
  • 3,579
  • 21
  • 25
6

Now Ansible 2.4 supports decrypt option on copy module: http://docs.ansible.com/ansible/latest/copy_module.html#options

2

This shouldn't be used any more, see the comment below...


There is another possibility similar to the solution by fishi in case of a static file. By using copy instead of template there is no need for an additional file.

Using vars.yml:

Store in a vault-encrypted vars.yml:

encrypted_content: |
  foo = {{ bar }}
  password = abcabc
  ...

Code for the task:

- name: Save encrypted template
  copy: 
    content: "{{ encrypted_content }}"
    dest: /path/to/destination

Using a separate YAML file

You can also store the encrypted template code in another YAML file. This is useful, wenn vars.yml shall not be encrypted. For example vars/encrypted.yml might be:

encrypted_content: |
  foo = {{ bar }}
  password = abcabc
  ...

Code for the task:

- name: Read encrypted variable file
  include_vars: encrypted.yml
  no_log: true

- name: Save encrypted template
  copy: 
    content: "{{ encrypted_content }}"
    dest: /path/to/destination
Stephan Kulla
  • 4,739
  • 3
  • 26
  • 35
  • 1
    [According to Ansible’s documentation](https://stackoverflow.com/a/26640778/735926) "`Using a variable in the content field will result in unpredictable output.`" so this is not an option. – bfontaine Dec 15 '21 at 14:30
0

In short, use copy module and ansible-vault.

Here is the complete example for copying a local encrypted file named hello.vault to hello.txt on remote server. Its clear content is WORLD and the encryption key is 1234.

  1. Create your vault file hello.vault:
$ ansible-vault create hello.vault
New Vault password: 1234
Confirm New Vault password: 1234
## Then input your secret and exit the editor ##
WORLD

$ cat hello.vault
$ANSIBLE_VAULT;1.1;AES256
39653932393834613339393036613931393636663638636331323034653036326237373061666139
6434373635373065613135633866333733356532616635640a663739306639326535336637616138
39666462343737653030346463326464333937333161306561333062663164313162376564663262
3533393839633466300a666661303363383265613736376564623465613165656531366331366664
6436
  1. Create your password file, e.g. vault.key as follow
1234
  1. Use copy module to transfer the vault file to clear text on webserver(defined in inventory).
ansible webserver -i inventory --vault-password-file=vault.key \
        -m copy -a "src=hello.vault dest=hello.txt"

ansible webserver -i inventory -m command -a "cat hello.txt"
WORLD
wizawu
  • 1,881
  • 21
  • 33