chamber 2.4.0 → 2.7.1

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -930
  3. data/Rakefile +6 -0
  4. data/lib/chamber.rb +11 -7
  5. data/lib/chamber/binary/heroku.rb +45 -25
  6. data/lib/chamber/binary/runner.rb +82 -44
  7. data/lib/chamber/binary/travis.rb +14 -8
  8. data/lib/chamber/commands/base.rb +1 -2
  9. data/lib/chamber/commands/comparable.rb +0 -1
  10. data/lib/chamber/commands/compare.rb +1 -1
  11. data/lib/chamber/commands/files.rb +0 -1
  12. data/lib/chamber/commands/heroku.rb +2 -3
  13. data/lib/chamber/commands/heroku/push.rb +1 -1
  14. data/lib/chamber/commands/initialize.rb +69 -12
  15. data/lib/chamber/commands/securable.rb +9 -4
  16. data/lib/chamber/commands/secure.rb +1 -1
  17. data/lib/chamber/commands/show.rb +20 -4
  18. data/lib/chamber/commands/travis.rb +0 -1
  19. data/lib/chamber/configuration.rb +5 -5
  20. data/lib/chamber/context_resolver.rb +12 -12
  21. data/lib/chamber/decryption_key.rb +51 -0
  22. data/lib/chamber/environmentable.rb +4 -1
  23. data/lib/chamber/errors/decryption_failure.rb +6 -0
  24. data/lib/chamber/file.rb +7 -8
  25. data/lib/chamber/file_set.rb +23 -22
  26. data/lib/chamber/filters/boolean_conversion_filter.rb +1 -2
  27. data/lib/chamber/filters/decryption_filter.rb +42 -25
  28. data/lib/chamber/filters/encryption_filter.rb +7 -5
  29. data/lib/chamber/filters/environment_filter.rb +7 -7
  30. data/lib/chamber/filters/failed_decryption_filter.rb +41 -0
  31. data/lib/chamber/filters/namespace_filter.rb +1 -1
  32. data/lib/chamber/filters/secure_filter.rb +3 -5
  33. data/lib/chamber/filters/translate_secure_keys_filter.rb +5 -24
  34. data/lib/chamber/namespace_set.rb +6 -6
  35. data/lib/chamber/rails.rb +1 -3
  36. data/lib/chamber/rails/railtie.rb +6 -3
  37. data/lib/chamber/settings.rb +34 -32
  38. data/lib/chamber/version.rb +1 -1
  39. data/spec/fixtures/settings.yml +1 -0
  40. data/spec/lib/chamber/commands/files_spec.rb +4 -2
  41. data/spec/lib/chamber/commands/secure_spec.rb +8 -5
  42. data/spec/lib/chamber/commands/show_spec.rb +18 -3
  43. data/spec/lib/chamber/context_resolver_spec.rb +38 -18
  44. data/spec/lib/chamber/file_set_spec.rb +73 -52
  45. data/spec/lib/chamber/file_spec.rb +37 -23
  46. data/spec/lib/chamber/filters/boolean_conversion_filter_spec.rb +35 -33
  47. data/spec/lib/chamber/filters/decryption_filter_spec.rb +142 -21
  48. data/spec/lib/chamber/filters/encryption_filter_spec.rb +51 -19
  49. data/spec/lib/chamber/filters/environment_filter_spec.rb +12 -6
  50. data/spec/lib/chamber/filters/failed_decryption_filter_spec.rb +53 -0
  51. data/spec/lib/chamber/filters/insecure_filter_spec.rb +38 -18
  52. data/spec/lib/chamber/filters/namespace_filter_spec.rb +38 -38
  53. data/spec/lib/chamber/filters/secure_filter_spec.rb +10 -10
  54. data/spec/lib/chamber/filters/translate_secure_keys_filter_spec.rb +9 -6
  55. data/spec/lib/chamber/namespace_set_spec.rb +7 -5
  56. data/spec/lib/chamber/settings_spec.rb +168 -79
  57. data/spec/lib/chamber_spec.rb +72 -71
  58. metadata +22 -21
  59. data/lib/chamber/errors/undecryptable_value_error.rb +0 -6
  60. data/templates/settings.yml +0 -14
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -3,8 +3,6 @@ require 'chamber/instance'
3
3
  require 'chamber/rails'
4
4
 
5
5
  module Chamber
6
- extend self
7
-
8
6
  def load(options = {})
9
7
  self.instance = Instance.new(options)
10
8
  end
@@ -19,12 +17,10 @@ module Chamber
19
17
 
20
18
  protected
21
19
 
22
- def instance
23
- @@instance ||= Instance.new({})
24
- end
20
+ attr_accessor :instance
25
21
 
26
- def instance=(new_instance)
27
- @@instance = new_instance
22
+ def instance
23
+ @instance ||= Instance.new({})
28
24
  end
29
25
 
30
26
  public
@@ -38,4 +34,12 @@ module Chamber
38
34
  def respond_to_missing?(name, include_private = false)
39
35
  instance.respond_to?(name, include_private)
40
36
  end
37
+
38
+ module_function :load,
39
+ :to_s,
40
+ :env,
41
+ :instance,
42
+ :instance=,
43
+ :method_missing,
44
+ :respond_to_missing?
41
45
  end
@@ -7,50 +7,70 @@ require 'chamber/commands/heroku/compare'
7
7
  module Chamber
8
8
  module Binary
9
9
  class Heroku < Thor
10
-
11
10
  class_option :app,
12
- type: :string,
13
- aliases: '-a',
14
- required: true,
15
- desc: 'The name of the Heroku application whose config values will be affected'
11
+ type: :string,
12
+ aliases: '-a',
13
+ required: true,
14
+ desc: 'The name of the Heroku application whose config values will ' \
15
+ 'be affected'
16
+
17
+ desc 'clear', 'Removes all Heroku environment variables which match settings that ' \
18
+ 'Chamber knows about'
16
19
 
17
- desc 'clear', 'Removes all Heroku environment variables which match settings that Chamber knows about'
18
20
  method_option :dry_run,
19
- type: :boolean,
20
- aliases: '-d',
21
- desc: 'Does not actually remove anything, but instead displays what would change if cleared'
21
+ type: :boolean,
22
+ aliases: '-d',
23
+ desc: 'Does not actually remove anything, but instead displays what ' \
24
+ 'would change if cleared'
25
+
22
26
  def clear
23
27
  Commands::Heroku::Clear.call(options)
24
28
  end
25
29
 
26
- desc 'push', 'Sends settings to Heroku so that they may be used in the application once it is deployed'
30
+ desc 'push', 'Sends settings to Heroku so that they may be used in the application ' \
31
+ 'once it is deployed'
32
+
27
33
  method_option :dry_run,
28
- type: :boolean,
29
- aliases: '-d',
30
- desc: 'Does not actually push anything to Heroku, but instead displays what would change if pushed'
34
+ type: :boolean,
35
+ aliases: '-d',
36
+ desc: 'Does not actually push anything to Heroku, but instead ' \
37
+ 'displays what would change if pushed'
38
+
31
39
  method_option :only_sensitive,
32
- type: :boolean,
33
- aliases: '-o',
34
- default: true,
35
- desc: 'When enabled, only settings contained in files which have been gitignored or settings which are marked as "_secure" will be pushed'
40
+ type: :boolean,
41
+ aliases: '-o',
42
+ default: true,
43
+ desc: 'When enabled, only settings contained in files which have ' \
44
+ 'been gitignored or settings which are marked as "_secure" ' \
45
+ 'will be pushed'
46
+
36
47
  def push
37
48
  Commands::Heroku::Push.call(options)
38
49
  end
39
50
 
40
- desc 'pull', 'Retrieves the environment variables for the application and stores them in a temporary file'
51
+ desc 'pull', 'Retrieves the environment variables for the application and stores ' \
52
+ 'them in a temporary file'
53
+
41
54
  method_option :into,
42
- type: :string,
43
- desc: 'The file into which the Heroku config information should be stored. This file WILL BE OVERRIDDEN.'
55
+ type: :string,
56
+ desc: 'The file into which the Heroku config information should be ' \
57
+ 'stored. This file WILL BE OVERRIDDEN.'
58
+
44
59
  def pull
45
60
  puts Commands::Heroku::Pull.call(options)
46
61
  end
47
62
 
48
- desc 'compare', 'Displays the difference between what is currently stored in the Heroku application\'s config and what Chamber knows about locally'
63
+ desc 'compare', 'Displays the difference between what is currently stored in the ' \
64
+ 'Heroku application\'s config and what Chamber knows about locally'
65
+
49
66
  method_option :only_sensitive,
50
- type: :boolean,
51
- aliases: '-o',
52
- default: true,
53
- desc: 'When enabled, the diff will only consider settings contained in files which have been gitignored or settings which are marked as "_secure"'
67
+ type: :boolean,
68
+ aliases: '-o',
69
+ default: true,
70
+ desc: 'When enabled, the diff will only consider settings ' \
71
+ 'contained in files which have been gitignored or settings ' \
72
+ 'which are marked as "_secure"'
73
+
54
74
  def compare
55
75
  Commands::Heroku::Compare.call(options)
56
76
  end
@@ -16,49 +16,72 @@ class Runner < Thor
16
16
  source_root ::File.expand_path('../../../../templates', __FILE__)
17
17
 
18
18
  class_option :rootpath,
19
- type: :string,
20
- aliases: '-r',
21
- default: ENV['PWD'],
22
- desc: 'The root filepath of the application'
19
+ type: :string,
20
+ aliases: '-r',
21
+ default: ENV['PWD'],
22
+ desc: 'The root filepath of the application'
23
+
23
24
  class_option :basepath,
24
- type: :string,
25
- aliases: '-b',
26
- desc: 'The base filepath where Chamber will look for the conventional settings files'
25
+ type: :string,
26
+ aliases: '-b',
27
+ desc: 'The base filepath where Chamber will look for the ' \
28
+ 'conventional settings files'
29
+
27
30
  class_option :files,
28
- type: :array,
29
- aliases: '-f',
30
- desc: 'The set of file globs that Chamber will use for processing'
31
+ type: :array,
32
+ aliases: '-f',
33
+ desc: 'The set of file globs that Chamber will use for processing'
34
+
31
35
  class_option :namespaces,
32
- type: :array,
33
- aliases: '-n',
34
- default: [],
35
- desc: 'The set of namespaces that Chamber will use for processing'
36
+ type: :array,
37
+ aliases: '-n',
38
+ default: [],
39
+ desc: 'The set of namespaces that Chamber will use for processing'
40
+
36
41
  class_option :preset,
37
- type: :string,
38
- aliases: '-p',
39
- enum: ['rails'],
40
- desc: 'Used to quickly assign a given scenario to the chamber command (eg Rails apps)'
42
+ type: :string,
43
+ aliases: '-p',
44
+ enum: %w{rails},
45
+ desc: 'Used to quickly assign a given scenario to the chamber ' \
46
+ 'command (eg Rails apps)'
47
+
41
48
  class_option :decryption_key,
42
- type: :string,
43
- desc: 'The path to or contents of the private key associated with the project (typically .chamber.pem)'
49
+ type: :string,
50
+ desc: 'The path to or contents of the private key associated with ' \
51
+ 'the project (typically .chamber.pem)'
52
+
44
53
  class_option :encryption_key,
45
- type: :string,
46
- desc: 'The path to or contents of the public key associated with the project (typically .chamber.pub.pem)'
54
+ type: :string,
55
+ desc: 'The path to or contents of the public key associated with ' \
56
+ 'the project (typically .chamber.pub.pem)'
57
+
47
58
  class_option :shell,
48
- default: self.new,
49
- desc: 'The command runner. Can be overridden for specific logging capabilities.'
59
+ default: new,
60
+ desc: 'The command runner. Can be overridden for specific logging ' \
61
+ 'capabilities.'
50
62
 
51
63
  desc 'travis SUBCOMMAND ...ARGS', 'For manipulating Travis CI environment variables'
52
- subcommand 'travis', Chamber::Binary::Travis
64
+ subcommand 'travis', Chamber::Binary::Travis
53
65
 
54
66
  desc 'heroku SUBCOMMAND ...ARGS', 'For manipulating Heroku environment variables'
55
- subcommand 'heroku', Chamber::Binary::Heroku
67
+ subcommand 'heroku', Chamber::Binary::Heroku
56
68
 
57
69
  desc 'show', 'Displays the list of settings and their values'
70
+
58
71
  method_option :as_env,
59
- type: :boolean,
60
- aliases: '-e',
61
- desc: 'Whether the displayed settings should be environment variable compatible'
72
+ type: :boolean,
73
+ aliases: '-e',
74
+ desc: 'Whether the displayed settings should be environment ' \
75
+ 'variable compatible'
76
+
77
+ desc 'only_sensitive', 'Only show secured/securable settings'
78
+
79
+ method_option :only_sensitive,
80
+ type: :boolean,
81
+ aliases: '-s',
82
+ desc: 'Only displays the settings that are/should be secured. ' \
83
+ 'Useful for debugging.'
84
+
62
85
  def show
63
86
  puts Commands::Show.call(options)
64
87
  end
@@ -68,34 +91,49 @@ class Runner < Thor
68
91
  puts Commands::Files.call(options)
69
92
  end
70
93
 
71
- desc 'compare', 'Displays the difference between what is currently stored in the Heroku application\'s config and what Chamber knows about locally'
94
+ desc 'compare', 'Displays the difference between what is currently stored in the ' \
95
+ 'Heroku application\'s config and what Chamber knows about locally'
96
+
72
97
  method_option :keys_only,
73
- type: :boolean,
74
- default: true,
75
- desc: 'Whether or not to only compare the keys but not the values of the two sets of settings'
98
+ type: :boolean,
99
+ default: true,
100
+ desc: 'Whether or not to only compare the keys but not the values ' \
101
+ 'of the two sets of settings'
102
+
76
103
  method_option :first,
77
- type: :array,
78
- desc: 'The list of namespaces which will be used as the source of the comparison'
104
+ type: :array,
105
+ desc: 'The list of namespaces which will be used as the source of ' \
106
+ 'the comparison'
107
+
79
108
  method_option :second,
80
- type: :array,
81
- desc: 'The list of namespaces which will be used as the destination of the comparison'
109
+ type: :array,
110
+ desc: 'The list of namespaces which will be used as the destination ' \
111
+ 'of the comparison'
112
+
82
113
  def compare
83
114
  Commands::Compare.call(options)
84
115
  end
85
116
 
86
- desc 'secure', 'Secures any values which appear to need to be encrypted in any of the settings files which match irrespective of namespaces'
117
+ desc 'secure', 'Secures any values which appear to need to be encrypted in any of ' \
118
+ 'the settings files which match irrespective of namespaces'
119
+
87
120
  method_option :only_sensitive,
88
- type: :boolean,
89
- default: true
121
+ type: :boolean,
122
+ default: true
123
+
90
124
  method_option :dry_run,
91
- type: :boolean,
92
- aliases: '-d',
93
- desc: 'Does not actually encrypt anything, but instead displays what values would be encrypted'
125
+ type: :boolean,
126
+ aliases: '-d',
127
+ desc: 'Does not actually encrypt anything, but instead displays ' \
128
+ 'what values would be encrypted'
129
+
94
130
  def secure
95
131
  Commands::Secure.call(options)
96
132
  end
97
133
 
98
- desc 'init', 'Sets Chamber up matching best practices for secure configuration management'
134
+ desc 'init', 'Sets Chamber up matching best practices for secure configuration ' \
135
+ 'management'
136
+
99
137
  def init
100
138
  Commands::Initialize.call(options)
101
139
  end
@@ -4,17 +4,23 @@ require 'chamber/commands/travis/secure'
4
4
  module Chamber
5
5
  module Binary
6
6
  class Travis < Thor
7
+ desc 'secure', 'Uses your Travis CI public key to encrypt the settings you have ' \
8
+ 'chosen not to commit to the repo'
7
9
 
8
- desc 'secure', 'Uses your Travis CI public key to encrypt the settings you have chosen not to commit to the repo'
9
10
  method_option :dry_run,
10
- type: :boolean,
11
- aliases: '-d',
12
- desc: 'Does not actually encrypt anything to .travis.yml, but instead displays what values would be encrypted'
11
+ type: :boolean,
12
+ aliases: '-d',
13
+ desc: 'Does not actually encrypt anything to .travis.yml, but ' \
14
+ 'instead displays what values would be encrypted'
15
+
13
16
  method_option :only_sensitive,
14
- type: :boolean,
15
- aliases: '-o',
16
- default: true,
17
- desc: 'Does not encrypt settings into .travis.yml unless they are contained in files which have been gitignored or settings which are marked as "_secure"'
17
+ type: :boolean,
18
+ aliases: '-o',
19
+ default: true,
20
+ desc: 'Does not encrypt settings into .travis.yml unless they are ' \
21
+ 'contained in files which have been gitignored or settings ' \
22
+ 'which are marked as "_secure"'
23
+
18
24
  def secure
19
25
  Commands::Travis::Secure.call(options)
20
26
  end
@@ -4,7 +4,6 @@ require 'chamber/instance'
4
4
  module Chamber
5
5
  module Commands
6
6
  class Base
7
-
8
7
  def initialize(options = {})
9
8
  self.chamber = Chamber::Instance.new options
10
9
  self.shell = options[:shell]
@@ -13,7 +12,7 @@ class Base
13
12
  end
14
13
 
15
14
  def self.call(options = {})
16
- self.new(options).call
15
+ new(options).call
17
16
  end
18
17
 
19
18
  protected
@@ -3,7 +3,6 @@ require 'tempfile'
3
3
  module Chamber
4
4
  module Commands
5
5
  module Comparable
6
-
7
6
  def initialize(options = {})
8
7
  super
9
8
 
@@ -18,7 +18,7 @@ class Compare < Chamber::Commands::Base
18
18
  end
19
19
 
20
20
  def self.call(options = {})
21
- self.new(options).call
21
+ new(options).call
22
22
  end
23
23
 
24
24
  protected
@@ -3,7 +3,6 @@ require 'chamber/commands/base'
3
3
  module Chamber
4
4
  module Commands
5
5
  class Files < Chamber::Commands::Base
6
-
7
6
  def call
8
7
  chamber.filenames
9
8
  end
@@ -3,7 +3,6 @@ require 'bundler'
3
3
  module Chamber
4
4
  module Commands
5
5
  module Heroku
6
-
7
6
  def initialize(options = {})
8
7
  super
9
8
 
@@ -15,7 +14,7 @@ module Heroku
15
14
  attr_accessor :app
16
15
 
17
16
  def configuration
18
- @configuration ||= heroku("config --shell").chomp
17
+ @configuration ||= heroku('config --shell').chomp
19
18
  end
20
19
 
21
20
  def heroku(command)
@@ -23,7 +22,7 @@ module Heroku
23
22
  end
24
23
 
25
24
  def app_option
26
- app ? " --app #{app}" : ""
25
+ app ? " --app #{app}" : ''
27
26
  end
28
27
  end
29
28
  end
@@ -15,7 +15,7 @@ class Push < Chamber::Commands::Base
15
15
  shell.say_status 'push', key, :blue
16
16
  else
17
17
  shell.say_status 'push', key, :green
18
- shell.say heroku(%Q{config:set #{key}=#{value}})
18
+ heroku("config:set #{key}=#{value}")
19
19
  end
20
20
  end
21
21
  end
@@ -6,29 +6,56 @@ require 'chamber/commands/base'
6
6
  module Chamber
7
7
  module Commands
8
8
  class Initialize < Chamber::Commands::Base
9
-
10
9
  def initialize(options = {})
11
10
  super
12
11
 
13
12
  self.basepath = Chamber.configuration.basepath
14
13
  end
15
14
 
15
+ # rubocop:disable Metrics/LineLength, Metrics/MethodLength
16
16
  def call
17
- shell.create_file private_key_filepath, rsa_private_key.to_pem
18
- shell.create_file public_key_filepath, rsa_public_key.to_pem
17
+ shell.create_file private_key_filepath, rsa_private_key.to_pem
18
+ shell.create_file protected_key_filepath, rsa_protected_key
19
+ shell.create_file public_key_filepath, rsa_public_key.to_pem
19
20
 
20
21
  `chmod 600 #{private_key_filepath}`
22
+ `chmod 600 #{protected_key_filepath}`
21
23
  `chmod 644 #{public_key_filepath}`
22
24
 
23
25
  unless ::File.read(gitignore_filepath).match(/^.chamber.pem$/)
24
- shell.append_to_file gitignore_filepath, private_key_filepath.basename.to_s
26
+ shell.append_to_file gitignore_filepath, private_key_filename
27
+ shell.append_to_file gitignore_filepath, protected_key_filename
25
28
  end
26
29
 
27
30
  shell.copy_file settings_template_filepath, settings_filepath
31
+
32
+ shell.say ''
33
+ shell.say 'The passphrase for your encrypted private key is:', :green
34
+ shell.say ''
35
+ shell.say rsa_key_passphrase, :green
36
+ shell.say ''
37
+ shell.say 'Store this securely somewhere.', :green
38
+ shell.say ''
39
+ shell.say 'You can send them the file located at:', :green
40
+ shell.say ''
41
+ shell.say protected_key_filepath, :green
42
+ shell.say ''
43
+ shell.say 'and not have to worry about sending it via a secure medium (such as', :green
44
+ shell.say 'email), however do not send the passphrase along with it. Give it to'
45
+ shell.say 'your team members in person.', :green
46
+ shell.say ''
47
+ shell.say 'In order for them to decrypt it (for use with Chamber), they can run:'
48
+ shell.say ''
49
+ shell.say "$ cp /path/to/{#{protected_key_filename},#{private_key_filename}}", :green
50
+ shell.say "$ ssh-keygen -p -f /path/to/#{private_key_filename}", :green
51
+ shell.say ''
52
+ shell.say 'Enter the passphrase when prompted and leave the new passphrase blank.', :green
53
+ shell.say ''
28
54
  end
55
+ # rubocop:enable Metrics/LineLength, Metrics/MethodLength
29
56
 
30
57
  def self.call(options = {})
31
- self.new(options).call
58
+ new(options).call
32
59
  end
33
60
 
34
61
  protected
@@ -40,7 +67,12 @@ class Initialize < Chamber::Commands::Base
40
67
  end
41
68
 
42
69
  def templates_path
43
- @templates_path ||= Pathname.new(::File.expand_path('../../../../templates', __FILE__))
70
+ @templates_path ||= gem_path + 'templates'
71
+ end
72
+
73
+ def gem_path
74
+ @gem_path ||= Pathname.new(
75
+ ::File.expand_path('../../../..', __FILE__))
44
76
  end
45
77
 
46
78
  def settings_filepath
@@ -51,24 +83,49 @@ class Initialize < Chamber::Commands::Base
51
83
  @gitignore_filepath ||= rootpath + '.gitignore'
52
84
  end
53
85
 
86
+ def protected_key_filepath
87
+ @protected_key_filepath ||= rootpath + protected_key_filename
88
+ end
89
+
54
90
  def private_key_filepath
55
- @private_key_filepath ||= rootpath + '.chamber.pem'
91
+ @private_key_filepath ||= rootpath + private_key_filename
56
92
  end
57
93
 
58
94
  def public_key_filepath
59
- @public_key_filepath ||= rootpath + '.chamber.pub.pem'
95
+ @public_key_filepath ||= rootpath + public_key_filename
96
+ end
97
+
98
+ def protected_key_filename
99
+ '.chamber.pem.enc'
60
100
  end
61
101
 
62
- def rsa_key
63
- @rsa_key ||= OpenSSL::PKey::RSA.new(2048)
102
+ def private_key_filename
103
+ '.chamber.pem'
104
+ end
105
+
106
+ def public_key_filename
107
+ '.chamber.pub.pem'
108
+ end
109
+
110
+ def rsa_protected_key
111
+ @rsa_protected_key ||= begin
112
+ cipher = OpenSSL::Cipher.new 'AES-128-CBC'
113
+ key = OpenSSL::PKey::RSA.new(2048)
114
+
115
+ key.export cipher, rsa_key_passphrase
116
+ end
64
117
  end
65
118
 
66
119
  def rsa_private_key
67
- rsa_key
120
+ @rsa_private_key ||= OpenSSL::PKey::RSA.new(2048)
68
121
  end
69
122
 
70
123
  def rsa_public_key
71
- rsa_key.public_key
124
+ rsa_private_key.public_key
125
+ end
126
+
127
+ def rsa_key_passphrase
128
+ @rsa_key_passphrase ||= SecureRandom.uuid
72
129
  end
73
130
  end
74
131
  end