vault-rails 0.2.0 → 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.
- checksums.yaml +4 -4
- data/lib/vault/rails.rb +165 -158
- data/lib/vault/rails/version.rb +1 -1
- data/spec/dummy/config/environments/development.rb +11 -9
- data/spec/dummy/config/environments/test.rb +7 -2
- data/spec/dummy/config/initializers/assets.rb +3 -1
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +589 -0
- data/spec/dummy/log/test.log +558 -0
- data/spec/integration/rails_spec.rb +8 -0
- metadata +21 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c8f664265ab1fa257f49bae1035a07ce66ab25c
|
4
|
+
data.tar.gz: 0afe845824aa882453dd07574e895b4a09e8fda1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7dd41623f7b78a7fe21aaaa0d53ff6be679dfc211d6f4da409216d58505663f71f615170a619eb9a5a1304903761ef7a46b754c90024b1d40d0a56cda8299b7
|
7
|
+
data.tar.gz: 0421c3b2d52fd8dd1355e5a9f4a41b4607c946ba5477d7d6c3c7592fc220ed6fc4ef7968dd05709d84dd93bb36fed31bb306c9b8bc1f6d2a918dca62137e08fd
|
data/lib/vault/rails.rb
CHANGED
@@ -11,8 +11,6 @@ require_relative "rails/version"
|
|
11
11
|
|
12
12
|
module Vault
|
13
13
|
module Rails
|
14
|
-
extend Vault::Rails::Configurable
|
15
|
-
|
16
14
|
# The list of serializers.
|
17
15
|
#
|
18
16
|
# @return [Hash<Symbol, Module>]
|
@@ -29,199 +27,208 @@ module Vault
|
|
29
27
|
DEV_WARNING = "[vault-rails] Using in-memory cipher - this is not secure " \
|
30
28
|
"and should never be used in production-like environments!".freeze
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
if !defined?(@client) || !@client.same_options?(options)
|
38
|
-
@client = Vault::Client.new(options)
|
39
|
-
end
|
40
|
-
return @client
|
41
|
-
end
|
30
|
+
class << self
|
31
|
+
# API client object based off the configured options in {Configurable}.
|
32
|
+
#
|
33
|
+
# @return [Vault::Client]
|
34
|
+
attr_reader :client
|
42
35
|
|
43
|
-
|
44
|
-
|
45
|
-
def self.method_missing(m, *args, &block)
|
46
|
-
if client.respond_to?(m)
|
47
|
-
client.public_send(m, *args, &block)
|
48
|
-
else
|
49
|
-
super
|
50
|
-
end
|
51
|
-
end
|
36
|
+
def setup!
|
37
|
+
Vault.setup!
|
52
38
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
39
|
+
@client = Vault.client
|
40
|
+
@client.class.instance_eval do
|
41
|
+
include Vault::Rails::Configurable
|
42
|
+
end
|
57
43
|
|
58
|
-
|
59
|
-
#
|
60
|
-
# @param [String] path
|
61
|
-
# the mount point
|
62
|
-
# @param [String] key
|
63
|
-
# the key to encrypt at
|
64
|
-
# @param [String] plaintext
|
65
|
-
# the plaintext to encrypt
|
66
|
-
# @param [Vault::Client] client
|
67
|
-
# the Vault client to use
|
68
|
-
#
|
69
|
-
# @return [String]
|
70
|
-
# the encrypted cipher text
|
71
|
-
def self.encrypt(path, key, plaintext, client = self.client)
|
72
|
-
if plaintext.blank?
|
73
|
-
return plaintext
|
44
|
+
self
|
74
45
|
end
|
75
46
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
result = self.vault_encrypt(path, key, plaintext, client)
|
47
|
+
# Delegate all methods to the client object, essentially making the module
|
48
|
+
# object behave like a {Vault::Client}.
|
49
|
+
def method_missing(m, *args, &block)
|
50
|
+
if client.respond_to?(m)
|
51
|
+
client.public_send(m, *args, &block)
|
82
52
|
else
|
83
|
-
|
53
|
+
super
|
84
54
|
end
|
85
|
-
|
86
|
-
return self.force_encoding(result)
|
87
55
|
end
|
88
|
-
end
|
89
56
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
# the mount point
|
94
|
-
# @param [String] key
|
95
|
-
# the key to decrypt at
|
96
|
-
# @param [String] ciphertext
|
97
|
-
# the ciphertext to decrypt
|
98
|
-
# @param [Vault::Client] client
|
99
|
-
# the Vault client to use
|
100
|
-
#
|
101
|
-
# @return [String]
|
102
|
-
# the decrypted plaintext text
|
103
|
-
def self.decrypt(path, key, ciphertext, client = self.client)
|
104
|
-
if ciphertext.blank?
|
105
|
-
return ciphertext
|
57
|
+
# Delegating `respond_to` to the {Vault::Client}.
|
58
|
+
def respond_to_missing?(m, include_private = false)
|
59
|
+
client.respond_to?(m, include_private) || super
|
106
60
|
end
|
107
61
|
|
108
|
-
|
109
|
-
|
62
|
+
# Encrypt the given plaintext data using the provided mount and key.
|
63
|
+
#
|
64
|
+
# @param [String] path
|
65
|
+
# the mount point
|
66
|
+
# @param [String] key
|
67
|
+
# the key to encrypt at
|
68
|
+
# @param [String] plaintext
|
69
|
+
# the plaintext to encrypt
|
70
|
+
# @param [Vault::Client] client
|
71
|
+
# the Vault client to use
|
72
|
+
#
|
73
|
+
# @return [String]
|
74
|
+
# the encrypted cipher text
|
75
|
+
def encrypt(path, key, plaintext, client = self.client)
|
76
|
+
if plaintext.blank?
|
77
|
+
return plaintext
|
78
|
+
end
|
110
79
|
|
111
|
-
|
112
|
-
if
|
113
|
-
|
114
|
-
|
115
|
-
|
80
|
+
path = path.to_s if !path.is_a?(String)
|
81
|
+
key = key.to_s if !key.is_a?(String)
|
82
|
+
|
83
|
+
with_retries do
|
84
|
+
if self.enabled?
|
85
|
+
result = self.vault_encrypt(path, key, plaintext, client)
|
86
|
+
else
|
87
|
+
result = self.memory_encrypt(path, key, plaintext, client)
|
88
|
+
end
|
89
|
+
|
90
|
+
return self.force_encoding(result)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Decrypt the given ciphertext data using the provided mount and key.
|
95
|
+
#
|
96
|
+
# @param [String] path
|
97
|
+
# the mount point
|
98
|
+
# @param [String] key
|
99
|
+
# the key to decrypt at
|
100
|
+
# @param [String] ciphertext
|
101
|
+
# the ciphertext to decrypt
|
102
|
+
# @param [Vault::Client] client
|
103
|
+
# the Vault client to use
|
104
|
+
#
|
105
|
+
# @return [String]
|
106
|
+
# the decrypted plaintext text
|
107
|
+
def decrypt(path, key, ciphertext, client = self.client)
|
108
|
+
if ciphertext.blank?
|
109
|
+
return ciphertext
|
116
110
|
end
|
117
111
|
|
118
|
-
|
112
|
+
path = path.to_s if !path.is_a?(String)
|
113
|
+
key = key.to_s if !key.is_a?(String)
|
114
|
+
|
115
|
+
with_retries do
|
116
|
+
if self.enabled?
|
117
|
+
result = self.vault_decrypt(path, key, ciphertext, client)
|
118
|
+
else
|
119
|
+
result = self.memory_decrypt(path, key, ciphertext, client)
|
120
|
+
end
|
121
|
+
|
122
|
+
return self.force_encoding(result)
|
123
|
+
end
|
119
124
|
end
|
120
|
-
end
|
121
125
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
126
|
+
# Get the serializer that corresponds to the given key. If the key does not
|
127
|
+
# correspond to a known serializer, an exception will be raised.
|
128
|
+
#
|
129
|
+
# @param [#to_sym] key
|
130
|
+
# the name of the serializer
|
131
|
+
#
|
132
|
+
# @return [~Serializer]
|
133
|
+
def serializer_for(key)
|
134
|
+
key = key.to_sym if !key.is_a?(Symbol)
|
135
|
+
|
136
|
+
if serializer = SERIALIZERS[key]
|
137
|
+
return serializer
|
138
|
+
else
|
139
|
+
raise Vault::Rails::UnknownSerializerError.new(key)
|
140
|
+
end
|
136
141
|
end
|
137
|
-
end
|
138
142
|
|
139
|
-
|
143
|
+
protected
|
140
144
|
|
141
|
-
|
142
|
-
|
143
|
-
|
145
|
+
# Perform in-memory encryption. This is useful for testing and development.
|
146
|
+
def memory_encrypt(path, key, plaintext, client)
|
147
|
+
log_warning(DEV_WARNING)
|
144
148
|
|
145
|
-
|
149
|
+
return nil if plaintext.nil?
|
146
150
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
151
|
+
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
|
152
|
+
cipher.encrypt
|
153
|
+
cipher.key = memory_key_for(path, key)
|
154
|
+
return Base64.strict_encode64(cipher.update(plaintext) + cipher.final)
|
155
|
+
end
|
152
156
|
|
153
|
-
|
154
|
-
|
155
|
-
|
157
|
+
# Perform in-memory decryption. This is useful for testing and development.
|
158
|
+
def memory_decrypt(path, key, ciphertext, client)
|
159
|
+
log_warning(DEV_WARNING)
|
156
160
|
|
157
|
-
|
161
|
+
return nil if ciphertext.nil?
|
158
162
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
163
|
+
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
|
164
|
+
cipher.decrypt
|
165
|
+
cipher.key = memory_key_for(path, key)
|
166
|
+
return cipher.update(Base64.strict_decode64(ciphertext)) + cipher.final
|
167
|
+
end
|
164
168
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
+
# Perform encryption using Vault. This will raise exceptions if Vault is
|
170
|
+
# unavailable.
|
171
|
+
def vault_encrypt(path, key, plaintext, client)
|
172
|
+
return nil if plaintext.nil?
|
169
173
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
174
|
+
route = File.join(path, "encrypt", key)
|
175
|
+
secret = client.logical.write(route,
|
176
|
+
plaintext: Base64.strict_encode64(plaintext),
|
177
|
+
)
|
178
|
+
return secret.data[:ciphertext]
|
179
|
+
end
|
176
180
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
+
# Perform decryption using Vault. This will raise exceptions if Vault is
|
182
|
+
# unavailable.
|
183
|
+
def vault_decrypt(path, key, ciphertext, client)
|
184
|
+
return nil if ciphertext.nil?
|
181
185
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
+
route = File.join(path, "decrypt", key)
|
187
|
+
secret = client.logical.write(route, ciphertext: ciphertext)
|
188
|
+
return Base64.strict_decode64(secret.data[:plaintext])
|
189
|
+
end
|
186
190
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
191
|
+
# The symmetric key for the given params.
|
192
|
+
# @return [String]
|
193
|
+
def memory_key_for(path, key)
|
194
|
+
return Base64.strict_encode64("#{path}/#{key}".ljust(32, "x"))
|
195
|
+
end
|
192
196
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
197
|
+
# Forces the encoding into the default Rails encoding and returns the
|
198
|
+
# newly encoded string.
|
199
|
+
# @return [String]
|
200
|
+
def force_encoding(str)
|
201
|
+
encoding = ::Rails.application.config.encoding || DEFAULT_ENCODING
|
202
|
+
str.force_encoding(encoding).encode(encoding)
|
203
|
+
end
|
200
204
|
|
201
|
-
|
205
|
+
private
|
202
206
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
207
|
+
def with_retries(client = self.client, &block)
|
208
|
+
exceptions = [Vault::HTTPConnectionError, Vault::HTTPServerError]
|
209
|
+
options = {
|
210
|
+
attempts: self.retry_attempts,
|
211
|
+
base: self.retry_base,
|
212
|
+
max_wait: self.retry_max_wait,
|
213
|
+
}
|
210
214
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
215
|
+
client.with_retries(*exceptions, options) do |i, e|
|
216
|
+
if !e.nil?
|
217
|
+
log_warning "[vault-rails] (#{i}) An error occurred when trying to " \
|
218
|
+
"communicate with Vault: #{e.message}"
|
219
|
+
end
|
216
220
|
|
217
|
-
|
221
|
+
yield
|
222
|
+
end
|
218
223
|
end
|
219
|
-
end
|
220
224
|
|
221
|
-
|
222
|
-
|
223
|
-
|
225
|
+
def log_warning(msg)
|
226
|
+
if defined?(::Rails) && ::Rails.logger != nil
|
227
|
+
::Rails.logger.warn { msg }
|
228
|
+
end
|
224
229
|
end
|
225
230
|
end
|
226
231
|
end
|
227
232
|
end
|
233
|
+
|
234
|
+
Vault::Rails.setup!
|
data/lib/vault/rails/version.rb
CHANGED
@@ -22,15 +22,17 @@ Rails.application.configure do
|
|
22
22
|
# Raise an error on page load if there are pending migrations.
|
23
23
|
config.active_record.migration_error = :page_load
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
25
|
+
if config.respond_to?(:assets)
|
26
|
+
# Debug mode disables concatenation and preprocessing of assets.
|
27
|
+
# This option may cause significant delays in view rendering with a large
|
28
|
+
# number of complex assets.
|
29
|
+
config.assets.debug = true
|
30
|
+
|
31
|
+
# Adds additional error checking when serving assets at runtime.
|
32
|
+
# Checks for improperly declared sprockets dependencies.
|
33
|
+
# Raises helpful error messages.
|
34
|
+
config.assets.raise_runtime_errors = true
|
35
|
+
end
|
34
36
|
|
35
37
|
# Raises error for missing translations
|
36
38
|
# config.action_view.raise_on_missing_translations = true
|
@@ -13,8 +13,13 @@ Rails.application.configure do
|
|
13
13
|
config.eager_load = false
|
14
14
|
|
15
15
|
# Configure static asset server for tests with Cache-Control for performance.
|
16
|
-
config.
|
17
|
-
|
16
|
+
if config.respond_to?(:public_file_server)
|
17
|
+
config.public_file_server.enabled = true
|
18
|
+
config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }
|
19
|
+
else
|
20
|
+
config.serve_static_files = true
|
21
|
+
config.static_cache_control = 'public, max-age=3600'
|
22
|
+
end
|
18
23
|
|
19
24
|
# Show full error reports and disable caching.
|
20
25
|
config.consider_all_requests_local = true
|
@@ -1,7 +1,9 @@
|
|
1
1
|
# Be sure to restart your server when you modify this file.
|
2
2
|
|
3
3
|
# Version of your assets, change this if you want to expire all your assets.
|
4
|
-
Rails.application.config.assets
|
4
|
+
if Rails.application.config.respond_to?(:assets)
|
5
|
+
Rails.application.config.assets.version = '1.0'
|
6
|
+
end
|
5
7
|
|
6
8
|
# Precompile additional assets.
|
7
9
|
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
|
Binary file
|