Class: Hippo::SecretManager

Inherits:
Object
  • Object
show all
Defined in:
lib/hippo/secret_manager.rb

Constant Summary collapse

CIPHER =
OpenSSL::Cipher.new('aes-256-gcm')

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(recipe, stage) ⇒ SecretManager

Returns a new instance of SecretManager.



14
15
16
17
# File 'lib/hippo/secret_manager.rb', line 14

def initialize(recipe, stage)
  @recipe = recipe
  @stage = stage
end

Instance Attribute Details

#keyObject (readonly)

Returns the value of attribute key.



10
11
12
# File 'lib/hippo/secret_manager.rb', line 10

def key
  @key
end

#recipeObject (readonly)

Returns the value of attribute recipe.



8
9
10
# File 'lib/hippo/secret_manager.rb', line 8

def recipe
  @recipe
end

#stageObject (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

#create_keyvoid

This method returns an undefined value.

Generate and publish a new secret key to the Kubernetes API.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/hippo/secret_manager.rb', line 22

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
  object = {
    'apiVersion' => 'v1',
    'kind' => 'Secret',
    'type' => 'hippo.adam.ac/secret-encryption-key',
    'metadata' => { 'name' => 'hippo-secret-key', 'namespace' => @stage.namespace },
    'data' => { 'key' => Base64.encode64(secret_key64).gsub("\n", '').strip }
  }
  @recipe.kubernetes.apply_with_kubectl(object.to_yaml)
  @key = secret_key
end

#decrypt(value) ⇒ String

Decrypt the given value value and return it

Parameters:

  • value (String)

Returns:

  • (String)


88
89
90
91
92
93
94
95
96
97
# File 'lib/hippo/secret_manager.rb', line 88

def decrypt(value)
  value = value.to_s
  if value =~ /\Aencrypted:(.*)/
    value = Base64.decode64(Regexp.last_match(1))
    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
  else
    value
  end
end

#download_keyvoid

This method returns an undefined value.

Download the current key from the Kubernetes API and set it as the key for this instance



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/hippo/secret_manager.rb', line 45

def download_key
  return if @key

  value = @recipe.kubernetes.get_with_kubectl(@stage, '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.message =~ /not found/
end

#encrypt(value) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
# File 'lib/hippo/secret_manager.rb', line 72

def encrypt(value)
  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)
  'encrypted:' + Base64.encode64([
    Base64.encode64(encrypted_value),
    Base64.encode64(salt),
    Base64.encode64(iv)
  ].join('---')).gsub("\n", '')
end

#key_available?Boolean

Returns:

  • (Boolean)


57
58
59
60
# File 'lib/hippo/secret_manager.rb', line 57

def key_available?
  download_key
  !@key.nil?
end

#secret(name) ⇒ Object



62
63
64
# File 'lib/hippo/secret_manager.rb', line 62

def secret(name)
  Secret.new(self, name)
end

#secretsObject



66
67
68
69
70
# File 'lib/hippo/secret_manager.rb', line 66

def secrets
  Dir[File.join('secrets', @stage.name, '**', '*.{yml,yaml}')].map do |path|
    secret(path.split('/').last.sub(/\.ya?ml\z/, ''))
  end
end