hiera-vault-less-noise 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d631946488c613044259758ec2de506488ca21732a477c32756ddb5f48cc0ebd
4
+ data.tar.gz: 677fe4a0ceaf17b79464e93a52a6ac1af730afdc9cc4048d9a795248d4c47127
5
+ SHA512:
6
+ metadata.gz: 41ffd67979afbfeb45eba510ad22909275086235dfad52727c9c30ac06a9cf8560cbcf2f5103cdc241460a0f78992461c6169a1eae554f553629afaeabbaa099
7
+ data.tar.gz: 66c0f56b836cc81fd5a59ddbaadcaade1cf9f35b839e68c8d7fe9ca4c07b9bbb8a89ee91e31dc3eae9e53d821864b320cad30db5c13b899d6c44e33e0c20f4e8
@@ -0,0 +1,267 @@
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
+ Hiera.debug("Hiera VAULT backend starting")
10
+
11
+ @config = Config[:vault]
12
+ @config[:mounts] ||= {}
13
+ @config[:mounts][:generic] ||= ['secret']
14
+ @config[:default_field_parse] ||= 'string' # valid values: 'string', 'json'
15
+
16
+ # :override_behavior:
17
+ # Valid values: 'normal', 'flag'
18
+ # Default: 'normal'
19
+ # If set to 'flag' a read from vault will only be done if the override parameter
20
+ # is a hash, and it contains the 'flag', it will behave like this:
21
+ # - when the value of the 'flag' key is 'vault', it will look in vault
22
+ # - when the value is 'vault_only', it will return the default or raise an exception
23
+ # if the lookup key is not found in vault
24
+ # If the 'flag' key does not exist, or if the override parameter is not a hash,
25
+ # nil will be returned to signal that the next backend should be searched.
26
+ # If the hash contains the 'override' key, its value will be used as the actual
27
+ # override.
28
+ # To support the 'flag' behavior, the `hiera_vault`, `hiera_vault_array`, and
29
+ # `hiera_vault_hash` functions need to be used, since they will make sure the
30
+ # override parameter is checked and changed where needed
31
+ # Additionally, when 'vault_only' is used, it will only work properly using the
32
+ # special hiera_vault* functions
33
+ #
34
+ # The 'flag_default' setting can be used to set the default for the 'flag' element
35
+ # to 'vault_only'. This is handled by the hiera_vault* parser functions.
36
+ #
37
+ @config[:override_behavior] ||= 'normal'
38
+ if not ['normal','flag'].include?(@config[:override_behavior])
39
+ raise Exception, "[hiera-vault] invalid value for :override_behavior: '#{@config[:override_behavior]}', should be one of 'normal','flag'"
40
+ end
41
+
42
+ @config[:flag_default] ||= 'vault_first'
43
+ if not ['vault_first','vault_only'].include?(@config[:flag_default])
44
+ raise Exception, "hiera_vault: invalid value '#{@config[:flag_default]}' for :flag_default in hiera config, one of 'vault_first', 'vault_only' expected"
45
+ end
46
+
47
+ @config[:default_field_parse] ||= 'string' # valid values: 'string', 'json'
48
+ if not ['string','json'].include?(@config[:default_field_parse])
49
+ raise Exception, "[hiera-vault] invalid value for :default_field_parse: '#{@config[:default_field_behavior]}', should be one of 'string','json'"
50
+ end
51
+
52
+ # :default_field_behavior:
53
+ # 'ignore' => ignore additional fields, if the field is not present return nil
54
+ # 'only' => only return value of default_field when it is present and the only field, otherwise return hash as normal
55
+ @config[:default_field_behavior] ||= 'ignore'
56
+ if not ['ignore','only'].include?(@config[:default_field_behavior])
57
+ raise Exception, "[hiera-vault] invalid value for :default_field_behavior: '#{@config[:default_field_behavior]}', should be one of 'ignore','only'"
58
+ end
59
+
60
+ vault_connect
61
+ end
62
+
63
+ def lookup(key, scope, order_override, resolution_type)
64
+ begin
65
+ vault_connect
66
+
67
+ read_vault = false
68
+ genpw = false
69
+ otp = nil
70
+
71
+ if @config[:override_behavior] == 'flag'
72
+ if order_override.kind_of? Hash
73
+ if order_override.has_key?('flag')
74
+ if ['vault_default','vault_first','vault_only'].include?(order_override['flag'])
75
+ read_vault = true
76
+ if order_override['flag'] == 'vault_default'
77
+ # since variables are passed by reference, the caller will know afterwards, which flag was actually used
78
+ order_override['flag'] = @config[:flag_default]
79
+ end
80
+ if order_override.has_key?('generate')
81
+ pwlen = order_override['generate'].to_i
82
+ if pwlen > 8 # TODO: make configurable
83
+ genpw = true
84
+ end
85
+ end
86
+ if order_override.has_key?('vault_otp')
87
+ otp = order_override['vault_otp']
88
+ end
89
+ if order_override.has_key?('resolution_type')
90
+ resolution_type = order_override['resolution_type']
91
+ end
92
+ # this one must be last, because order_override gets a new value!:
93
+ if order_override.has_key?('override')
94
+ order_override = order_override['override']
95
+ else
96
+ order_override = nil
97
+ end
98
+ else
99
+ raise Exception, "[hiera-vault] Invalid value '#{order_override['flag']}' for 'flag' element in override parameter, expected one of ['vault_default', 'vault_first', 'vault_only'], while override_behavior is 'flag'"
100
+ end
101
+ if @vault.nil?
102
+ raise Exception, "[hiera-vault] Cannot skip, because vault is unavailable and vault must be read, while override_behavior is 'flag'"
103
+ end
104
+ else
105
+ Hiera.debug("[hiera-vault] Not reading from vault, because 'flag' element does not exist in override parameter, while override_behavior is 'flag'")
106
+ end
107
+ else
108
+ Hiera.debug("[hiera-vault] Not reading from vault, because override parameter is not a hash, while override_behavior is 'flag'")
109
+ end
110
+ else
111
+ # normal behavior
112
+ return nil if @vault.nil?
113
+ read_vault = true
114
+ end
115
+
116
+ answer = nil
117
+
118
+ if read_vault
119
+ Hiera.debug("[hiera-vault] Looking up #{key} in vault backend")
120
+
121
+ found = false
122
+
123
+ # Only generic mounts supported so far
124
+ @config[:mounts][:generic].each do |mount|
125
+ path = Backend.parse_string(mount, scope, { 'key' => key })
126
+ Backend.datasources(scope, order_override) do |source|
127
+ Hiera.debug("Looking in path #{path}/#{source}/")
128
+ new_answer = lookup_generic("#{path}/#{source}/#{key}", scope)
129
+ #Hiera.debug("[hiera-vault] Answer: #{new_answer}:#{new_answer.class}")
130
+ next if new_answer.nil?
131
+ case resolution_type
132
+ when :array
133
+ raise Exception, "Hiera type mismatch: expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String
134
+ answer ||= []
135
+ answer << new_answer
136
+ when :hash
137
+ raise Exception, "Hiera type mismatch: expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash
138
+ answer ||= {}
139
+ answer = Backend.merge_answer(new_answer,answer)
140
+ else
141
+ answer = new_answer
142
+ found = true
143
+ break
144
+ end
145
+ end
146
+
147
+ break if found
148
+ end
149
+ end
150
+
151
+ if answer.nil? and @config[:default_field] and genpw
152
+ new_answer = generate(pwlen)
153
+
154
+ @config[:mounts][:generic].each do |mount|
155
+ path = Backend.parse_string(mount, scope, { 'key' => key })
156
+ Backend.datasources(scope, order_override) do |source|
157
+ # Storing the generated secret in the override path or the highest path in the hierarchy
158
+ # make sure to use a proper override or an appropriate hierarchy if the secret is to be used
159
+ # on different nodes, otherwise the same key might be written with a different value at different
160
+ # paths
161
+ Hiera.debug("Storing generated secret in vault at path #{path}/#{source}/#{key}")
162
+ answer = new_answer if store("#{path}/#{source}/#{key}", { @config[:default_field].to_sym => new_answer })
163
+ break
164
+ end
165
+ break
166
+ end
167
+ end
168
+ if answer.nil? and not otp.nil?
169
+ answer = otp
170
+ end
171
+ return answer
172
+ rescue Exception => e
173
+ raise Exception, "#{e.message} in #{e.backtrace[0]}"
174
+ end
175
+ end
176
+
177
+ def vault_connect
178
+ if @vault.nil?
179
+ begin
180
+ @vault = Vault::Client.new
181
+ @vault.configure do |config|
182
+ config.address = @config[:addr] if @config[:addr]
183
+ config.token = @config[:token] if @config[:token]
184
+ config.ssl_pem_file = @config[:ssl_pem_file] if @config[:ssl_pem_file]
185
+ config.ssl_verify = @config[:ssl_verify] if @config[:ssl_verify]
186
+ config.ssl_ca_cert = @config[:ssl_ca_cert] if config.respond_to? :ssl_ca_cert
187
+ config.ssl_ca_path = @config[:ssl_ca_path] if config.respond_to? :ssl_ca_path
188
+ config.ssl_ciphers = @config[:ssl_ciphers] if config.respond_to? :ssl_ciphers
189
+ end
190
+
191
+ fail if @vault.sys.seal_status.sealed?
192
+ Hiera.debug("[hiera-vault] Client configured to connect to #{@vault.address}")
193
+ rescue Exception => e
194
+ @vault = nil
195
+ Hiera.warn("[hiera-vault] Skipping backend. Configuration error: #{e}")
196
+ end
197
+ end
198
+ if @vault
199
+ begin
200
+ fail if @vault.sys.seal_status.sealed?
201
+ rescue Exception => e
202
+ @vault = nil
203
+ Hiera.warn("[hiera-vault] Vault is unavailable or configuration error: #{e}")
204
+ end
205
+ end
206
+ end
207
+
208
+ def lookup_generic(key, scope)
209
+ begin
210
+ secret = @vault.logical.read(key)
211
+ rescue Exception => e
212
+ raise Exception, "[hiera-vault] Could not read secret #{key}, #{e.class}: #{e.errors.join("\n").rstrip}"
213
+ end
214
+
215
+ return nil if secret.nil?
216
+
217
+ Hiera.debug("[hiera-vault] Read secret: #{key}")
218
+ 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))
219
+ return nil if not secret.data.has_key?(@config[:default_field].to_sym)
220
+ # Return just our default_field
221
+ data = secret.data[@config[:default_field].to_sym]
222
+ if @config[:default_field_parse] == 'json'
223
+ begin
224
+ data = JSON.parse(data)
225
+ rescue JSON::ParserError
226
+ Hiera.debug("[hiera-vault] Could not parse string as JSON")
227
+ end
228
+ end
229
+ else
230
+ # Turn secret's hash keys into strings
231
+ data = secret.data.inject({}) { |h, (k, v)| h[k.to_s] = v; h }
232
+ end
233
+ #Hiera.debug("[hiera-vault] Data: #{data}:#{data.class}")
234
+
235
+ return Backend.parse_answer(data, scope)
236
+ end
237
+
238
+ def generate(password_size)
239
+ pass = ""
240
+ (1..password_size).each do
241
+ pass += (("a".."z").to_a+("A".."Z").to_a+("0".."9").to_a)[rand(62).to_int]
242
+ end
243
+
244
+ pass
245
+ end
246
+
247
+ def store(key, secret_hash)
248
+ begin
249
+ write_result = @vault.logical.write(key, secret_hash)
250
+ rescue Vault::HTTPConnectionError
251
+ Hiera.debug("[hiera-vault] Could not connect to write secret: #{key}")
252
+ rescue Vault::HTTPError => e
253
+ Hiera.warn("[hiera-vault] Could not write secret #{key}: #{e.errors.join("\n").rstrip}")
254
+ end
255
+
256
+ if write_result == true
257
+ Hiera.debug("[hiera-vault] Successfully written secret: #{key}")
258
+ return true
259
+ else
260
+ Hiera.warn("[hiera-vault] Could not write secret #{key}: #{write_result}")
261
+ return false
262
+ end
263
+ end
264
+
265
+ end
266
+ end
267
+ end
@@ -0,0 +1,122 @@
1
+ require 'hiera_puppet'
2
+
3
+ module HieraVault
4
+
5
+ module_function
6
+ def lookup(key, default, scope, override, resolution_type)
7
+ begin
8
+ flag_default = 'vault_default'
9
+ override ||= {'flag' => flag_default}
10
+ case override.class.to_s
11
+ when 'String'
12
+ override = {'flag' => flag_default, 'override' => override}
13
+ when 'Hash'
14
+ if not override.has_key?('flag')
15
+ override['flag'] = flag_default
16
+ end
17
+ else
18
+ raise(Puppet::ParseError, "hiera_vault: invalid 'override' parameter supplied: '#{override}':#{override.class}")
19
+ end
20
+
21
+ if resolution_type == :priority
22
+ if default.kind_of? Hash
23
+ if default.has_key?('generate')
24
+ override['generate'] = default['generate'].to_i
25
+ default = nil
26
+ end
27
+ end
28
+ end
29
+
30
+ r = rand(2147483647).to_s
31
+ otp = "vault_otp_#{r}"
32
+ case resolution_type
33
+ when :array
34
+ otp = [otp]
35
+ when :hash
36
+ otp = {'otp' => otp}
37
+ end
38
+ override['vault_otp'] = otp
39
+
40
+ # this is for vault_backend so that it will use the actual resolution type internally
41
+ override['resolution_type'] = resolution_type
42
+
43
+ new_answer = HieraPuppet.lookup(key, nil, scope, override, :priority)
44
+ if new_answer == otp
45
+ # this means that vault_backend could not find anything, so it returned the value of vault_otp
46
+ new_answer = nil
47
+ end
48
+
49
+ if new_answer.nil?
50
+ answer = nil
51
+ else
52
+ case resolution_type
53
+ when :array
54
+ raise Puppet::ParseError, "hiera_vault: after vault_backend.lookup: type mismatch: expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String
55
+ answer ||= []
56
+ answer << new_answer
57
+ when :hash
58
+ raise Puppet::ParseError, "hiera_vault: after vault_backend.lookup: type mismatch: expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash
59
+ answer ||= {}
60
+ answer = Hiera::Backend.merge_answer(new_answer,answer)
61
+ else
62
+ answer = new_answer
63
+ end
64
+ end
65
+
66
+ hiera_scope = Hiera::Scope.new(scope)
67
+ if override['flag'] == 'vault_only'
68
+ if not (default.nil? or default.empty?)
69
+ answer = Hiera::Backend.resolve_answer(answer, resolution_type) unless answer.nil?
70
+ answer = Hiera::Backend.parse_string(default, hiera_scope) if answer.nil? and default.is_a?(String)
71
+ answer = default if answer.nil?
72
+ end
73
+ if answer.nil?
74
+ raise(Puppet::ParseError, "hiera_vault: Could not find data item #{key} in vault, while vault_only was requested, and empty default supplied")
75
+ end
76
+ return answer
77
+ end
78
+
79
+ if answer.nil? or resolution_type != :priority
80
+ # continue with normal hiera lookup, while vault_backend will skip automatically
81
+ if override.has_key?('override')
82
+ override = override['override']
83
+ else
84
+ override = nil
85
+ end
86
+
87
+ begin
88
+ new_answer = HieraPuppet.lookup(key, nil, scope, override, resolution_type)
89
+ rescue Puppet::ParseError
90
+ if answer.nil?
91
+ if default.nil? or default.empty?
92
+ raise(Puppet::ParseError, "Could not find data item #{key} in vault and in any Hiera data file and no or empty default supplied")
93
+ end
94
+ answer = Hiera::Backend.parse_string(default, hiera_scope) if default.is_a?(String)
95
+ answer = default if answer.nil?
96
+ return answer
97
+ end
98
+ end
99
+ end
100
+ if not new_answer.nil?
101
+ case resolution_type
102
+ when :array
103
+ raise Puppet::ParseError, "hiera_vault: after normal Hiera lookup: type mismatch: expected Array and got #{new_answer.class}" unless new_answer.nil? or new_answer.kind_of? Array or new_answer.kind_of? String
104
+ answer ||= []
105
+ answer << new_answer
106
+ when :hash
107
+ raise Puppet::ParseError, "hiera_vault: after normal Hiera lookup: type mismatch: expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash
108
+ answer ||= {}
109
+ answer = Hiera::Backend.merge_answer(new_answer,answer)
110
+ else
111
+ answer = new_answer
112
+ end
113
+ end
114
+ answer = Hiera::Backend.resolve_answer(answer, resolution_type)
115
+ return answer
116
+ rescue Exception => e
117
+ raise(Puppet::ParseError, "#{e.message} in #{e.backtrace[0]}")
118
+ end
119
+ end
120
+
121
+ end
122
+
@@ -0,0 +1,17 @@
1
+ require 'hiera_puppet'
2
+ require 'hiera_vault'
3
+
4
+ module Puppet::Parser::Functions
5
+ newfunction(:hiera_vault, :type => :rvalue, :arity => -2, :doc => "Performs a
6
+ hiera lookup, first and optionally only in the 'vault' backend.
7
+
8
+ The behavior depends on the 'override' parameter.
9
+
10
+ NOTICE: For this function to work properly, set :override_behavior: 'flag' in the
11
+ :vault: config part in the hiera config.
12
+ ") do |*args|
13
+ key, default, override = HieraPuppet.parse_args(args)
14
+ HieraVault.lookup(key, default, self, override, :priority)
15
+ end
16
+ end
17
+
@@ -0,0 +1,17 @@
1
+ require 'hiera_puppet'
2
+ require 'hiera_vault'
3
+
4
+ module Puppet::Parser::Functions
5
+ newfunction(:hiera_vault_array, :type => :rvalue, :arity => -2, :doc => "Performs a
6
+ hiera_array lookup, first and optionally only in the 'vault' backend.
7
+
8
+ The behavior depends on the 'override' parameter.
9
+
10
+ NOTICE: For this function to work properly, set :override_behavior: 'flag' in the
11
+ :vault: config part in the hiera config.
12
+ ") do |*args|
13
+ key, default, override = HieraPuppet.parse_args(args)
14
+ HieraVault.lookup(key, default, self, override, :array)
15
+ end
16
+ end
17
+
@@ -0,0 +1,17 @@
1
+ require 'hiera_puppet'
2
+ require 'hiera_vault'
3
+
4
+ module Puppet::Parser::Functions
5
+ newfunction(:hiera_vault_hash, :type => :rvalue, :arity => -2, :doc => "Performs a
6
+ hiera_hash lookup, first and optionally only in the 'vault' backend.
7
+
8
+ The behavior depends on the 'override' parameter.
9
+
10
+ NOTICE: For this function to work properly, set :override_behavior: 'flag' in the
11
+ :vault: config part in the hiera config.
12
+ ") do |*args|
13
+ key, default, override = HieraPuppet.parse_args(args)
14
+ HieraVault.lookup(key, default, self, override, :hash)
15
+ end
16
+ end
17
+
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hiera-vault-less-noise
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Sokolowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-04-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: vault
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.4'
27
+ description: Hiera backend for looking up secrets stored in Vault
28
+ email: jonathan.sokolowski@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/hiera/backend/vault_backend.rb
34
+ - lib/hiera_vault.rb
35
+ - lib/puppet/parser/functions/hiera_vault.rb
36
+ - lib/puppet/parser/functions/hiera_vault_array.rb
37
+ - lib/puppet/parser/functions/hiera_vault_hash.rb
38
+ homepage: http://github.com/tylerjl/hiera-vault
39
+ licenses:
40
+ - Apache-2.0
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 2.7.3
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Module for using vault as a hiera backend
62
+ test_files: []