hiera-eyaml-vault_rs 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
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: []
|