chamber 2.13.1 → 2.14.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +101 -26
  5. data/lib/chamber.rb +72 -10
  6. data/lib/chamber/adapters/cloud/circle_ci.rb +16 -13
  7. data/lib/chamber/adapters/cloud/heroku.rb +40 -13
  8. data/lib/chamber/binary/circle_ci.rb +25 -12
  9. data/lib/chamber/binary/heroku.rb +31 -12
  10. data/lib/chamber/binary/runner.rb +37 -27
  11. data/lib/chamber/binary/travis.rb +5 -3
  12. data/lib/chamber/commands/base.rb +10 -16
  13. data/lib/chamber/commands/cloud/base.rb +3 -3
  14. data/lib/chamber/commands/cloud/pull.rb +2 -2
  15. data/lib/chamber/commands/cloud/push.rb +7 -7
  16. data/lib/chamber/commands/comparable.rb +2 -2
  17. data/lib/chamber/commands/compare.rb +6 -9
  18. data/lib/chamber/commands/initialize.rb +26 -22
  19. data/lib/chamber/commands/securable.rb +9 -9
  20. data/lib/chamber/commands/secure.rb +2 -2
  21. data/lib/chamber/commands/show.rb +8 -8
  22. data/lib/chamber/commands/sign.rb +2 -2
  23. data/lib/chamber/commands/verify.rb +2 -2
  24. data/lib/chamber/configuration.rb +6 -3
  25. data/lib/chamber/context_resolver.rb +8 -7
  26. data/lib/chamber/encryption_methods/ssl.rb +12 -12
  27. data/lib/chamber/file.rb +16 -14
  28. data/lib/chamber/file_set.rb +18 -8
  29. data/lib/chamber/files/signature.rb +16 -14
  30. data/lib/chamber/filters/decryption_filter.rb +12 -10
  31. data/lib/chamber/filters/encryption_filter.rb +8 -8
  32. data/lib/chamber/filters/environment_filter.rb +12 -14
  33. data/lib/chamber/filters/failed_decryption_filter.rb +6 -6
  34. data/lib/chamber/filters/insecure_filter.rb +3 -3
  35. data/lib/chamber/filters/namespace_filter.rb +5 -5
  36. data/lib/chamber/filters/secure_filter.rb +5 -5
  37. data/lib/chamber/filters/translate_secure_keys_filter.rb +5 -5
  38. data/lib/chamber/instance.rb +37 -21
  39. data/lib/chamber/key_pair.rb +7 -7
  40. data/lib/chamber/keys/base.rb +13 -13
  41. data/lib/chamber/keys/decryption.rb +3 -3
  42. data/lib/chamber/keys/encryption.rb +3 -3
  43. data/lib/chamber/namespace_set.rb +2 -4
  44. data/lib/chamber/settings.rb +45 -43
  45. data/lib/chamber/types/secured.rb +8 -10
  46. data/lib/chamber/version.rb +1 -1
  47. data/templates/settings.yml +2 -0
  48. metadata +24 -26
  49. metadata.gz.sig +0 -0
@@ -6,15 +6,15 @@ require 'chamber/instance'
6
6
  module Chamber
7
7
  module Commands
8
8
  module Securable
9
- def initialize(options = {})
10
- super
11
-
12
- ignored_settings_options = options.
13
- merge(files: ignored_settings_filepaths).
14
- reject { |k, _v| k == 'basepath' }
15
- self.ignored_settings_instance = Chamber::Instance.new(ignored_settings_options)
16
- self.current_settings_instance = Chamber::Instance.new(options)
17
- self.only_sensitive = options[:only_sensitive]
9
+ def initialize(only_sensitive: nil, **args)
10
+ super(**args)
11
+
12
+ ignored_settings_options = args
13
+ .merge(files: ignored_settings_filepaths)
14
+ .reject { |k, _v| k == 'basepath' }
15
+ self.ignored_settings_instance = Chamber::Instance.new(**ignored_settings_options)
16
+ self.current_settings_instance = Chamber::Instance.new(**args)
17
+ self.only_sensitive = only_sensitive
18
18
  end
19
19
 
20
20
  protected
@@ -8,8 +8,8 @@ module Commands
8
8
  class Secure < Chamber::Commands::Base
9
9
  include Chamber::Commands::Securable
10
10
 
11
- def initialize(options = {})
12
- super(options.merge(namespaces: ['*']))
11
+ def initialize(**args)
12
+ super(**args.merge(namespaces: ['*']))
13
13
  end
14
14
 
15
15
  def call
@@ -9,21 +9,21 @@ class Show < Chamber::Commands::Base
9
9
  attr_accessor :as_env,
10
10
  :only_sensitive
11
11
 
12
- def initialize(options = {})
13
- super
12
+ def initialize(as_env: nil, only_sensitive: nil, **args)
13
+ super(**args)
14
14
 
15
- self.as_env = options[:as_env]
16
- self.only_sensitive = options[:only_sensitive]
15
+ self.as_env = as_env
16
+ self.only_sensitive = only_sensitive
17
17
  end
18
18
 
19
19
  def call
20
20
  if as_env
21
21
  settings.to_s(pair_separator: "\n")
22
22
  else
23
- PP.
24
- pp(settings.to_hash, StringIO.new, 60).
25
- string.
26
- chomp
23
+ PP
24
+ .pp(settings.to_hash, StringIO.new, 60)
25
+ .string
26
+ .chomp
27
27
  end
28
28
  end
29
29
 
@@ -5,8 +5,8 @@ require 'chamber/commands/base'
5
5
  module Chamber
6
6
  module Commands
7
7
  class Sign < Chamber::Commands::Base
8
- def initialize(options = {})
9
- super(options.merge(namespaces: ['*']))
8
+ def initialize(**args)
9
+ super(**args.merge(namespaces: ['*']))
10
10
  end
11
11
 
12
12
  def call
@@ -5,8 +5,8 @@ require 'chamber/commands/base'
5
5
  module Chamber
6
6
  module Commands
7
7
  class Verify < Chamber::Commands::Base
8
- def initialize(options = {})
9
- super(options.merge(namespaces: ['*']))
8
+ def initialize(**args)
9
+ super(**args.merge(namespaces: ['*']))
10
10
  end
11
11
 
12
12
  def call
@@ -9,10 +9,11 @@ class Configuration
9
9
  :encryption_keys,
10
10
  :files,
11
11
  :namespaces,
12
- :rootpath
12
+ :rootpath,
13
+ :signature_name
13
14
 
14
- def initialize(options = {})
15
- options = ContextResolver.resolve(options)
15
+ def initialize(**args)
16
+ options = ContextResolver.resolve(**args)
16
17
 
17
18
  self.basepath = options.fetch(:basepath)
18
19
  self.namespaces = options.fetch(:namespaces)
@@ -20,6 +21,7 @@ class Configuration
20
21
  self.encryption_keys = options.fetch(:encryption_keys)
21
22
  self.files = options.fetch(:files)
22
23
  self.rootpath = options.fetch(:rootpath)
24
+ self.signature_name = options.fetch(:signature_name)
23
25
  end
24
26
 
25
27
  def to_hash
@@ -29,6 +31,7 @@ class Configuration
29
31
  encryption_keys: encryption_keys,
30
32
  files: files,
31
33
  namespaces: namespaces,
34
+ signature_name: signature_name,
32
35
  }
33
36
  end
34
37
  end
@@ -3,7 +3,6 @@
3
3
  require 'pathname'
4
4
  require 'socket'
5
5
 
6
- require 'chamber/core_ext/hash'
7
6
  require 'chamber/keys/decryption'
8
7
  require 'chamber/keys/encryption'
9
8
 
@@ -11,11 +10,11 @@ module Chamber
11
10
  class ContextResolver
12
11
  attr_accessor :options
13
12
 
14
- def initialize(options = {})
15
- self.options = options.transform_keys(&:to_sym)
13
+ def initialize(**args)
14
+ self.options = args
16
15
  end
17
16
 
18
- # rubocop:disable Metrics/AbcSize, Metrics/LineLength
17
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Layout/LineLength
19
18
  def resolve
20
19
  options[:rootpath] ||= Pathname.pwd
21
20
  options[:rootpath] = Pathname.new(options[:rootpath])
@@ -43,12 +42,14 @@ class ContextResolver
43
42
  options[:basepath] + 'settings',
44
43
  ]
45
44
 
45
+ options[:signature_name] = options[:signature_name]
46
+
46
47
  options
47
48
  end
48
- # rubocop:enable Metrics/AbcSize, Metrics/LineLength
49
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Layout/LineLength
49
50
 
50
- def self.resolve(options = {})
51
- new(options).resolve
51
+ def self.resolve(**args)
52
+ new(**args).resolve
52
53
  end
53
54
 
54
55
  protected
@@ -5,7 +5,7 @@ require 'base64'
5
5
  module Chamber
6
6
  module EncryptionMethods
7
7
  class Ssl
8
- BASE64_STRING_PATTERN = %r{[A-Za-z0-9\+\/#]*\={0,2}}.freeze
8
+ BASE64_STRING_PATTERN = %r{[A-Za-z0-9+/#]*={0,2}}.freeze
9
9
  LARGE_DATA_STRING_PATTERN = /
10
10
  \A
11
11
  (#{BASE64_STRING_PATTERN})
@@ -16,12 +16,12 @@ class Ssl
16
16
  \z
17
17
  /x.freeze
18
18
 
19
- def self.encrypt(_key, value, encryption_keys)
20
- value = YAML.dump(value)
21
- cipher = OpenSSL::Cipher.new('AES-128-CBC')
19
+ def self.encrypt(_key, value, encryption_keys) # rubocop:disable Metrics/AbcSize
20
+ value = YAML.dump(value)
21
+ cipher = OpenSSL::Cipher.new('AES-128-CBC')
22
22
  cipher.encrypt
23
23
  symmetric_key = cipher.random_key
24
- iv = cipher.random_iv
24
+ iv = cipher.random_iv
25
25
 
26
26
  # encrypt all data with this key and iv
27
27
  encrypted_data = cipher.update(value) + cipher.final
@@ -35,24 +35,24 @@ class Ssl
35
35
  Base64.strict_encode64(encrypted_data)
36
36
  end
37
37
 
38
- def self.decrypt(key, value, decryption_keys)
38
+ def self.decrypt(key, value, decryption_keys) # rubocop:disable Metrics/AbcSize
39
39
  if decryption_keys.nil?
40
40
  value
41
41
  else
42
- key, iv, decoded_string = value.
43
- match(LARGE_DATA_STRING_PATTERN).
44
- captures.
45
- map do |part|
42
+ key, iv, decoded_string = value
43
+ .match(LARGE_DATA_STRING_PATTERN)
44
+ .captures
45
+ .map do |part|
46
46
  Base64.strict_decode64(part)
47
47
  end
48
- key = decryption_keys.private_decrypt(key)
48
+ key = decryption_keys.private_decrypt(key)
49
49
 
50
50
  cipher_dec = OpenSSL::Cipher.new('AES-128-CBC')
51
51
 
52
52
  cipher_dec.decrypt
53
53
 
54
54
  cipher_dec.key = key
55
- cipher_dec.iv = iv
55
+ cipher_dec.iv = iv
56
56
 
57
57
  begin
58
58
  unencrypted_value = cipher_dec.update(decoded_string) + cipher_dec.final
@@ -13,7 +13,8 @@ module Chamber
13
13
  class File < Pathname
14
14
  attr_accessor :namespaces,
15
15
  :decryption_keys,
16
- :encryption_keys
16
+ :encryption_keys,
17
+ :signature_name
17
18
 
18
19
  ###
19
20
  # Internal: Creates a settings file representing a path to a file on the
@@ -42,12 +43,13 @@ class File < Pathname
42
43
  # Chamber::File.new path: '/tmp/settings.yml'
43
44
  # # => <Chamber::File>
44
45
  #
45
- def initialize(options = {})
46
- self.namespaces = options[:namespaces] || {}
47
- self.decryption_keys = options[:decryption_keys] || {}
48
- self.encryption_keys = options[:encryption_keys] || {}
46
+ def initialize(path:, namespaces: {}, decryption_keys: {}, encryption_keys: {}, signature_name: nil)
47
+ self.namespaces = namespaces
48
+ self.decryption_keys = decryption_keys
49
+ self.encryption_keys = encryption_keys
50
+ self.signature_name = signature_name
49
51
 
50
- super options.fetch(:path)
52
+ super path
51
53
  end
52
54
 
53
55
  ###
@@ -76,7 +78,7 @@ class File < Pathname
76
78
  encryption_keys: encryption_keys)
77
79
  end
78
80
 
79
- # rubocop:disable Metrics/LineLength
81
+ # rubocop:disable Layout/LineLength, Metrics/AbcSize
80
82
  def secure
81
83
  insecure_settings = to_settings.insecure.to_flattened_name_hash
82
84
  secure_settings = to_settings.insecure.secure.to_flattened_name_hash
@@ -88,14 +90,14 @@ class File < Pathname
88
90
  escaped_name = Regexp.escape(name_pieces.last)
89
91
  escaped_value = Regexp.escape(value)
90
92
 
91
- file_contents.
92
- sub!(
93
+ file_contents
94
+ .sub!(
93
95
  /^(\s*)#{secure_prefix_pattern}#{escaped_name}(\s*):(\s*)['"]?#{escaped_value}['"]?$/,
94
96
  "\\1#{secure_prefix}#{name_pieces.last}\\2:\\3#{secure_value}",
95
97
  )
96
98
 
97
- file_contents.
98
- sub!(
99
+ file_contents
100
+ .sub!(
99
101
  /^(\s*)#{secure_prefix_pattern}#{escaped_name}(\s*):(\s*)\|((?:\n\1\s{2}.*)+)/,
100
102
  "\\1#{secure_prefix}#{name_pieces.last}\\2:\\3#{secure_value}",
101
103
  )
@@ -103,7 +105,7 @@ class File < Pathname
103
105
 
104
106
  write(file_contents)
105
107
  end
106
- # rubocop:enable Metrics/LineLength
108
+ # rubocop:enable Layout/LineLength, Metrics/AbcSize
107
109
 
108
110
  def sign
109
111
  signature_key_contents = decryption_keys[:signature]
@@ -111,7 +113,7 @@ class File < Pathname
111
113
  fail ArgumentError, 'You asked to sign your settings files but no signature key was found. Run `chamber init --signature` to generate one.' \
112
114
  unless signature_key_contents
113
115
 
114
- signature = Files::Signature.new(to_s, read, signature_key_contents)
116
+ signature = Files::Signature.new(to_s, read, signature_key_contents, signature_name)
115
117
 
116
118
  signature.write
117
119
  end
@@ -122,7 +124,7 @@ class File < Pathname
122
124
  fail ArgumentError, 'You asked to verify your settings files but no signature key was found. Run `chamber init --signature` to generate one.' \
123
125
  unless signature_key_contents
124
126
 
125
- signature = Files::Signature.new(to_s, read, signature_key_contents)
127
+ signature = Files::Signature.new(to_s, read, signature_key_contents, signature_name)
126
128
 
127
129
  signature.verify
128
130
  end
@@ -114,17 +114,26 @@ module Chamber
114
114
  class FileSet
115
115
  attr_accessor :decryption_keys,
116
116
  :encryption_keys,
117
- :basepath
117
+ :basepath,
118
+ :signature_name
118
119
  attr_reader :namespaces,
119
120
  :paths
120
121
 
121
- def initialize(options = {})
122
- self.namespaces = options[:namespaces] || {}
123
- self.decryption_keys = options[:decryption_keys]
124
- self.encryption_keys = options[:encryption_keys]
125
- self.paths = options.fetch(:files)
126
- self.basepath = options[:basepath]
122
+ # rubocop:disable Metrics/ParameterLists
123
+ def initialize(files:,
124
+ basepath: nil,
125
+ decryption_keys: nil,
126
+ encryption_keys: nil,
127
+ namespaces: {},
128
+ signature_name: nil)
129
+ self.basepath = basepath
130
+ self.decryption_keys = decryption_keys
131
+ self.encryption_keys = encryption_keys
132
+ self.namespaces = namespaces
133
+ self.paths = files
134
+ self.signature_name = signature_name
127
135
  end
136
+ # rubocop:enable Metrics/ParameterLists
128
137
 
129
138
  ###
130
139
  # Internal: Returns an Array of the ordered list of files that was processed
@@ -234,7 +243,8 @@ class FileSet
234
243
  File.new(path: file,
235
244
  namespaces: namespaces,
236
245
  decryption_keys: decryption_keys,
237
- encryption_keys: encryption_keys)
246
+ encryption_keys: encryption_keys,
247
+ signature_name: signature_name)
238
248
  end
239
249
 
240
250
  sorted_relevant_files += relevant_glob_files
@@ -8,9 +8,9 @@ module Chamber
8
8
  module Files
9
9
  class Signature
10
10
  SIGNATURE_HEADER = '-----BEGIN CHAMBER SIGNATURE-----'
11
- SIGNATURE_HEADER_PATTERN = /\-\-\-\-\-BEGIN\sCHAMBER\sSIGNATURE\-\-\-\-\-/.freeze
11
+ SIGNATURE_HEADER_PATTERN = /-----BEGIN\sCHAMBER\sSIGNATURE-----/.freeze
12
12
  SIGNATURE_FOOTER = '-----END CHAMBER SIGNATURE-----'
13
- SIGNATURE_FOOTER_PATTERN = /\-\-\-\-\-END\sCHAMBER\sSIGNATURE\-\-\-\-\-/.freeze
13
+ SIGNATURE_FOOTER_PATTERN = /-----END\sCHAMBER\sSIGNATURE-----/.freeze
14
14
  SIGNATURE_IN_FILE_PATTERN = /
15
15
  #{SIGNATURE_HEADER_PATTERN}\n # Header
16
16
  (.*)\n # Signature Body
@@ -18,14 +18,16 @@ class Signature
18
18
  /x.freeze
19
19
 
20
20
  attr_accessor :settings_content,
21
- :settings_filename
21
+ :settings_filename,
22
+ :signature_name
22
23
 
23
24
  attr_reader :signature_key
24
25
 
25
- def initialize(settings_filename, settings_content, signature_key)
26
+ def initialize(settings_filename, settings_content, signature_key, signature_name)
26
27
  self.signature_key = signature_key
27
28
  self.settings_content = settings_content
28
29
  self.settings_filename = Pathname.new(settings_filename)
30
+ self.signature_name = signature_name
29
31
  end
30
32
 
31
33
  def signature_key=(keyish)
@@ -41,7 +43,7 @@ class Signature
41
43
 
42
44
  def write
43
45
  signature_filename.write(<<-HEREDOC, 0, mode: 'w+')
44
- Signed By: #{`git config --get 'user.name'`.chomp}
46
+ Signed By: #{signature_name}
45
47
  Signed At: #{Time.now.utc.iso8601}
46
48
 
47
49
  #{SIGNATURE_HEADER}
@@ -61,20 +63,20 @@ Signed At: #{Time.now.utc.iso8601}
61
63
  end
62
64
 
63
65
  def raw_signature
64
- @raw_signature ||= signature_key.
65
- sign(digest, settings_content)
66
+ @raw_signature ||= signature_key
67
+ .sign(digest, settings_content)
66
68
  end
67
69
 
68
70
  def signature_filename
69
- @signature_filename ||= settings_filename.
70
- sub('.yml', '.sig').
71
- sub('.erb', '')
71
+ @signature_filename ||= settings_filename
72
+ .sub('.yml', '.sig')
73
+ .sub('.erb', '')
72
74
  end
73
75
 
74
76
  def encoded_signature_content
75
- @encoded_signature_content ||= signature_filename.
76
- read.
77
- match(SIGNATURE_IN_FILE_PATTERN) do |match|
77
+ @encoded_signature_content ||= signature_filename
78
+ .read
79
+ .match(SIGNATURE_IN_FILE_PATTERN) do |match|
78
80
  match[1]
79
81
  end
80
82
  end
@@ -84,7 +86,7 @@ Signed At: #{Time.now.utc.iso8601}
84
86
  end
85
87
 
86
88
  def digest
87
- @digest ||= OpenSSL::Digest::SHA512.new
89
+ @digest ||= OpenSSL::Digest.new('SHA512')
88
90
  end
89
91
  end
90
92
  end
@@ -12,19 +12,19 @@ require 'chamber/errors/decryption_failure'
12
12
  module Chamber
13
13
  module Filters
14
14
  class DecryptionFilter
15
- BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9\+/]{342}==\z}.freeze
15
+ BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9+/]{342}==\z}.freeze
16
16
  LARGE_DATA_STRING_PATTERN = %r{
17
17
  \A # Beginning of String
18
18
  (
19
- [A-Za-z0-9\+\/#]*\={0,2} # Base64 Encoded Key
19
+ [A-Za-z0-9+/#]*={0,2} # Base64 Encoded Key
20
20
  )
21
21
  \# # Separator
22
22
  (
23
- [A-Za-z0-9\+\/#]*\={0,2} # Base64 Encoded IV
23
+ [A-Za-z0-9+/#]*={0,2} # Base64 Encoded IV
24
24
  )
25
25
  \# # Separator
26
26
  (
27
- [A-Za-z0-9\+\/#]*\={0,2} # Base64 Encoded Data
27
+ [A-Za-z0-9+/#]*={0,2} # Base64 Encoded Data
28
28
  )
29
29
  \z # End of String
30
30
  }x.freeze
@@ -33,14 +33,14 @@ class DecryptionFilter
33
33
  :secure_key_token
34
34
  attr_reader :decryption_keys
35
35
 
36
- def initialize(options = {})
37
- self.decryption_keys = options.fetch(:decryption_keys, {}) || {}
38
- self.data = options.fetch(:data).dup
39
- self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
36
+ def initialize(data:, secure_key_prefix:, decryption_keys: {}, **_args)
37
+ self.decryption_keys = decryption_keys || {}
38
+ self.data = data.dup
39
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
40
40
  end
41
41
 
42
- def self.execute(options = {})
43
- new(options).__send__(:execute)
42
+ def self.execute(**args)
43
+ new(**args).__send__(:execute)
44
44
  end
45
45
 
46
46
  protected
@@ -75,6 +75,7 @@ class DecryptionFilter
75
75
 
76
76
  private
77
77
 
78
+ # rubocop:disable Style/RedundantBegin
78
79
  def decrypt(key, value)
79
80
  method = decryption_method(value)
80
81
 
@@ -88,6 +89,7 @@ class DecryptionFilter
88
89
 
89
90
  value
90
91
  end
92
+ # rubocop:enable Style/RedundantBegin
91
93
 
92
94
  def decryption_method(value)
93
95
  if value.respond_to?(:match)