southalc / vault

Puppet module for use with Hashicorp Vault
8 stars 8 forks source link

try to JSON::parse values from vault_hiera_hash #16

Closed Enucatl closed 2 years ago

Enucatl commented 2 years ago

Hi, I found that this little addition works very well for my use case, do you think it's good in general?

Goal

Allow storing hashes in the vault as json, they can then be retrieved as hashes and even merged by the puppet lookup_option strategy = "deep".

Example

1. Store part of a hash in the vault

vault kv patch -mount=secret puppet config='{"global": {"password": "supersecret"}}'

2. Not all of it is secret, so I can store the rest in yaml

---
lookup_options:
  config:
    merge:
      strategy: deep

config:
  global:
    log: 'file'
    ipv4: true
    ipv6: true

3. Output

puppet lookup config --merge deep --environment production --explain
[...]
    Merged result: {
      "global" => {
        "log" => "file",
        "ipv4" => true,
        "ipv6" => true,
        "password" => "supersecret"
      }
    }

Benefits

Mainly this avoids writing puppet code to handle this situation, and keeps all the data operations in hiera, reusing the merge strategy concept.

southalc commented 2 years ago

I prefer to keep the Vault data as simple as possible and use just key/value pairs. For hiera hash data where some values in the hash are secrets, I use interpolation to reference the value I want. You have to update your main YAML file(s) for this, but it keeps the secrets nicely separated. I also tag the reference with a comment so it's clear that the value is expected to be in Vault. For example, in your current Vault kv secrets path used by puppet, add a new key for "global_password" with whatever value you want. Then update the config hash to reference the value like this:

config: global: log: 'file' ipv4: true ipv6: true password: "%{lookup('global_password')}" # secure value from Vault

Enucatl commented 2 years ago

You're right, I like your solution better as it's simpler. I'm now facing another issue though that was accidentally solved by this change: all vault values are returned as strings. What if the class requires an integer? This worked with json parsing because in ruby

require "json"
JSON.parse("12345")
=> 12345

I'm now doing stuff like site.pp

port => Integer($port)

Is there a better way?

Enucatl commented 2 years ago

For whoever stumbles upon this question, I found the answer myself: https://puppet.com/docs/puppet/7/hiera_merging.html#setting_lookup_options_to_refine_the_result_of_a_lookup-make-hiera-return-data-by-casting

For example, to convert a string ("042") to an Integer with explicit decimal (base 10) interpretation of the string:

mymodule::mykey: "042"
lookup_options:
  mymodule::mykey:
    convert_to:
      - "Integer"
      - 10
southalc commented 2 years ago

Good find. Thanks for sharing!