Working with Ansible vaults in Go

Vault is a feature of Ansible that allows keeping sensitive data such as passwords or keys in encrypted files, rather than as plaintext in your playbooks or roles. These vault files can then be distributed or placed in source control. Vault is also just a fancy name for files (or strings) with encrypted and formatted content.

Vault secret data typically looks like this:

$ANSIBLE_VAULT;1.1;AES256
63623566326538326634613931303733326439646130316566653930616264656431626135303933
6266626261373039363436353766613666356331653866310a303637623931666464326234616334
34303333663837316437613531383566633065333563616437356337643965336131376266366431
3031303331323232650a373739393962343137316261383931383436633262303661303537326462
3732

Vault files are generated (or modified) with ansible-vault command which is a Python-based CLI tool that ships with Ansible. All secret data is protected by a user-generated password that should never be checked into any source code repository. These secrets are then consumed by Ansible playbooks or by any third-party module.

Reading and writing AES256 vault files is pretty straightforward and there are libraries for other languages like Ruby. After using a small Go script that deals with vault files i finally decided to make an open source package that i can reuse across projects instead of copy pasting it every time.

Package is available on Github: ansible-vault-go

Installation

To install run the command:

go get -u github.com/sosedoff/ansible-vault-go

Documentation is hosted on GoDoc

Full Example

Create a Go file, like ansible.go with the following content:

package main

import (
    "fmt"

    "github.com/sosedoff/ansible-vault-go"
)

func main() {
    // Encrypt secret data
    str, err := vault.Encrypt("secret", "password")
    if err != nil {
        panic(err)
    }
    fmt.Println("encrypted data:", str)

    // Decrypt secret data encrypted in the previos step
    str, err = vault.Decrypt(str, "password")
    if err != nil {
        panic(err)
    }
    fmt.Println("decrypted data:", str)

    // Write secret data to file
    err = vault.EncryptFile("/tmp/vault", "secret", "password")
    if err != nil {
        panic(err)
    }

    // Read existing secret
    str, err = vault.DecryptFile("/tmp/vault", "password")
    if err != nil {
        panic(err)
    }
    fmt.Println("decrypted file:", str)
}

Then simply run:

go run ansible.go

Example output:

encrypted data: $ANSIBLE_VAULT;1.1;AES256
33366263643835313430646262343262353730383036363632396237613433333063333736306339
3535663232333535346336323862343130356232323165640a353961386235363164343566383866
38633037393665353730656539383165346365613936623664336236303633393363396636356536
3965643364366365360a326231636536326537656130313338306530663735303738326663656531
6336
decrypted data: secret
decrypted file: secret

You can also verify that ansible can read our test file:

# Read the file using ansible vault CLI
$ ansible-vault view /tmp/vault
Vault password: **** type password ***

# This is the decrypted contents
secret

That's pretty much it.