chamber 2.10.2 → 2.11.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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE.txt +1 -1
- data/lib/chamber.rb +3 -6
- data/lib/chamber/binary/heroku.rb +1 -0
- data/lib/chamber/binary/runner.rb +29 -13
- data/lib/chamber/binary/travis.rb +1 -0
- data/lib/chamber/commands/base.rb +10 -9
- data/lib/chamber/commands/comparable.rb +1 -0
- data/lib/chamber/commands/compare.rb +8 -7
- data/lib/chamber/commands/files.rb +1 -0
- data/lib/chamber/commands/heroku.rb +1 -0
- data/lib/chamber/commands/heroku/clear.rb +2 -1
- data/lib/chamber/commands/heroku/compare.rb +1 -0
- data/lib/chamber/commands/heroku/pull.rb +3 -4
- data/lib/chamber/commands/heroku/push.rb +1 -0
- data/lib/chamber/commands/initialize.rb +88 -76
- data/lib/chamber/commands/securable.rb +3 -2
- data/lib/chamber/commands/secure.rb +3 -1
- data/lib/chamber/commands/show.rb +7 -6
- data/lib/chamber/commands/travis.rb +1 -0
- data/lib/chamber/commands/travis/secure.rb +1 -0
- data/lib/chamber/configuration.rb +14 -13
- data/lib/chamber/context_resolver.rb +52 -55
- data/lib/chamber/encryption_methods/none.rb +4 -2
- data/lib/chamber/encryption_methods/public_key.rb +4 -2
- data/lib/chamber/encryption_methods/ssl.rb +11 -9
- data/lib/chamber/errors/decryption_failure.rb +1 -0
- data/lib/chamber/file.rb +27 -18
- data/lib/chamber/file_set.rb +14 -13
- data/lib/chamber/filters/decryption_filter.rb +48 -18
- data/lib/chamber/filters/encryption_filter.rb +32 -22
- data/lib/chamber/filters/environment_filter.rb +109 -16
- data/lib/chamber/filters/failed_decryption_filter.rb +10 -8
- data/lib/chamber/filters/insecure_filter.rb +1 -0
- data/lib/chamber/filters/namespace_filter.rb +8 -7
- data/lib/chamber/filters/secure_filter.rb +10 -9
- data/lib/chamber/filters/translate_secure_keys_filter.rb +10 -9
- data/lib/chamber/instance.rb +5 -4
- data/lib/chamber/key_pair.rb +82 -0
- data/lib/chamber/keys/base.rb +64 -0
- data/lib/chamber/keys/decryption.rb +41 -0
- data/lib/chamber/keys/encryption.rb +41 -0
- data/lib/chamber/namespace_set.rb +10 -9
- data/lib/chamber/rails.rb +1 -0
- data/lib/chamber/rails/railtie.rb +1 -0
- data/lib/chamber/rubinius_fix.rb +1 -0
- data/lib/chamber/settings.rb +45 -41
- data/lib/chamber/types/secured.rb +14 -12
- data/lib/chamber/version.rb +2 -1
- metadata +28 -27
- metadata.gz.sig +0 -0
- data/lib/chamber/decryption_key.rb +0 -52
- data/lib/chamber/environmentable.rb +0 -27
- data/lib/chamber/filters/boolean_conversion_filter.rb +0 -41
@@ -1,22 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'hashie/mash'
|
3
4
|
|
4
5
|
module Chamber
|
5
6
|
module Filters
|
6
7
|
class SecureFilter
|
7
|
-
SECURE_KEY_TOKEN = /\A_secure_/
|
8
|
-
|
9
|
-
def initialize(options = {})
|
10
|
-
self.data = Hashie::Mash.new(options.fetch(:data))
|
11
|
-
end
|
12
|
-
|
13
8
|
def self.execute(options = {})
|
14
9
|
new(options).__send__(:execute)
|
15
10
|
end
|
16
11
|
|
17
|
-
|
12
|
+
attr_accessor :data,
|
13
|
+
:secure_key_token
|
18
14
|
|
19
|
-
|
15
|
+
def initialize(options = {})
|
16
|
+
self.data = Hashie::Mash.new(options.fetch(:data))
|
17
|
+
self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
20
21
|
|
21
22
|
def execute(raw_data = data)
|
22
23
|
settings = Hashie::Mash.new
|
@@ -25,7 +26,7 @@ class SecureFilter
|
|
25
26
|
secure_value = if value.respond_to? :each_pair
|
26
27
|
execute(value)
|
27
28
|
elsif key.respond_to? :match
|
28
|
-
value if key.match(
|
29
|
+
value if key.match(secure_key_token)
|
29
30
|
end
|
30
31
|
|
31
32
|
settings[key] = secure_value unless secure_value.nil?
|
@@ -1,22 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'hashie/mash'
|
3
4
|
|
4
5
|
module Chamber
|
5
6
|
module Filters
|
6
7
|
class TranslateSecureKeysFilter
|
7
|
-
SECURE_KEY_TOKEN = /\A_secure_/
|
8
|
-
|
9
|
-
def initialize(options = {})
|
10
|
-
self.data = options.fetch(:data).dup
|
11
|
-
end
|
12
|
-
|
13
8
|
def self.execute(options = {})
|
14
9
|
new(options).__send__(:execute)
|
15
10
|
end
|
16
11
|
|
17
|
-
|
12
|
+
attr_accessor :data,
|
13
|
+
:secure_key_token
|
18
14
|
|
19
|
-
|
15
|
+
def initialize(options = {})
|
16
|
+
self.data = options.fetch(:data).dup
|
17
|
+
self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
20
21
|
|
21
22
|
def execute(raw_data = data)
|
22
23
|
settings = Hashie::Mash.new
|
@@ -24,7 +25,7 @@ class TranslateSecureKeysFilter
|
|
24
25
|
raw_data.each_pair do |key, value|
|
25
26
|
value = execute(value) if value.respond_to? :each_pair
|
26
27
|
key = key.to_s
|
27
|
-
key = key.sub(
|
28
|
+
key = key.sub(secure_key_token, '') if key.match(secure_key_token)
|
28
29
|
|
29
30
|
settings[key] = value
|
30
31
|
end
|
data/lib/chamber/instance.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'chamber/configuration'
|
3
4
|
require 'chamber/file_set'
|
4
5
|
require 'chamber/settings'
|
@@ -29,21 +30,21 @@ class Instance
|
|
29
30
|
config = configuration.to_hash.merge(options)
|
30
31
|
|
31
32
|
Settings.
|
32
|
-
|
33
|
+
new(
|
33
34
|
config.merge(
|
34
35
|
settings: data,
|
35
36
|
pre_filters: [Filters::EncryptionFilter],
|
36
37
|
post_filters: [],
|
37
38
|
),
|
38
39
|
).
|
39
|
-
|
40
|
+
to_hash
|
40
41
|
end
|
41
42
|
|
42
43
|
def decrypt(data, options = {})
|
43
44
|
config = configuration.to_hash.merge(options)
|
44
45
|
|
45
46
|
Settings.
|
46
|
-
|
47
|
+
new(
|
47
48
|
config.merge(
|
48
49
|
settings: data,
|
49
50
|
pre_filters: [Filters::NamespaceFilter],
|
@@ -53,7 +54,7 @@ class Instance
|
|
53
54
|
],
|
54
55
|
),
|
55
56
|
).
|
56
|
-
|
57
|
+
to_hash
|
57
58
|
end
|
58
59
|
|
59
60
|
def to_s(options = {})
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
module Chamber
|
7
|
+
class KeyPair
|
8
|
+
attr_accessor :key_file_path,
|
9
|
+
:namespace,
|
10
|
+
:passphrase
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
self.namespace = options[:namespace]
|
14
|
+
self.passphrase = options.fetch(:passphrase, SecureRandom.uuid)
|
15
|
+
self.key_file_path = Pathname.new(options.fetch(:key_file_path))
|
16
|
+
end
|
17
|
+
|
18
|
+
def encrypted_private_key_filepath
|
19
|
+
key_file_path + encrypted_private_key_filename
|
20
|
+
end
|
21
|
+
|
22
|
+
def unencrypted_private_key_filepath
|
23
|
+
key_file_path + unencrypted_private_key_filename
|
24
|
+
end
|
25
|
+
|
26
|
+
def public_key_filepath
|
27
|
+
key_file_path + public_key_filename
|
28
|
+
end
|
29
|
+
|
30
|
+
def encrypted_private_key_pem
|
31
|
+
encrypted_private_key
|
32
|
+
end
|
33
|
+
|
34
|
+
def unencrypted_private_key_pem
|
35
|
+
unencrypted_private_key.to_pem
|
36
|
+
end
|
37
|
+
|
38
|
+
def public_key_pem
|
39
|
+
public_key.to_pem
|
40
|
+
end
|
41
|
+
|
42
|
+
def encrypted_private_key_filename
|
43
|
+
"#{base_key_filename}.enc"
|
44
|
+
end
|
45
|
+
|
46
|
+
def unencrypted_private_key_filename
|
47
|
+
"#{base_key_filename}.pem"
|
48
|
+
end
|
49
|
+
|
50
|
+
def public_key_filename
|
51
|
+
"#{base_key_filename}.pub.pem"
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def encrypted_private_key
|
57
|
+
@encrypted_private_key ||= \
|
58
|
+
unencrypted_private_key.export(encryption_cipher, passphrase)
|
59
|
+
end
|
60
|
+
|
61
|
+
def unencrypted_private_key
|
62
|
+
@unencrypted_private_key ||= OpenSSL::PKey::RSA.new(2048)
|
63
|
+
end
|
64
|
+
|
65
|
+
def public_key
|
66
|
+
@public_key ||= unencrypted_private_key.public_key
|
67
|
+
end
|
68
|
+
|
69
|
+
def encryption_cipher
|
70
|
+
@encryption_cipher ||= OpenSSL::Cipher.new('AES-128-CBC')
|
71
|
+
end
|
72
|
+
|
73
|
+
def base_key_filename
|
74
|
+
@base_key_filename ||= [
|
75
|
+
'.chamber',
|
76
|
+
namespace,
|
77
|
+
].
|
78
|
+
compact.
|
79
|
+
join('.')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Chamber
|
4
|
+
module Keys
|
5
|
+
class Base
|
6
|
+
def self.resolve(*args)
|
7
|
+
new(*args).resolve
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :namespaces,
|
11
|
+
:rootpath
|
12
|
+
attr_reader :filenames
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
self.rootpath = Pathname.new(options.fetch(:rootpath))
|
16
|
+
self.namespaces = options.fetch(:namespaces)
|
17
|
+
self.filenames = options[:filenames]
|
18
|
+
end
|
19
|
+
|
20
|
+
def resolve
|
21
|
+
filenames.each_with_object({}) do |filename, memo|
|
22
|
+
namespace = namespace_from_filename(filename) || '__default'
|
23
|
+
value = key_from_file_contents(filename) ||
|
24
|
+
key_from_environment_variable(filename)
|
25
|
+
|
26
|
+
memo[namespace.downcase.to_sym] = value if value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def filenames=(other)
|
31
|
+
@filenames = begin
|
32
|
+
paths = Array(other).
|
33
|
+
map { |o| Pathname.new(o) }.
|
34
|
+
compact
|
35
|
+
|
36
|
+
paths << default_key_file_path if paths.empty?
|
37
|
+
|
38
|
+
(
|
39
|
+
paths +
|
40
|
+
generate_key_filenames
|
41
|
+
).
|
42
|
+
uniq
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def key_from_file_contents(filename)
|
49
|
+
filename.readable? && filename.read
|
50
|
+
end
|
51
|
+
|
52
|
+
def key_from_environment_variable(filename)
|
53
|
+
ENV[environment_variable_from_filename(filename)]
|
54
|
+
end
|
55
|
+
|
56
|
+
def namespace_from_filename(filename)
|
57
|
+
filename.
|
58
|
+
basename.
|
59
|
+
to_s.
|
60
|
+
match(self.class::NAMESPACE_PATTERN) { |m| m[1].upcase }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'chamber/keys/base'
|
5
|
+
|
6
|
+
module Chamber
|
7
|
+
module Keys
|
8
|
+
class Decryption < Chamber::Keys::Base
|
9
|
+
NAMESPACE_PATTERN = /
|
10
|
+
\A # Beginning of Filename
|
11
|
+
\.chamber # Initial Chamber Prefix
|
12
|
+
\. # Pre-Namespace Dot
|
13
|
+
(\w+) # Namespace
|
14
|
+
\.pem # Extension
|
15
|
+
\z # End of Filename
|
16
|
+
/x
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def environment_variable_from_filename(filename)
|
21
|
+
[
|
22
|
+
'CHAMBER',
|
23
|
+
namespace_from_filename(filename),
|
24
|
+
'KEY',
|
25
|
+
].
|
26
|
+
compact.
|
27
|
+
join('_')
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_key_filenames
|
31
|
+
namespaces.map do |namespace|
|
32
|
+
rootpath + ".chamber.#{namespace}.pem"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_key_file_path
|
37
|
+
Pathname.new(rootpath + '.chamber.pem')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'chamber/keys/base'
|
5
|
+
|
6
|
+
module Chamber
|
7
|
+
module Keys
|
8
|
+
class Encryption < Chamber::Keys::Base
|
9
|
+
NAMESPACE_PATTERN = /
|
10
|
+
\A # Beginning of Filename
|
11
|
+
\.chamber # Initial Chamber Prefix
|
12
|
+
\. # Pre-Namespace Dot
|
13
|
+
(\w+) # Namespace
|
14
|
+
\.pub\.pem # Extension
|
15
|
+
\z # End of Filename
|
16
|
+
/x
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def environment_variable_from_filename(filename)
|
21
|
+
[
|
22
|
+
'CHAMBER',
|
23
|
+
namespace_from_filename(filename),
|
24
|
+
'PUBLIC_KEY',
|
25
|
+
].
|
26
|
+
compact.
|
27
|
+
join('_')
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_key_filenames
|
31
|
+
namespaces.map do |namespace|
|
32
|
+
rootpath + ".chamber.#{namespace}.pub.pem"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_key_file_path
|
37
|
+
Pathname.new(rootpath + '.chamber.pub.pem')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'set'
|
3
4
|
|
4
5
|
###
|
@@ -13,13 +14,6 @@ module Chamber
|
|
13
14
|
class NamespaceSet
|
14
15
|
include Enumerable
|
15
16
|
|
16
|
-
###
|
17
|
-
# Internal: Creates a new NamespaceSet from arrays, hashes and sets.
|
18
|
-
#
|
19
|
-
def initialize(raw_namespaces = {})
|
20
|
-
self.raw_namespaces = raw_namespaces
|
21
|
-
end
|
22
|
-
|
23
17
|
###
|
24
18
|
# Internal: Allows for more compact NamespaceSet creation by giving a list of
|
25
19
|
# namespace values.
|
@@ -34,6 +28,15 @@ class NamespaceSet
|
|
34
28
|
new(namespace_values)
|
35
29
|
end
|
36
30
|
|
31
|
+
attr_reader :raw_namespaces
|
32
|
+
|
33
|
+
###
|
34
|
+
# Internal: Creates a new NamespaceSet from arrays, hashes and sets.
|
35
|
+
#
|
36
|
+
def initialize(raw_namespaces = {})
|
37
|
+
self.raw_namespaces = raw_namespaces
|
38
|
+
end
|
39
|
+
|
37
40
|
###
|
38
41
|
# Internal: Allows a NamespaceSet to be combined with some other array-like
|
39
42
|
# object.
|
@@ -109,8 +112,6 @@ class NamespaceSet
|
|
109
112
|
|
110
113
|
protected
|
111
114
|
|
112
|
-
attr_accessor :raw_namespaces
|
113
|
-
|
114
115
|
###
|
115
116
|
# Internal: Sets the namespaces for the set from a variety of objects and
|
116
117
|
# processes them by checking to see if they can be 'called'.
|
data/lib/chamber/rails.rb
CHANGED
data/lib/chamber/rubinius_fix.rb
CHANGED
data/lib/chamber/settings.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'hashie/mash'
|
3
4
|
require 'chamber/namespace_set'
|
4
5
|
require 'chamber/filters/namespace_filter'
|
5
6
|
require 'chamber/filters/encryption_filter'
|
6
7
|
require 'chamber/filters/decryption_filter'
|
7
8
|
require 'chamber/filters/environment_filter'
|
8
|
-
require 'chamber/filters/boolean_conversion_filter'
|
9
9
|
require 'chamber/filters/secure_filter'
|
10
10
|
require 'chamber/filters/translate_secure_keys_filter'
|
11
11
|
require 'chamber/filters/insecure_filter'
|
@@ -16,24 +16,29 @@ require 'chamber/filters/failed_decryption_filter'
|
|
16
16
|
#
|
17
17
|
module Chamber
|
18
18
|
class Settings
|
19
|
-
|
19
|
+
attr_accessor :pre_filters,
|
20
|
+
:post_filters,
|
21
|
+
:encryption_keys,
|
22
|
+
:decryption_keys
|
23
|
+
attr_reader :namespaces
|
20
24
|
|
25
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/LineLength
|
21
26
|
def initialize(options = {})
|
22
|
-
self.namespaces
|
23
|
-
self.raw_data
|
24
|
-
self.
|
25
|
-
self.
|
26
|
-
self.pre_filters
|
27
|
-
|
28
|
-
|
29
|
-
self.post_filters
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
]
|
27
|
+
self.namespaces = options[:namespaces] || []
|
28
|
+
self.raw_data = options[:settings] || {}
|
29
|
+
self.decryption_keys = options[:decryption_keys] || {}
|
30
|
+
self.encryption_keys = options[:encryption_keys] || {}
|
31
|
+
self.pre_filters = options[:pre_filters] || [
|
32
|
+
Filters::NamespaceFilter,
|
33
|
+
]
|
34
|
+
self.post_filters = options[:post_filters] || [
|
35
|
+
Filters::DecryptionFilter,
|
36
|
+
Filters::EnvironmentFilter,
|
37
|
+
Filters::FailedDecryptionFilter,
|
38
|
+
Filters::TranslateSecureKeysFilter,
|
39
|
+
]
|
36
40
|
end
|
41
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/LineLength
|
37
42
|
|
38
43
|
###
|
39
44
|
# Internal: Converts a Settings object into a hash that is compatible as an
|
@@ -183,12 +188,14 @@ class Settings
|
|
183
188
|
Settings.new(settings: other)
|
184
189
|
end
|
185
190
|
|
191
|
+
# rubocop:disable Metrics/LineLength
|
186
192
|
Settings.new(
|
187
|
-
|
188
|
-
|
189
|
-
namespaces:
|
190
|
-
settings:
|
193
|
+
encryption_keys: encryption_keys.any? ? encryption_keys : other_settings.encryption_keys,
|
194
|
+
decryption_keys: decryption_keys.any? ? decryption_keys : other_settings.decryption_keys,
|
195
|
+
namespaces: (namespaces + other_settings.namespaces),
|
196
|
+
settings: raw_data.merge(other_settings.raw_data),
|
191
197
|
)
|
198
|
+
# rubocop:enable Metrics/LineLength
|
192
199
|
end
|
193
200
|
|
194
201
|
###
|
@@ -235,13 +242,17 @@ class Settings
|
|
235
242
|
))
|
236
243
|
end
|
237
244
|
|
238
|
-
|
245
|
+
def method_missing(name, *args)
|
246
|
+
return data.public_send(name, *args) if data.respond_to?(name)
|
239
247
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
248
|
+
super
|
249
|
+
end
|
250
|
+
|
251
|
+
def respond_to_missing?(name, include_private = false)
|
252
|
+
data.respond_to?(name, include_private)
|
253
|
+
end
|
254
|
+
|
255
|
+
protected
|
245
256
|
|
246
257
|
def raw_data=(new_raw_data)
|
247
258
|
@raw_data = Hashie::Mash.new(new_raw_data)
|
@@ -265,24 +276,17 @@ class Settings
|
|
265
276
|
end
|
266
277
|
end
|
267
278
|
|
279
|
+
def secure_key_prefix
|
280
|
+
'_secure_'
|
281
|
+
end
|
282
|
+
|
268
283
|
def metadata
|
269
284
|
{
|
270
|
-
|
271
|
-
|
272
|
-
|
285
|
+
decryption_keys: decryption_keys,
|
286
|
+
encryption_keys: encryption_keys,
|
287
|
+
namespaces: namespaces,
|
288
|
+
secure_key_prefix: secure_key_prefix,
|
273
289
|
}
|
274
290
|
end
|
275
|
-
|
276
|
-
public
|
277
|
-
|
278
|
-
def method_missing(name, *args)
|
279
|
-
return data.public_send(name, *args) if data.respond_to?(name)
|
280
|
-
|
281
|
-
super
|
282
|
-
end
|
283
|
-
|
284
|
-
def respond_to_missing?(name, include_private = false)
|
285
|
-
data.respond_to?(name, include_private)
|
286
|
-
end
|
287
291
|
end
|
288
292
|
end
|