chamber 2.10.2 → 2.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'shellwords'
|
3
4
|
require 'chamber/instance'
|
4
5
|
|
@@ -9,8 +10,8 @@ module Securable
|
|
9
10
|
super
|
10
11
|
|
11
12
|
ignored_settings_options = options.
|
12
|
-
|
13
|
-
|
13
|
+
merge(files: ignored_settings_filepaths).
|
14
|
+
reject { |k, _v| k == 'basepath' }
|
14
15
|
self.ignored_settings_instance = Chamber::Instance.new(ignored_settings_options)
|
15
16
|
self.current_settings_instance = Chamber::Instance.new(options)
|
16
17
|
self.only_sensitive = options[:only_sensitive]
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'chamber/commands/base'
|
4
|
+
require 'chamber/commands/securable'
|
3
5
|
|
4
6
|
module Chamber
|
5
7
|
module Commands
|
@@ -12,7 +14,7 @@ class Secure < Chamber::Commands::Base
|
|
12
14
|
|
13
15
|
def call
|
14
16
|
disable_warnings do
|
15
|
-
insecure_environment_variables.
|
17
|
+
insecure_environment_variables.each_key do |key|
|
16
18
|
if dry_run
|
17
19
|
shell.say_status 'encrypt', key, :blue
|
18
20
|
else
|
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'pp'
|
3
4
|
require 'chamber/commands/base'
|
4
5
|
|
5
6
|
module Chamber
|
6
7
|
module Commands
|
7
8
|
class Show < Chamber::Commands::Base
|
9
|
+
attr_accessor :as_env,
|
10
|
+
:only_sensitive
|
11
|
+
|
8
12
|
def initialize(options = {})
|
9
13
|
super
|
10
14
|
|
@@ -17,17 +21,14 @@ class Show < Chamber::Commands::Base
|
|
17
21
|
settings.to_s(pair_separator: "\n")
|
18
22
|
else
|
19
23
|
PP.
|
20
|
-
|
21
|
-
|
22
|
-
|
24
|
+
pp(settings.to_hash, StringIO.new, 60).
|
25
|
+
string.
|
26
|
+
chomp
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
26
30
|
protected
|
27
31
|
|
28
|
-
attr_accessor :as_env,
|
29
|
-
:only_sensitive
|
30
|
-
|
31
32
|
def settings
|
32
33
|
@settings ||= if only_sensitive
|
33
34
|
chamber.settings.securable
|
@@ -1,31 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'chamber/context_resolver'
|
3
4
|
|
4
5
|
module Chamber
|
5
6
|
class Configuration
|
6
7
|
attr_accessor :basepath,
|
7
|
-
:
|
8
|
-
:
|
8
|
+
:decryption_keys,
|
9
|
+
:encryption_keys,
|
9
10
|
:files,
|
10
11
|
:namespaces
|
11
12
|
|
12
13
|
def initialize(options = {})
|
13
|
-
options
|
14
|
+
options = ContextResolver.resolve(options)
|
14
15
|
|
15
|
-
self.basepath
|
16
|
-
self.namespaces
|
17
|
-
self.
|
18
|
-
self.
|
19
|
-
self.files
|
16
|
+
self.basepath = options.fetch(:basepath)
|
17
|
+
self.namespaces = options.fetch(:namespaces)
|
18
|
+
self.decryption_keys = options.fetch(:decryption_keys)
|
19
|
+
self.encryption_keys = options.fetch(:encryption_keys)
|
20
|
+
self.files = options.fetch(:files)
|
20
21
|
end
|
21
22
|
|
22
23
|
def to_hash
|
23
24
|
{
|
24
|
-
basepath:
|
25
|
-
|
26
|
-
|
27
|
-
files:
|
28
|
-
namespaces:
|
25
|
+
basepath: basepath,
|
26
|
+
decryption_keys: decryption_keys,
|
27
|
+
encryption_keys: encryption_keys,
|
28
|
+
files: files,
|
29
|
+
namespaces: namespaces,
|
29
30
|
}
|
30
31
|
end
|
31
32
|
end
|
@@ -1,64 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'pathname'
|
3
4
|
require 'socket'
|
4
5
|
require 'hashie/mash'
|
5
|
-
|
6
|
+
|
7
|
+
require 'chamber/keys/decryption'
|
8
|
+
require 'chamber/keys/encryption'
|
6
9
|
|
7
10
|
module Chamber
|
8
11
|
class ContextResolver
|
12
|
+
attr_accessor :options
|
13
|
+
|
9
14
|
def initialize(options = {})
|
10
|
-
self.options =
|
15
|
+
self.options = options.dup
|
11
16
|
end
|
12
17
|
|
13
|
-
# rubocop:disable Metrics/AbcSize, Metrics/
|
14
|
-
# rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength
|
18
|
+
# rubocop:disable Metrics/AbcSize, Metrics/LineLength
|
15
19
|
def resolve
|
16
|
-
options[:rootpath]
|
17
|
-
options[:rootpath]
|
18
|
-
options[:
|
19
|
-
options[:
|
20
|
-
options[:namespaces] ||= []
|
21
|
-
options[:preset] ||= resolve_preset
|
20
|
+
options[:rootpath] ||= Pathname.pwd
|
21
|
+
options[:rootpath] = Pathname.new(options[:rootpath])
|
22
|
+
options[:namespaces] ||= []
|
23
|
+
options[:preset] ||= resolve_preset
|
22
24
|
|
23
25
|
if %w{rails rails-engine}.include?(options[:preset])
|
24
|
-
if options[:preset]
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
options[:rootpath] = if (engine_spec_dummy_directory + 'config.ru').exist?
|
29
|
-
engine_spec_dummy_directory
|
30
|
-
elsif (engine_test_dummy_directory + 'config.ru').exist?
|
31
|
-
engine_test_dummy_directory
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
options[:basepath] ||= options[:rootpath] + 'config'
|
36
|
-
|
37
|
-
if options[:namespaces] == []
|
38
|
-
require options[:rootpath].join('config', 'application').to_s
|
39
|
-
|
40
|
-
options[:namespaces] = [
|
41
|
-
::Rails.env,
|
42
|
-
Socket.gethostname,
|
43
|
-
]
|
44
|
-
end
|
26
|
+
options[:rootpath] = detect_engine_root if options[:preset] == 'rails-engine'
|
27
|
+
options[:namespaces] = load_rails_default_namespaces(options[:rootpath]) if options[:namespaces] == []
|
28
|
+
options[:basepath] ||= options[:rootpath] + 'config'
|
45
29
|
else
|
46
|
-
options[:basepath]
|
30
|
+
options[:basepath] ||= options[:rootpath]
|
47
31
|
end
|
48
32
|
|
49
|
-
options[:
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
33
|
+
options[:encryption_keys] = Keys::Encryption.resolve(filenames: options[:encryption_keys],
|
34
|
+
namespaces: options[:namespaces],
|
35
|
+
rootpath: options[:rootpath])
|
36
|
+
options[:decryption_keys] = Keys::Decryption.resolve(filenames: options[:decryption_keys],
|
37
|
+
namespaces: options[:namespaces],
|
38
|
+
rootpath: options[:rootpath])
|
39
|
+
options[:basepath] = Pathname.new(options[:basepath])
|
40
|
+
options[:files] ||= [
|
41
|
+
options[:basepath] + 'settings*.yml',
|
42
|
+
options[:basepath] + 'settings',
|
43
|
+
]
|
55
44
|
|
56
|
-
options
|
57
|
-
rescue LoadError
|
58
45
|
options
|
59
46
|
end
|
60
|
-
# rubocop:enable Metrics/AbcSize, Metrics/
|
61
|
-
# rubocop:enable Metrics/PerceivedComplexity, Metrics/MethodLength
|
47
|
+
# rubocop:enable Metrics/AbcSize, Metrics/LineLength
|
62
48
|
|
63
49
|
def self.resolve(options = {})
|
64
50
|
new(options).resolve
|
@@ -66,8 +52,6 @@ class ContextResolver
|
|
66
52
|
|
67
53
|
protected
|
68
54
|
|
69
|
-
attr_accessor :options
|
70
|
-
|
71
55
|
def resolve_preset
|
72
56
|
if in_a_rails_project?
|
73
57
|
'rails'
|
@@ -76,17 +60,6 @@ class ContextResolver
|
|
76
60
|
end
|
77
61
|
end
|
78
62
|
|
79
|
-
def resolve_encryption_key(key)
|
80
|
-
key ||= options[:rootpath] + '.chamber.pub.pem'
|
81
|
-
|
82
|
-
key if Pathname.new(key).readable?
|
83
|
-
end
|
84
|
-
|
85
|
-
def resolve_decryption_key(key)
|
86
|
-
DecryptionKey.resolve(filename: key,
|
87
|
-
rootpath: options[:rootpath])
|
88
|
-
end
|
89
|
-
|
90
63
|
def in_a_rails_project?
|
91
64
|
(options[:rootpath] + 'config.ru').exist? &&
|
92
65
|
rails_executable_exists?
|
@@ -102,5 +75,29 @@ class ContextResolver
|
|
102
75
|
options[:rootpath].join('script', 'rails').exist? ||
|
103
76
|
options[:rootpath].join('script', 'console').exist?
|
104
77
|
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def detect_engine_root
|
82
|
+
engine_spec_dummy_directory = options[:rootpath] + 'spec' + 'dummy'
|
83
|
+
engine_test_dummy_directory = options[:rootpath] + 'test' + 'dummy'
|
84
|
+
|
85
|
+
if (engine_spec_dummy_directory + 'config.ru').exist?
|
86
|
+
engine_spec_dummy_directory
|
87
|
+
elsif (engine_test_dummy_directory + 'config.ru').exist?
|
88
|
+
engine_test_dummy_directory
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def load_rails_default_namespaces(root)
|
93
|
+
require root.join('config', 'application').to_s
|
94
|
+
|
95
|
+
[
|
96
|
+
::Rails.env,
|
97
|
+
Socket.gethostname,
|
98
|
+
]
|
99
|
+
rescue LoadError
|
100
|
+
[]
|
101
|
+
end
|
105
102
|
end
|
106
103
|
end
|
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Chamber
|
2
4
|
module EncryptionMethods
|
3
5
|
class None
|
4
|
-
def self.encrypt(_key, value,
|
6
|
+
def self.encrypt(_key, value, _encryption_keys)
|
5
7
|
value
|
6
8
|
end
|
7
9
|
|
8
|
-
def self.decrypt(key, value,
|
10
|
+
def self.decrypt(key, value, _decryption_keys)
|
9
11
|
return value if value.nil?
|
10
12
|
|
11
13
|
warn "WARNING: It appears that you would like to keep your information for #{key} " \
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Chamber
|
2
4
|
module EncryptionMethods
|
3
5
|
class PublicKey
|
4
|
-
def self.encrypt(_key, value,
|
6
|
+
def self.encrypt(_key, value, encryption_keys)
|
5
7
|
value = YAML.dump(value)
|
6
|
-
encrypted_string =
|
8
|
+
encrypted_string = encryption_keys.public_encrypt(value)
|
7
9
|
|
8
10
|
Base64.strict_encode64(encrypted_string)
|
9
11
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Chamber
|
2
4
|
module EncryptionMethods
|
3
5
|
class Ssl
|
4
6
|
LARGE_DATA_STRING_PATTERN = %r{\A([A-Za-z0-9\+\/#]*\={0,2})#([A-Za-z0-9\+\/#]*\={0,2})#([A-Za-z0-9\+\/#]*\={0,2})\z} # rubocop:disable Metrics/LineLength
|
5
7
|
|
6
|
-
def self.encrypt(_key, value,
|
8
|
+
def self.encrypt(_key, value, encryption_keys)
|
7
9
|
value = YAML.dump(value)
|
8
10
|
cipher = OpenSSL::Cipher.new('AES-128-CBC')
|
9
11
|
cipher.encrypt
|
@@ -14,7 +16,7 @@ class Ssl
|
|
14
16
|
encrypted_data = cipher.update(value) + cipher.final
|
15
17
|
|
16
18
|
# encrypt the key with the public key
|
17
|
-
encrypted_key =
|
19
|
+
encrypted_key = encryption_keys.public_encrypt(symmetric_key)
|
18
20
|
|
19
21
|
# assemble the resulting Base64 encoded data, the key
|
20
22
|
Base64.strict_encode64(encrypted_key) + '#' +
|
@@ -22,17 +24,17 @@ class Ssl
|
|
22
24
|
Base64.strict_encode64(encrypted_data)
|
23
25
|
end
|
24
26
|
|
25
|
-
def self.decrypt(key, value,
|
26
|
-
if
|
27
|
+
def self.decrypt(key, value, decryption_keys)
|
28
|
+
if decryption_keys.nil?
|
27
29
|
value
|
28
30
|
else
|
29
31
|
key, iv, decoded_string = value.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
match(LARGE_DATA_STRING_PATTERN).
|
33
|
+
captures.
|
34
|
+
map do |part|
|
35
|
+
Base64.strict_decode64(part)
|
34
36
|
end
|
35
|
-
key =
|
37
|
+
key = decryption_keys.private_decrypt(key)
|
36
38
|
|
37
39
|
cipher_dec = OpenSSL::Cipher.new('AES-128-CBC')
|
38
40
|
|
data/lib/chamber/file.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'pathname'
|
3
4
|
require 'yaml'
|
4
5
|
require 'erb'
|
@@ -9,6 +10,10 @@ require 'erb'
|
|
9
10
|
#
|
10
11
|
module Chamber
|
11
12
|
class File < Pathname
|
13
|
+
attr_accessor :namespaces,
|
14
|
+
:decryption_keys,
|
15
|
+
:encryption_keys
|
16
|
+
|
12
17
|
###
|
13
18
|
# Internal: Creates a settings file representing a path to a file on the
|
14
19
|
# filesystem.
|
@@ -37,9 +42,9 @@ class File < Pathname
|
|
37
42
|
# # => <Chamber::File>
|
38
43
|
#
|
39
44
|
def initialize(options = {})
|
40
|
-
self.namespaces
|
41
|
-
self.
|
42
|
-
self.
|
45
|
+
self.namespaces = options[:namespaces] || {}
|
46
|
+
self.decryption_keys = options[:decryption_keys] || {}
|
47
|
+
self.encryption_keys = options[:encryption_keys] || {}
|
43
48
|
|
44
49
|
super options.fetch(:path)
|
45
50
|
end
|
@@ -64,12 +69,13 @@ class File < Pathname
|
|
64
69
|
# ```
|
65
70
|
#
|
66
71
|
def to_settings
|
67
|
-
@data ||= Settings.new(settings:
|
68
|
-
namespaces:
|
69
|
-
|
70
|
-
|
72
|
+
@data ||= Settings.new(settings: file_contents_hash,
|
73
|
+
namespaces: namespaces,
|
74
|
+
decryption_keys: decryption_keys,
|
75
|
+
encryption_keys: encryption_keys)
|
71
76
|
end
|
72
77
|
|
78
|
+
# rubocop:disable Metrics/LineLength
|
73
79
|
def secure
|
74
80
|
insecure_settings = to_settings.insecure.to_flattened_name_hash
|
75
81
|
secure_settings = to_settings.insecure.secure.to_flattened_name_hash
|
@@ -82,28 +88,31 @@ class File < Pathname
|
|
82
88
|
escaped_value = Regexp.escape(value)
|
83
89
|
|
84
90
|
file_contents.
|
85
|
-
|
86
|
-
/^(\s*)
|
87
|
-
"\\
|
91
|
+
sub!(
|
92
|
+
/^(\s*)#{secure_prefix_pattern}#{escaped_name}(\s*):(\s*)['"]?#{escaped_value}['"]?$/,
|
93
|
+
"\\1#{secure_prefix}#{name_pieces.last}\\2:\\3#{secure_value}",
|
88
94
|
)
|
89
95
|
|
90
96
|
file_contents.
|
91
|
-
|
92
|
-
/^(\s*)
|
93
|
-
"\\
|
97
|
+
sub!(
|
98
|
+
/^(\s*)#{secure_prefix_pattern}#{escaped_name}(\s*):(\s*)\|((?:\n\1\s{2}.*)+)/,
|
99
|
+
"\\1#{secure_prefix}#{name_pieces.last}\\2:\\3#{secure_value}",
|
94
100
|
)
|
95
101
|
end
|
96
102
|
|
97
103
|
write(file_contents)
|
98
104
|
end
|
105
|
+
# rubocop:enable Metrics/LineLength
|
99
106
|
|
100
|
-
|
107
|
+
private
|
101
108
|
|
102
|
-
|
103
|
-
|
104
|
-
|
109
|
+
def secure_prefix
|
110
|
+
'_secure_'
|
111
|
+
end
|
105
112
|
|
106
|
-
|
113
|
+
def secure_prefix_pattern
|
114
|
+
@secure_prefix_pattern ||= Regexp.escape(secure_prefix)
|
115
|
+
end
|
107
116
|
|
108
117
|
def file_contents_hash
|
109
118
|
file_contents = read
|