hiera-eyaml-vault_rs 1.2.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.
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2e55bd23a41143fe641654c75c61936401f98a7de0fe4835a7261f6199eea043
|
4
|
+
data.tar.gz: a5669657795470fff19b4769b1b20c28ab0d2d1be47ab19ad8c049fed36821b0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 983ab8eb12b8296a0ea57249ec500f6d8222b07b6992afb971966621e8e2d67110508b3f369e0a1a395bfe0489dab91cc29aa7c5b3e03cf604555e19bdb132ca
|
7
|
+
data.tar.gz: 6ce2265df3b4eb256e4105ed1928cf63ea328e560777d2ce06a1ed76a29b20220ed5b4132ff6ef95e804eba09580e338836e67c052c2884fe10f627a11a06156
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
class Hiera
|
6
|
+
module Backend
|
7
|
+
module Eyaml
|
8
|
+
module Encryptors
|
9
|
+
class Vault_rs < Encryptor
|
10
|
+
class HTTPError < Exception
|
11
|
+
end
|
12
|
+
class Httphandler
|
13
|
+
class << self
|
14
|
+
|
15
|
+
def post(uri_str, data={}, headers={}, options={})
|
16
|
+
uri = URI.parse(uri_str)
|
17
|
+
request = Net::HTTP::Post.new(uri.path)
|
18
|
+
request.body = data.to_json
|
19
|
+
http_send(uri, request, headers, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def put(uri_str, data={}, headers={}, options={})
|
23
|
+
uri = URI.parse(uri_str)
|
24
|
+
request = Net::HTTP::Put.new(uri.path)
|
25
|
+
request.body = data.to_json
|
26
|
+
http_send(uri, request, headers, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def http_send(uri, request, headers={}, options={})
|
31
|
+
request.add_field('Content-Type', options[:content_type]) if options[:content_type]
|
32
|
+
|
33
|
+
headers.each do |header, value|
|
34
|
+
request.add_field(header, value)
|
35
|
+
end
|
36
|
+
|
37
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
38
|
+
if options[:cert]
|
39
|
+
http.use_ssl = true
|
40
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
41
|
+
http.cert = OpenSSL::X509::Certificate.new(options[:cert])
|
42
|
+
http.key = OpenSSL::PKey::RSA.new(options[:key])
|
43
|
+
elsif options[:ssl]
|
44
|
+
http.use_ssl = true
|
45
|
+
http.verify_mode = options[:ssl_verify] ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
46
|
+
http.cert = OpenSSL::X509::Certificate.new(options[:ssl_cert]) if options[:ssl_cert]
|
47
|
+
http.key = OpenSSL::PKey::RSA.new(options[:ssl_key]) if options[:ssl_key]
|
48
|
+
end
|
49
|
+
|
50
|
+
begin
|
51
|
+
response = http.request(request)
|
52
|
+
return response
|
53
|
+
rescue => e
|
54
|
+
raise HTTPError, e.message
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,300 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'hiera/backend/eyaml/encryptor'
|
3
|
+
require 'hiera/backend/eyaml/encryptors/vault_rs/httphandler'
|
4
|
+
require 'hiera/backend/eyaml/utils'
|
5
|
+
require 'hiera/backend/eyaml/plugins'
|
6
|
+
require 'hiera/backend/eyaml/options'
|
7
|
+
|
8
|
+
class Hiera
|
9
|
+
module Backend
|
10
|
+
module Eyaml
|
11
|
+
module Encryptors
|
12
|
+
class Vault_rs < Encryptor
|
13
|
+
class AuthenticationError < Exception
|
14
|
+
end
|
15
|
+
|
16
|
+
HTTP_HANDLER = Hiera::Backend::Eyaml::Encryptors::Vault_rs::Httphandler
|
17
|
+
|
18
|
+
self.tag = 'VAULT'
|
19
|
+
|
20
|
+
self.options = {
|
21
|
+
:addr => {
|
22
|
+
desc: "Address of the vault server",
|
23
|
+
type: :string,
|
24
|
+
default: "https://127.0.0.1:8200"
|
25
|
+
},
|
26
|
+
|
27
|
+
:role_id => {
|
28
|
+
desc: "role_id for the Approle",
|
29
|
+
type: :string,
|
30
|
+
},
|
31
|
+
|
32
|
+
:secret_id => {
|
33
|
+
desc: "secret_id for the Approle",
|
34
|
+
type: :string,
|
35
|
+
},
|
36
|
+
|
37
|
+
:auth_name => {
|
38
|
+
desc: "Name for certificate-based authentication",
|
39
|
+
type: :string,
|
40
|
+
},
|
41
|
+
|
42
|
+
:client_cert => {
|
43
|
+
desc: "Path to the client certificate for certificate-based authentication",
|
44
|
+
type: :string,
|
45
|
+
},
|
46
|
+
|
47
|
+
:client_key => {
|
48
|
+
desc: "Path to the client private key for certificate-based authentication",
|
49
|
+
type: :string,
|
50
|
+
},
|
51
|
+
|
52
|
+
:use_ssl => {
|
53
|
+
desc: "Use SSL to connect to vault",
|
54
|
+
type: :boolean,
|
55
|
+
default: true
|
56
|
+
},
|
57
|
+
|
58
|
+
:ssl_verify => {
|
59
|
+
desc: "Verify SSL certs",
|
60
|
+
type: :boolean,
|
61
|
+
default: true
|
62
|
+
},
|
63
|
+
|
64
|
+
:ssl_cert => {
|
65
|
+
desc: "SSL Certificate to connect with",
|
66
|
+
type: :string
|
67
|
+
},
|
68
|
+
|
69
|
+
:ssl_key => {
|
70
|
+
desc: "SSL Private key to connect with",
|
71
|
+
type: :string
|
72
|
+
},
|
73
|
+
|
74
|
+
:transit_name => {
|
75
|
+
desc: "Vault transit engine name (default 'transit')",
|
76
|
+
type: :string,
|
77
|
+
default: "transit"
|
78
|
+
},
|
79
|
+
|
80
|
+
:key_name => {
|
81
|
+
desc: "Vault transit key name (default 'hiera')",
|
82
|
+
type: :string,
|
83
|
+
default: "hiera"
|
84
|
+
},
|
85
|
+
|
86
|
+
:api_version => {
|
87
|
+
desc: "API version to use",
|
88
|
+
type: :integer,
|
89
|
+
default: 1
|
90
|
+
}
|
91
|
+
}
|
92
|
+
class << self
|
93
|
+
|
94
|
+
def config_file
|
95
|
+
ENV['EYAML_CONFIG'] || File.join(ENV['HOME'], '.eyaml/config.yaml') || '/etc/eyaml/config.yaml'
|
96
|
+
end
|
97
|
+
|
98
|
+
def load_config
|
99
|
+
if File.exists?(config_file)
|
100
|
+
@config_defaults = YAML.load_file(config_file)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Allow the inherited options method to allow for local
|
105
|
+
# configuration to fall back on
|
106
|
+
#
|
107
|
+
def option(key)
|
108
|
+
# Debug flag
|
109
|
+
debug = ENV['EYAML_DEBUG'] == 'true'
|
110
|
+
puts "Resolving option for key: #{key}" if debug
|
111
|
+
|
112
|
+
# Load the configuration file if not already loaded
|
113
|
+
load_config if @config_defaults.nil?
|
114
|
+
|
115
|
+
# Try to resolve the option from the configuration file first
|
116
|
+
unless @config_defaults.nil?
|
117
|
+
config_option = @config_defaults[key.to_s]
|
118
|
+
if config_option
|
119
|
+
puts "Resolved from config_defaults: #{config_option}" if debug
|
120
|
+
return config_option
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# If no value is found, fallback to super
|
125
|
+
puts "Falling back to super for key: #{key}" if debug
|
126
|
+
super
|
127
|
+
end
|
128
|
+
|
129
|
+
def create_keys
|
130
|
+
diagnostic_message = self.option :diagnostic_message
|
131
|
+
puts "Create_keys: #{diagnostic_message}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def vault_url(endpoint)
|
135
|
+
uri = []
|
136
|
+
uri << option(:addr)
|
137
|
+
uri << "v#{option :api_version}"
|
138
|
+
uri << endpoint
|
139
|
+
uri.flatten.join("/")
|
140
|
+
end
|
141
|
+
|
142
|
+
def login
|
143
|
+
if option(:role_id)
|
144
|
+
role_id = option :role_id
|
145
|
+
secret_id = option :secret_id
|
146
|
+
|
147
|
+
login_data = { "role_id" => role_id }
|
148
|
+
login_data['secret_id'] = secret_id unless secret_id.nil?
|
149
|
+
|
150
|
+
response = vault_post(login_data, :login, false)
|
151
|
+
@login_token = response['auth']['client_token']
|
152
|
+
elsif option(:client_cert)
|
153
|
+
auth_name = option :auth_name
|
154
|
+
|
155
|
+
login_data = { "name" => auth_name }
|
156
|
+
|
157
|
+
response = vault_post(login_data, :cert_login, false)
|
158
|
+
@login_token = response['auth']['client_token']
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def ssl?
|
163
|
+
option :use_ssl
|
164
|
+
end
|
165
|
+
|
166
|
+
def read_file(file)
|
167
|
+
raise Exception, "Cannot read #{file}" unless File.exists?(file)
|
168
|
+
File.read(file)
|
169
|
+
end
|
170
|
+
|
171
|
+
def ssl_key
|
172
|
+
return nil if option(:ssl_key).nil?
|
173
|
+
@vault_ssl_key ||= read_file(option :ssl_key)
|
174
|
+
@vault_ssl_key
|
175
|
+
end
|
176
|
+
|
177
|
+
def ssl_cert
|
178
|
+
return nil if option(:ssl_cert).nil?
|
179
|
+
@vault_ssl_cert ||= read_file(option :ssl_cert)
|
180
|
+
@vault_ssl_cert
|
181
|
+
end
|
182
|
+
|
183
|
+
def client_cert
|
184
|
+
return nil if option(:client_cert).nil?
|
185
|
+
@vault_client_cert ||= read_file(option :client_cert)
|
186
|
+
@vault_client_cert
|
187
|
+
end
|
188
|
+
|
189
|
+
def client_key
|
190
|
+
return nil if option(:client_key).nil?
|
191
|
+
@vault_client_key ||= read_file(option :client_key)
|
192
|
+
@vault_client_key
|
193
|
+
end
|
194
|
+
|
195
|
+
def token_configured?
|
196
|
+
return true if ENV['VAULT_TOKEN']
|
197
|
+
not option(:token).nil?
|
198
|
+
end
|
199
|
+
|
200
|
+
def token
|
201
|
+
authenticate
|
202
|
+
ENV['VAULT_TOKEN'] || option(:token) || @login_token
|
203
|
+
end
|
204
|
+
|
205
|
+
def authenticate
|
206
|
+
unless token_configured?
|
207
|
+
login if @login_token.nil?
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def endpoint(action)
|
212
|
+
# Check for the existence of a DEBUG environment variable
|
213
|
+
debug = ENV['EYAML_DEBUG'] == 'true'
|
214
|
+
|
215
|
+
# Compute the endpoints
|
216
|
+
endpoints = {
|
217
|
+
:decrypt => "#{option(:transit_name)}/decrypt/#{option(:key_name)}",
|
218
|
+
:encrypt => "#{option(:transit_name)}/encrypt/#{option(:key_name)}",
|
219
|
+
:login => "auth/approle/login",
|
220
|
+
:cert_login => "auth/cert/login"
|
221
|
+
}
|
222
|
+
|
223
|
+
# Output debug information if the debug mode is enabled
|
224
|
+
if debug
|
225
|
+
puts "Decrypt Endpoint: #{endpoints[:decrypt]}"
|
226
|
+
puts "Encrypt Endpoint: #{endpoints[:encrypt]}"
|
227
|
+
end
|
228
|
+
|
229
|
+
endpoints[action]
|
230
|
+
end
|
231
|
+
|
232
|
+
def url_path(action)
|
233
|
+
vault_url(endpoint(action))
|
234
|
+
end
|
235
|
+
|
236
|
+
def parse_response(response)
|
237
|
+
body = JSON.load(response.body)
|
238
|
+
if response.code_type == Net::HTTPOK
|
239
|
+
return body
|
240
|
+
else
|
241
|
+
if response.code == "403"
|
242
|
+
raise AuthenticationError, body
|
243
|
+
end
|
244
|
+
if body['errors'].is_a?(Array)
|
245
|
+
message = body['errors'].join("\n")
|
246
|
+
else
|
247
|
+
message = "Failed to decrypt entry #{body}"
|
248
|
+
end
|
249
|
+
raise Exception, "Error decrypting data from Vault: #{message}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def vault_post(data, action, use_token=true, headers={})
|
254
|
+
url = url_path(action)
|
255
|
+
http_options = {}
|
256
|
+
if option(:client_cert)
|
257
|
+
http_options = {
|
258
|
+
:cert => client_cert,
|
259
|
+
:key => client_key,
|
260
|
+
}
|
261
|
+
elsif ssl?
|
262
|
+
http_options = {
|
263
|
+
:ssl => true,
|
264
|
+
:ssl_verify => option(:ssl_verify),
|
265
|
+
:ssl_cert => ssl_cert,
|
266
|
+
:ssl_key => ssl_key,
|
267
|
+
}
|
268
|
+
end
|
269
|
+
|
270
|
+
begin
|
271
|
+
tries ||= 0
|
272
|
+
headers['X-Vault-Token'] = token if use_token
|
273
|
+
parse_response HTTP_HANDLER.post(url, data, headers, http_options)
|
274
|
+
rescue AuthenticationError => e
|
275
|
+
login
|
276
|
+
retry if (tries += 1) < 2
|
277
|
+
raise
|
278
|
+
rescue HTTPError => e
|
279
|
+
raise Exception, "HTTP Error: #{e}"
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def decrypt(string)
|
284
|
+
response = vault_post({ 'ciphertext' => string}, :decrypt)
|
285
|
+
response_data=response['data']
|
286
|
+
Base64.decode64(response_data['plaintext'])
|
287
|
+
end
|
288
|
+
|
289
|
+
def encrypt(plain)
|
290
|
+
encoded = Base64.encode64(plain)
|
291
|
+
response = vault_post({ 'plaintext' => encoded}, :encrypt)
|
292
|
+
response_data=response['data']
|
293
|
+
response_data['ciphertext']
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hiera-eyaml-vault_rs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ryan-scheinberg
|
8
|
+
- Craig Dunn
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2023-09-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: hiera-eyaml
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "<"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 4.0.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "<"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 4.0.0
|
28
|
+
description: Eyaml plugin for Vault transit secrets engine. Forked from https://github.com/crayfishx/hiera-eyaml-vault
|
29
|
+
email:
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/hiera/backend/eyaml/encryptors/vault_rs.rb
|
35
|
+
- lib/hiera/backend/eyaml/encryptors/vault_rs/eyaml_init.rb
|
36
|
+
- lib/hiera/backend/eyaml/encryptors/vault_rs/httphandler.rb
|
37
|
+
homepage: https://github.com/ryan-scheinberg/hiera-eyaml-vault
|
38
|
+
licenses:
|
39
|
+
- Apache-2.0
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubygems_version: 3.0.3.1
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: Encryption plugin for hiera-eyaml to use Vault's transit secrets engine
|
60
|
+
test_files: []
|