Module: Legion::Crypt::Vault

Includes:
Logging::Helper
Included in:
Legion::Crypt
Defined in:
lib/legion/crypt/vault.rb,
lib/legion/crypt/vault_renewer.rb

Defined Under Namespace

Classes: Renewer

Constant Summary

Constants included from Logging::Helper

Logging::Helper::CompatLogger

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging::Helper

#handle_exception, #log

Instance Attribute Details

#sessionsObject

Returns the value of attribute sessions.



12
13
14
# File 'lib/legion/crypt/vault.rb', line 12

def sessions
  @sessions
end

Instance Method Details

#add_session(path:) ⇒ Object



106
107
108
109
# File 'lib/legion/crypt/vault.rb', line 106

def add_session(path:)
  @sessions ||= []
  @sessions.push(path)
end

#close_session(session:) ⇒ Object



129
130
131
# File 'lib/legion/crypt/vault.rb', line 129

def close_session(session:)
  ::Vault.sys.revoke(session)
end

#close_sessionsObject



111
112
113
114
115
116
117
118
119
# File 'lib/legion/crypt/vault.rb', line 111

def close_sessions
  return if @sessions.nil?

  log.info 'Closing all Legion::Crypt vault sessions'

  @sessions.each do |session|
    close_session(session: session)
  end
end

#connect_vaultObject



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/legion/crypt/vault.rb', line 18

def connect_vault
  @sessions = []
  vault_settings = Legion::Settings[:crypt][:vault]
  ::Vault.address = resolve_vault_address(vault_settings)
  ::Vault.ssl_verify = resolve_ssl_verify(vault_settings[:tls])
  namespace = vault_settings[:vault_namespace]
  log.info "Vault connection requested address=#{::Vault.address} namespace=#{namespace || 'none'} ssl_verify=#{::Vault.ssl_verify}"

  Legion::Settings[:crypt][:vault][:token] = ENV['VAULT_DEV_ROOT_TOKEN_ID'] if ENV.key? 'VAULT_DEV_ROOT_TOKEN_ID'
  return nil if Legion::Settings[:crypt][:vault][:token].nil?

  ::Vault.token = Legion::Settings[:crypt][:vault][:token]
  ::Vault.namespace = namespace if namespace
  if vault_healthy?
    Legion::Settings[:crypt][:vault][:connected] = true
    log.info "Vault connected at #{::Vault.address} (namespace=#{namespace || 'none'})"
  end
rescue StandardError => e
  log_vault_connection_error(e)
  Legion::Settings[:crypt][:vault][:connected] = false
  false
end

#delete(path, cluster_name: nil) ⇒ Object



93
94
95
96
97
98
99
100
# File 'lib/legion/crypt/vault.rb', line 93

def delete(path, cluster_name: nil)
  logical_client(cluster_name: cluster_name).delete(path)
  log.info "Vault delete complete path=#{path}"
  { success: true, path: path }
rescue StandardError => e
  handle_exception(e, level: :error, operation: 'crypt.vault.delete', path: path)
  { success: false, path: path, error: e.message }
end

#exist?(path, cluster_name: nil) ⇒ Boolean

Returns:

  • (Boolean)


102
103
104
# File 'lib/legion/crypt/vault.rb', line 102

def exist?(path, cluster_name: nil)
  !kv_client(cluster_name: cluster_name).(path).nil?
end

#get(path, cluster_name: nil) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/legion/crypt/vault.rb', line 69

def get(path, cluster_name: nil)
  log.debug "Vault kv get: path=#{path}"
  result = kv_client(cluster_name: cluster_name).read(path)
  if result.nil?
    log.debug "Vault kv get: #{path} returned nil"
    return nil
  end

  log.debug "Vault kv get: #{path} returned keys=#{result.data&.keys&.inspect}"
  result.data
rescue StandardError => e
  handle_exception(e, level: :error, operation: 'crypt.vault.get', path: path)
  raise
end

#read(path, type = nil, cluster_name: nil) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/legion/crypt/vault.rb', line 51

def read(path, type = nil, cluster_name: nil)
  full_path = type.nil? || type.empty? ? path : "#{type}/#{path}"
  log_read_context(full_path, cluster_name: cluster_name)
  lease = logical_client(cluster_name: cluster_name).read(full_path)
  if lease.nil?
    log_vault_debug("Vault read: #{full_path} returned nil")
    return nil
  end
  add_session(path: lease.lease_id) if lease.respond_to?(:lease_id) && lease.lease_id && !lease.lease_id.empty?

  data = lease.data
  log_vault_debug("Vault read: #{full_path} returned keys=#{data&.keys&.inspect}")
  unwrap_kv_v2(data, full_path)
rescue StandardError => e
  handle_exception(e, level: :error, operation: 'crypt.vault.read', path: full_path)
  raise
end

#renew_cluster_tokensObject



153
154
155
156
157
158
159
160
161
# File 'lib/legion/crypt/vault.rb', line 153

def renew_cluster_tokens
  connected_clusters.each_key do |name|
    client = vault_client(name)
    client.auth_token.renew_self
    log.info "Vault token renewed for cluster #{name}"
  rescue StandardError => e
    log_vault_error(name, e)
  end
end

#renew_session(session:) ⇒ Object



133
134
135
# File 'lib/legion/crypt/vault.rb', line 133

def renew_session(session:)
  ::Vault.sys.renew(session)
end

#renew_sessions(**_opts) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/legion/crypt/vault.rb', line 137

def renew_sessions(**_opts)
  log.debug 'Vault renewal cycle start'
  result = if respond_to?(:connected_clusters) && connected_clusters.any?
             renew_cluster_tokens
           else
             @sessions.each do |session|
               renew_session(session: session)
             end
           end
  log.debug 'Vault renewal cycle complete'
  result
rescue StandardError => e
  handle_exception(e, level: :error, operation: 'crypt.vault.renew_sessions')
  raise
end

#settingsObject



14
15
16
# File 'lib/legion/crypt/vault.rb', line 14

def settings
  Legion::Settings[:crypt][:vault]
end

#shutdown_renewerObject



121
122
123
124
125
126
127
# File 'lib/legion/crypt/vault.rb', line 121

def shutdown_renewer
  return unless Legion::Settings[:crypt][:vault][:connected]
  return if @renewer.nil?

  log.info 'Shutting down Legion::Crypt::Vault::Renewer'
  @renewer.cancel
end

#vault_exists?(name) ⇒ Boolean

Returns:

  • (Boolean)


163
164
165
# File 'lib/legion/crypt/vault.rb', line 163

def vault_exists?(name)
  ::Vault.sys.mounts.key?(name.to_sym)
end

#vault_healthy?Boolean

Returns:

  • (Boolean)


41
42
43
44
45
46
47
48
49
# File 'lib/legion/crypt/vault.rb', line 41

def vault_healthy?
  ::Vault.sys.health_status.initialized?
rescue ::Vault::HTTPError => e
  # 429 = standby, 472 = DR secondary, 473 = performance standby
  # All indicate an initialized, healthy Vault — just not the active node.
  return true if e.message =~ /\b(429|472|473)\b/

  raise
end

#write(path, cluster_name: nil, **hash) ⇒ Object



84
85
86
87
88
89
90
91
# File 'lib/legion/crypt/vault.rb', line 84

def write(path, cluster_name: nil, **hash)
  log.info "Vault kv write requested path=#{path}"
  kv_client(cluster_name: cluster_name).write(path, **hash)
  log.info "Vault kv write complete path=#{path}"
rescue StandardError => e
  handle_exception(e, level: :error, operation: 'crypt.vault.write', path: path)
  raise
end