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
- #write_file(contents) ⇒ Object
Constructor Details
#initialize(stage) ⇒ SecretManager
Returns a new instance of 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
148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/hippo/secret_manager.rb', line 148 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.}" end |
#create ⇒ void
This method returns an undefined value.
Create an empty encrypted example secret file
115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/hippo/secret_manager.rb', line 115 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 secret2!' }.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.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/hippo/secret_manager.rb', line 59 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
99 100 101 102 103 |
# File 'lib/hippo/secret_manager.rb', line 99 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
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/hippo/secret_manager.rb', line 25 def download_key return if @key Util.action 'Downloading secret encryiption key...' do |state| begin value = @stage.get('secret', 'hippo-secret-key').first if value.nil? || value.dig('data', 'key').nil? state.call('not found') return end @key = Base64.decode64(Base64.decode64(value['data']['key'])) rescue Hippo::Error => e if e. =~ /not found/ state.call('not found') else raise end end end end |
#edit ⇒ Object
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/hippo/secret_manager.rb', line 127 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?
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/hippo/secret_manager.rb', line 79 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.
108 109 110 |
# File 'lib/hippo/secret_manager.rb', line 108 def exists? File.file?(path) end |
#key_available? ⇒ Boolean
Is there a key availale in this manager?
51 52 53 54 |
# File 'lib/hippo/secret_manager.rb', line 51 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.config_root, 'secrets.yaml') end |
#write_file(contents) ⇒ Object
143 144 145 146 |
# File 'lib/hippo/secret_manager.rb', line 143 def write_file(contents) FileUtils.mkdir_p(File.dirname(path)) File.open(path, 'w') { |f| f.write(encrypt(contents)) } end |