├── .gitignore ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── RELEASING.md ├── Rakefile ├── hiera-vault.gemspec └── lib └── hiera └── backend └── vault_backend.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /test/tmp/ 9 | /test/version_tmp/ 10 | /tmp/ 11 | 12 | ## Specific to RubyMotion: 13 | .dat* 14 | .repl_history 15 | build/ 16 | 17 | ## Documentation cache and generated files: 18 | /.yardoc/ 19 | /_yardoc/ 20 | /doc/ 21 | /rdoc/ 22 | 23 | ## Environment normalisation: 24 | /.bundle/ 25 | /vendor/bundle 26 | /lib/bundler/man/ 27 | 28 | # for a library or gem, you might want to ignore these files since the code is 29 | # intended to run in multiple environments; otherwise, check them in: 30 | # Gemfile.lock 31 | # .ruby-version 32 | # .ruby-gemset 33 | 34 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 35 | .rvmrc 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2.0 4 | cache: bundler 5 | 6 | deploy: 7 | provider: rubygems 8 | on: 9 | tags: true 10 | api_key: 11 | secure: SRIaGxxXtAzJKQ7SZ4mTKGEUFg+ZGtejsjN11CAVwfdru20OeJRlU3sfkL2AxaLhuMr8AkRvRqSi7pVvDBG/u5wcMf1HyiXj9Xbr/O3Rf11NuGjEWC1WUik/Hov0wMnTtt3O2oZAHTlBOryhis5zcHvS25v1ikxjlfybH3g2EFhNFU4jiGX8es48Tt9mDvuHxtX962YaCtceAn/wW/ymZQNC8DxhrplR4wW1glhl3B7g+5qx6ntcWwEr5Gq1XPBuSuHeznHCjHzvL/TNCx0s2CFGQDK2XgRd/R9CbEE2RPXQHCFcKUSqAWGlbMllQJF6jpdd4fzO+lwcaALARFghWvumnwmxixKjT96MZo6NbfABY+cOewefefJdI6r0SnNQybeA/3MtG/8GApI9F+qCsSCKoi2bMcye4OJ5m6UvmIex76Ssn8QYDIrwBGAgLBXriGcw/tOr3jEBpcwlwUupIF9a17imTysDGhoFT/+EciqWs1KQ0r3I1+jql+Nsjag2xW+cQuKbYK2uB2a5dTS6mBwnipSw6wv5EzCHelptzFTOvtiTfUyMg1KKQ+c2WyfiqBBOB+fTRpPimiPifd1PS4VnUbTm0wcyf4qSgchDrzAb6lt07a5yXKXCekiA1aOOepbe/rxaq+dtUuBswgZvypVaO6P2lYTnSFHnb06vBjc= 12 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem "puppet", ENV['PUPPET_VERSION'] || '~> 3.8' 4 | gem "vault", '~> 0.4' 5 | gem "rake" 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | CFPropertyList (2.2.8) 5 | facter (2.4.6) 6 | CFPropertyList (~> 2.2.6) 7 | hiera (1.3.4) 8 | json_pure 9 | json_pure (1.8.3) 10 | puppet (3.8.6) 11 | facter (> 1.6, < 3) 12 | hiera (~> 1.0) 13 | json_pure 14 | rake (11.1.2) 15 | vault (0.4.0) 16 | 17 | PLATFORMS 18 | ruby 19 | 20 | DEPENDENCIES 21 | puppet (~> 3.8) 22 | rake 23 | vault (~> 0.4) 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2015 Jonathan Sokolowski 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version Badge](https://img.shields.io/gem/v/hiera-vault.svg)](https://rubygems.org/gems/hiera-vault) 2 | [![Build Status](https://travis-ci.org/jsok/hiera-vault.svg?branch=master)](https://travis-ci.org/jsok/hiera-vault) 3 | 4 | # hiera-vault 5 | A Hiera backend to retrieve secrets from Hashicorp's Vault 6 | 7 | [Vault](https://vaultproject.io) secures, stores, and tightly controls access to tokens, passwords, certificates, API keys, and other secrets in modern computing. Vault handles leasing, key revocation, key rolling, and auditing. Vault presents a unified API to access multiple backends: HSMs, AWS IAM, SQL databases, raw key/value, and more. 8 | 9 | ## Configuration 10 | 11 | You should modify `hiera.yaml` as follows: 12 | 13 | :backends: 14 | - vault 15 | 16 | :vault: 17 | :addr: http://127.0.0.1:8200 18 | :token: fake 19 | 20 | Alternatively (and recommended) you can specify your vault client configuration 21 | via the same environment variables read by 22 | [vault-ruby](https://github.com/hashicorp/vault-ruby#usage), e.g. 23 | 24 | VAULT_TOKEN=secret hiera -c hiera.yml foo 25 | 26 | 27 | ## Lookups 28 | 29 | ### Hash - default 30 | 31 | Since vault stores data in Key/Value pairs, this naturally lends itself to 32 | returning a Hash on lookup. 33 | For example: 34 | 35 | vault write secret/foo value=bar other=baz 36 | 37 | The hiera lookup for `foo` will return a Hash: 38 | 39 | {"value"=>"bar","other"=>"baz"} 40 | 41 | ### Single Value - optional 42 | 43 | If you use just a single field to store data, eg. "value" - you can request that just this is returned as a string, instead of a hash. 44 | 45 | To do this, set: 46 | 47 | :vault: 48 | :default_field: value 49 | 50 | For example: 51 | 52 | vault write secret/foo value=bar other=baz 53 | 54 | The hiera lookup for `foo` will return just "bar" as a string. 55 | 56 | In case `foo` does not have the `value` field, a Hash is returned as normal. 57 | In versions <= 0.1.4 an error occurred. 58 | 59 | #### Default field behavior - optional 60 | When using `:default_field`, by default, additional fields are ignored, and 61 | if the field is not present, nil will be returned. 62 | 63 | To only return the value of the default field if it is present and the only one, set: 64 | 65 | :vault: 66 | :default_field: value 67 | :default_field_behavior: only 68 | 69 | Then, when `foo` contains more fields in addition to `value`, a Hash will be returned, just like with the default behaviour. 70 | And, in case `foo` does not contain the `value` field, a Hash with the actual fields will be returned, as if `:default_field` 71 | was not specified. 72 | 73 | #### JSON parsing of single values - optional 74 | Only applicable when `:default_field` is used. 75 | To use JSON parsing, set, for example: 76 | 77 | :vault: 78 | :default_field: json_value 79 | :default_field_parse: json 80 | 81 | Then, for example, when: 82 | 83 | vault write secret/foo json_value='["bird","spider","fly"]' 84 | 85 | the hiera lookup for `foo` will return an array. 86 | When used in Array lookups (hiera_array), all occurences of `foo` will be merged into a single array. 87 | 88 | When, for example: 89 | 90 | vault write secret/foo json_value='{"user1":"pass1","user2":"pass2"}' 91 | 92 | the hiera lookup for `foo` will return a hash. This is the same behavior as when: 93 | 94 | vault write secret/foo user1='pass1' user2='pass2' 95 | 96 | Both will result in a hash: 97 | 98 | {"user1"=>"pass1","user2"=>"pass2"} 99 | 100 | 101 | In case the single field does not contain a parseable JSON string, the string will be returned as is. 102 | When used in Hash lookups, this will result in an error as normal. 103 | 104 | 105 | ### Lookup type behavior 106 | 107 | In case Array or Hash lookup is done, usual array or hash merging takes place based on the configured global `:merge_behavior` setting. 108 | 109 | 110 | ## Backends and Mounts 111 | 112 | The `mounts` config attribute should be used to customise which secret backends 113 | are interrogated in a hiera lookup. 114 | 115 | Currently only the `generic` secret backend is supported. 116 | By default the `secret/` mount is used if no mounts are specified. 117 | 118 | Inspect your `vault mounts` output, e.g.: 119 | 120 | > vault mounts 121 | Path Type Description 122 | staging/ generic generic secret storage for Staging data 123 | production/ generic generic secret storage for Production data 124 | secret/ generic generic secret storage 125 | sys/ system system endpoints used for control, policy and debugging 126 | 127 | For the above scenario, you may wish to separate your per-environment secrets 128 | into their own mount. This could be achieved with a configuration like: 129 | 130 | :vault: 131 | # ... 132 | :mounts: 133 | :generic: 134 | - %{environment} 135 | - secret 136 | 137 | 138 | Since version 0.2.0, the `:hierarchy` source paths from the hiera configuration are used 139 | on top of each mount. 140 | This makes the behavior of the vault backend the same as other backends. 141 | Additionally, this enables usage of the third parameter to the hiera functions in puppet, 142 | the so-called 'override' parameter. 143 | See http://docs.puppetlabs.com/hiera/1/puppet.html#hiera-lookup-functions 144 | 145 | Example: In case we have the following hiera config: 146 | 147 | :backends: 148 | - vault 149 | - yaml 150 | 151 | :hierarchy: 152 | - "nodes/%{::fqdn}" 153 | - "hostclass/%{::hostclass}" 154 | - ... 155 | - common 156 | 157 | :yaml: 158 | :datadir: "/var/lib/hiera/%{::environment}/" 159 | 160 | :vault: 161 | :addr: ... 162 | :mounts: 163 | :generic: 164 | - "%{::environment}" 165 | - secret 166 | 167 | Each hiera lookup will result in a lookup under each mount, honouring the configured `:hierarchy`. e.g.: 168 | 169 | %{::environment}/nodes/%{::fqdn} 170 | %{::environment}/hostclass/${::hostclass} 171 | %{::environment}/... 172 | %{environment}/common 173 | secret/nodes/%{::fqdn} 174 | secret/hostclass/%{::hostclass} 175 | secret/... 176 | secret/common 177 | 178 | With the third argument to the hiera functions, the `override` parameter, the call 179 | 180 | $val = hiera('thekey', 'thedefault', 'override_path/look_here_first') 181 | 182 | will result in lookups through the following paths in vault: 183 | 184 | %{::environment}/override_path/look_here_first 185 | %{::environment}/nodes/%{::fqdn} 186 | %{::environment}/hostclass/%{::hostclass} 187 | %{::environment}/... 188 | %{::environment}/common 189 | secret/override_path/look_here_first 190 | secret/nodes/%{::fqdn} 191 | secret/hostclass/%{::hostclass} 192 | secret/... 193 | secret/common 194 | 195 | 196 | ## SSL 197 | 198 | SSL can be configured with the following config variables: 199 | 200 | :vault: 201 | :ssl_pem_file: /path/to/pem 202 | :ssl_ca_cert: /path/to/ca.crt 203 | :ssl_ca_path: /path/to/ca/ 204 | :ssl_verify: false 205 | :ssl_ciphers: "MY:SSL:CIPHER:CONFIG" 206 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | ## Git commit and tag 4 | 5 | Create a new commit which updates the `gem.version` field in `hiera-vault.gemspec`. 6 | 7 | Then: 8 | 9 | git commit -m "vx.y.z release" 10 | git push 11 | git tag vx.y.z 12 | git push --tags 13 | 14 | ## Rubygems.org 15 | 16 | ### TravisCI 17 | 18 | The new tagged version should be released automatically. 19 | 20 | ### Manual 21 | 22 | Now build the gem: 23 | 24 | gem build hiera-vault.gemspec 25 | 26 | And push it to rubygems.org, it will prompt for credentials if necessary: 27 | 28 | gem push hiera-vault-x.y.z.gem 29 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | task :default do 2 | puts "TBD: https://github.com/jsok/hiera-vault/issues/17" 3 | end 4 | -------------------------------------------------------------------------------- /hiera-vault.gemspec: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rubygems/package_task' 3 | 4 | spec = Gem::Specification.new do |gem| 5 | gem.name = "hiera-vault" 6 | gem.version = "0.2.2" 7 | gem.license = "Apache-2.0" 8 | gem.summary = "Module for using vault as a hiera backend" 9 | gem.email = "jonathan.sokolowski@gmail.com" 10 | gem.author = "Jonathan Sokolowski" 11 | gem.homepage = "http://github.com/jsok/hiera-vault" 12 | gem.description = "Hiera backend for looking up secrets stored in Vault" 13 | gem.require_path = "lib" 14 | gem.files = FileList["lib/**/*"].to_a 15 | gem.add_dependency('vault', '~> 0.4') 16 | end 17 | -------------------------------------------------------------------------------- /lib/hiera/backend/vault_backend.rb: -------------------------------------------------------------------------------- 1 | # Vault backend for Hiera 2 | class Hiera 3 | module Backend 4 | class Vault_backend 5 | 6 | def initialize() 7 | require 'json' 8 | require 'vault' 9 | 10 | @config = Config[:vault] 11 | @config[:mounts] ||= {} 12 | @config[:mounts][:generic] ||= ['secret'] 13 | @config[:default_field_parse] ||= 'string' # valid values: 'string', 'json' 14 | 15 | if not ['string','json'].include?(@config[:default_field_parse]) 16 | raise Exception, "[hiera-vault] invalid value for :default_field_parse: '#{@config[:default_field_behavior]}', should be one of 'string','json'" 17 | end 18 | 19 | # :default_field_behavior: 20 | # 'ignore' => ignore additional fields, if the field is not present return nil 21 | # 'only' => only return value of default_field when it is present and the only field, otherwise return hash as normal 22 | @config[:default_field_behavior] ||= 'ignore' 23 | 24 | if not ['ignore','only'].include?(@config[:default_field_behavior]) 25 | raise Exception, "[hiera-vault] invalid value for :default_field_behavior: '#{@config[:default_field_behavior]}', should be one of 'ignore','only'" 26 | end 27 | 28 | begin 29 | @vault = Vault::Client.new 30 | @vault.configure do |config| 31 | config.address = @config[:addr] unless @config[:addr].nil? 32 | config.token = @config[:token] unless @config[:token].nil? 33 | config.ssl_pem_file = @config[:ssl_pem_file] unless @config[:ssl_pem_file].nil? 34 | config.ssl_verify = @config[:ssl_verify] unless @config[:ssl_verify].nil? 35 | config.ssl_ca_cert = @config[:ssl_ca_cert] if config.respond_to? :ssl_ca_cert 36 | config.ssl_ca_path = @config[:ssl_ca_path] if config.respond_to? :ssl_ca_path 37 | config.ssl_ciphers = @config[:ssl_ciphers] if config.respond_to? :ssl_ciphers 38 | end 39 | 40 | fail if @vault.sys.seal_status.sealed? 41 | Hiera.debug("[hiera-vault] Client configured to connect to #{@vault.address}") 42 | rescue Exception => e 43 | @vault = nil 44 | Hiera.warn("[hiera-vault] Skipping backend. Configuration error: #{e}") 45 | end 46 | end 47 | 48 | def lookup(key, scope, order_override, resolution_type) 49 | return nil if @vault.nil? 50 | 51 | Hiera.debug("[hiera-vault] Looking up #{key} in vault backend") 52 | 53 | answer = nil 54 | found = false 55 | 56 | # Only generic mounts supported so far 57 | @config[:mounts][:generic].each do |mount| 58 | path = Backend.parse_string(mount, scope, { 'key' => key }) 59 | Backend.datasources(scope, order_override) do |source| 60 | Hiera.debug("Looking in path #{path}/#{source}/") 61 | new_answer = lookup_generic("#{path}/#{source}/#{key}", scope) 62 | #Hiera.debug("[hiera-vault] Answer: #{new_answer}:#{new_answer.class}") 63 | next if new_answer.nil? 64 | case resolution_type 65 | when :array 66 | raise Exception, "Hiera type mismatch: expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String 67 | answer ||= [] 68 | answer << new_answer 69 | when :hash 70 | raise Exception, "Hiera type mismatch: expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash 71 | answer ||= {} 72 | answer = Backend.merge_answer(new_answer,answer) 73 | else 74 | answer = new_answer 75 | found = true 76 | break 77 | end 78 | end 79 | break if found 80 | end 81 | 82 | return answer 83 | end 84 | 85 | def lookup_generic(key, scope) 86 | begin 87 | secret = @vault.logical.read(key) 88 | rescue Vault::HTTPConnectionError 89 | Hiera.debug("[hiera-vault] Could not connect to read secret: #{key}") 90 | rescue Vault::HTTPError => e 91 | Hiera.warn("[hiera-vault] Could not read secret #{key}: #{e.errors.join("\n").rstrip}") 92 | end 93 | 94 | return nil if secret.nil? 95 | 96 | Hiera.debug("[hiera-vault] Read secret: #{key}") 97 | if @config[:default_field] and (@config[:default_field_behavior] == 'ignore' or (secret.data.has_key?(@config[:default_field].to_sym) and secret.data.length == 1)) 98 | return nil if not secret.data.has_key?(@config[:default_field].to_sym) 99 | # Return just our default_field 100 | data = secret.data[@config[:default_field].to_sym] 101 | if @config[:default_field_parse] == 'json' 102 | begin 103 | data = JSON.parse(data) 104 | rescue JSON::ParserError => e 105 | Hiera.debug("[hiera-vault] Could not parse string as json: #{e}") 106 | end 107 | end 108 | else 109 | # Turn secret's hash keys into strings 110 | data = secret.data.inject({}) { |h, (k, v)| h[k.to_s] = v; h } 111 | end 112 | #Hiera.debug("[hiera-vault] Data: #{data}:#{data.class}") 113 | 114 | return Backend.parse_answer(data, scope) 115 | end 116 | 117 | end 118 | end 119 | end 120 | --------------------------------------------------------------------------------