Class: Hippo::SecretManager
- Inherits:
-
Object
- Object
- Hippo::SecretManager
- Defined in:
- lib/hippo/secret_manager.rb
Constant Summary collapse
- CIPHER =
OpenSSL::Cipher.new('aes-256-gcm')
Instance Attribute Summary collapse
-
#stage ⇒ Object
readonly
Returns the value of attribute stage.
Instance Method Summary collapse
- #all ⇒ Object
-
#create ⇒ void
Create an empty encrypted example secret file.
-
#create_key ⇒ void
Generate and publish a new secret key to the Kubernetes API.
-
#decrypt(value) ⇒ String
Decrypt the given value value and return it.
-
#download_key ⇒ void
Download the current key from the Kubernetes API and set it as the key for this instance.
- #edit ⇒ Object
-
#encrypt(value) ⇒ Object
Encrypt a given value?.
-
#exists? ⇒ Boolean
Does a secrets file exist for this application.
-
#initialize(stage) ⇒ SecretManager
constructor
A new instance of SecretManager.
-
#key_available? ⇒ Boolean
Is there a key availale in this manager?.
- #path ⇒ Object
- #secret(name) ⇒ Object
- #secrets ⇒ Object
- #write_file(contents) ⇒ Object
Constructor Details
#initialize(stage) ⇒ SecretManager
11 12 13 |
# File 'lib/hippo/secret_manager.rb', line 11 def initialize(stage) @stage = stage end |
Instance Attribute Details
#stage ⇒ Object (readonly)
Returns the value of attribute stage.
9 10 11 |
# File 'lib/hippo/secret_manager.rb', line 9 def stage @stage end |
Instance Method Details
#all ⇒ Object
147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/hippo/secret_manager.rb', line 147 def all @all ||= begin return {} unless exists? unless key_available? raise Error, 'No encryption key is available to decrypt secrets' end YAML.safe_load(decrypt(File.read(path))) end rescue Psych::SyntaxError => e raise Error, "Could not parse secrets file: #{e.message}" end |
#create ⇒ void
This method returns an undefined value.
Create an empty encrypted example secret file
114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/hippo/secret_manager.rb', line 114 def create unless key_available? raise Error, 'Cannot create secret file because no key is available for encryption' end return if exists? yaml = { 'example' => 'This is an example secret!' }.to_yaml FileUtils.mkdir_p(File.dirname(path)) File.open(path, 'w') { |f| f.write(encrypt(yaml)) } end |
#create_key ⇒ void
This method returns an undefined value.
Generate and publish a new secret key to the Kubernetes API.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/hippo/secret_manager.rb', line 58 def create_key if key_available? raise Hippo::Error, 'A key already exists on Kubernetes. Remove this first.' end CIPHER.encrypt secret_key = CIPHER.random_key secret_key64 = Base64.encode64(secret_key).gsub("\n", '').strip od = ObjectDefinition.new({ 'apiVersion' => 'v1', 'kind' => 'Secret', 'type' => 'hippo.adam.ac/secret-encryption-key', 'metadata' => { 'name' => 'hippo-secret-key' }, 'data' => { 'key' => secret_key64 } }, @stage) @stage.apply([od]) @key = secret_key end |
#decrypt(value) ⇒ String
Decrypt the given value value and return it
98 99 100 101 102 |
# File 'lib/hippo/secret_manager.rb', line 98 def decrypt(value) value = Base64.decode64(value.to_s) encrypted_value, salt, iv = value.split('---', 3).map { |s| Base64.decode64(s) } Encryptor.decrypt(value: encrypted_value, key: @key, iv: iv, salt: salt).to_s end |
#download_key ⇒ void
This method returns an undefined value.
Download the current key from the Kubernetes API and set it as the key for this instance
35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/hippo/secret_manager.rb', line 35 def download_key return if @key value = @stage.get('secret', 'hippo-secret-key').first return if value.nil? return if value.dig('data', 'key').nil? @key = Base64.decode64(Base64.decode64(value['data']['key'])) rescue Hippo::Error => e raise unless e. =~ /not found/ end |
#edit ⇒ Object
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/hippo/secret_manager.rb', line 126 def edit create unless exists? unless key_available? raise Error, 'Cannot create edit file because no key is available for decryption' end old_contents = decrypt(File.read(path)) new_contents = Util.open_in_editor('secret', old_contents) if old_contents != new_contents write_file(new_contents) else puts 'No changes detected. Not re-encrypting secret file.' end end |
#encrypt(value) ⇒ Object
Encrypt a given value?
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/hippo/secret_manager.rb', line 78 def encrypt(value) unless key_available? raise Error, 'Cannot encrypt values because there is no key' end CIPHER.encrypt iv = CIPHER.random_iv salt = SecureRandom.random_bytes(16) encrypted_value = Encryptor.encrypt(value: value.to_s, key: @key, iv: iv, salt: salt) Base64.encode64([ Base64.encode64(encrypted_value), Base64.encode64(salt), Base64.encode64(iv) ].join('---')) end |
#exists? ⇒ Boolean
Does a secrets file exist for this application.
107 108 109 |
# File 'lib/hippo/secret_manager.rb', line 107 def exists? File.file?(path) end |
#key_available? ⇒ Boolean
Is there a key availale in this manager?
50 51 52 53 |
# File 'lib/hippo/secret_manager.rb', line 50 def key_available? download_key !@key.nil? end |
#path ⇒ Object
17 18 19 |
# File 'lib/hippo/secret_manager.rb', line 17 def path File.join(@stage.manifest.root, 'secrets', @stage.name + '.yaml') end |
#secret(name) ⇒ Object
21 22 23 |
# File 'lib/hippo/secret_manager.rb', line 21 def secret(name) Secret.new(self, name) end |
#secrets ⇒ Object
25 26 27 28 29 |
# File 'lib/hippo/secret_manager.rb', line 25 def secrets Dir[File.join(root, '*.{yml,yaml}')].map do |path| secret(path.split('/').last.sub(/\.ya?ml\z/, '')) end end |
#write_file(contents) ⇒ Object
142 143 144 145 |
# File 'lib/hippo/secret_manager.rb', line 142 def write_file(contents) FileUtils.mkdir_p(File.dirname(path)) File.open(path, 'w') { |f| f.write(encrypt(contents)) } end |