legion-crypt 1.4.19 → 1.4.20

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3a746c03498b392437f0d7db81caca5f6daa84aeda465bbf0db71bb898d25f1
4
- data.tar.gz: d27dfcdfa9032fa6bc8df4174f370c7896151c746bf8b810421d1c28d5d8e51b
3
+ metadata.gz: 6b78a171584f4ccb627b63cd35cad927a6773bd1802e0da432dd445dbceb85e4
4
+ data.tar.gz: b1806202e526aa259d1491a108f3d3b94b1c4564a05ea7b4afa702778069e715
5
5
  SHA512:
6
- metadata.gz: 9e3a72a7a2fc6b78439f582b33c3c667fb40f1ef86c8a0201fb53e38d2d122e3689fa3496e11df36e5085a551753d58864a8033bc2cb028662922d87ce7afc9a
7
- data.tar.gz: ab485a6da390007f060067bcca2863a5585fd6845f2dfc023658d48c6195364af7f99bf86f2492830d7991ae6a600a43fbd819d715412ca3f9c210fa95a830e3
6
+ metadata.gz: a1cc01154f5fef732c05efe2d99213f1742aa486e2c703f7707709dbf29ab74484b49e5be4c3a1513b116ffdb658382b70e124067771549f9dcabd8cad1fd6f1
7
+ data.tar.gz: e8e674be70714e3d439330e8d1d03b323319dd60b0f2069c26087fa772ec27c61cbc3a1ca5ed47cdff30e5ee8a826e31f16b0695f35c479184be7a02fd34e2b7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Legion::Crypt
2
2
 
3
+ ## [1.4.20] - 2026-03-27
4
+
5
+ ### Fixed
6
+ - `Vault#read`: unwrap KV v2 response envelope — `logical.read` returns `{data: {keys}, metadata: {}}` for KV v2 mounts; the nested `:data` key is now auto-detected and unwrapped
7
+
8
+ ### Added
9
+ - Debug logging throughout Vault auth, read, and cluster connection paths (`vault.rb`, `vault_cluster.rb`, `kerberos_auth.rb`, `lease_manager.rb`)
10
+ - `Vault#log_read_context`: logs path and namespace context for each Vault read
11
+ - `Vault#unwrap_kv_v2`: detects and unwraps KV v2 envelope pattern
12
+ - `VaultCluster`: debug logging for cluster connection, client build, and Kerberos auth flow
13
+ - `KerberosAuth`: debug logging for SPN, token exchange, policies, and renewal metadata
14
+ - `LeaseManager`: debug logging for lease fetch, renewal, and revocation
15
+
3
16
  ## [1.4.19] - 2026-03-26
4
17
 
5
18
  ### Fixed
@@ -17,10 +17,19 @@ module Legion
17
17
  def self.login(vault_client:, service_principal:, auth_path: DEFAULT_AUTH_PATH)
18
18
  raise GemMissingError, 'lex-kerberos gem is required for Kerberos auth' unless spnego_available?
19
19
 
20
+ log_debug("login: SPN=#{service_principal}, auth_path=#{auth_path}")
21
+ addr = vault_client.respond_to?(:address) ? vault_client.address : 'n/a'
22
+ ns = vault_client.respond_to?(:namespace) ? vault_client.namespace.inspect : 'n/a'
23
+ log_debug("login: vault_client.address=#{addr}, namespace=#{ns}")
24
+
20
25
  @kerberos_principal = nil
21
26
  token = obtain_token(service_principal)
27
+ log_debug("login: SPNEGO token obtained (#{token.length} chars)")
28
+
22
29
  result = exchange_token(vault_client, token, auth_path)
23
30
  @kerberos_principal = result[:metadata]&.dig('username') || result[:metadata]&.dig(:username)
31
+ log_debug("login: authenticated as #{@kerberos_principal.inspect}, policies=#{result[:policies].inspect}")
32
+ log_debug("login: renewable=#{result[:renewable]}, ttl=#{result[:lease_duration]}s")
24
33
  result
25
34
  end
26
35
 
@@ -41,6 +50,11 @@ module Legion
41
50
  @kerberos_principal = nil
42
51
  end
43
52
 
53
+ def self.log_debug(message)
54
+ Legion::Logging.debug("KerberosAuth: #{message}") if defined?(Legion::Logging)
55
+ end
56
+ private_class_method :log_debug
57
+
44
58
  class << self
45
59
  private
46
60
 
@@ -58,6 +72,7 @@ module Legion
58
72
 
59
73
  # The Vault Kerberos plugin reads the SPNEGO token from the HTTP
60
74
  # Authorization header, not the JSON body.
75
+ log_debug("exchange_token: PUT /v1/#{auth_path} (namespace=#{vault_client.respond_to?(:namespace) ? vault_client.namespace.inspect : 'n/a'})")
61
76
  json = vault_client.put(
62
77
  "/v1/#{auth_path}",
63
78
  '{}',
@@ -75,6 +90,7 @@ module Legion
75
90
  metadata: auth.metadata
76
91
  }
77
92
  rescue ::Vault::HTTPClientError => e
93
+ log_debug("exchange_token: HTTP error: #{e.message}")
78
94
  raise AuthError, "Vault Kerberos auth failed: #{e.message}"
79
95
  end
80
96
  end
@@ -27,7 +27,10 @@ module Legion
27
27
 
28
28
  begin
29
29
  response = logical.read(path)
30
- next unless response
30
+ unless response
31
+ log_warn("LeaseManager: no data at '#{name}' (#{path}) — path may not exist or role not configured")
32
+ next
33
+ end
31
34
 
32
35
  @lease_cache[name] = response.data || {}
33
36
  @active_leases[name] = {
@@ -44,6 +47,10 @@ module Legion
44
47
  end
45
48
  end
46
49
 
50
+ def fetched_count
51
+ @active_leases.size
52
+ end
53
+
47
54
  def fetch(name, key)
48
55
  data = @lease_cache[name.to_sym] || @lease_cache[name.to_s]
49
56
  return nil unless data
@@ -44,23 +44,34 @@ module Legion
44
44
 
45
45
  def read(path, type = 'legion')
46
46
  full_path = type.nil? || type.empty? ? "#{type}/#{path}" : path
47
- Legion::Logging.debug "Vault read: #{full_path}" if defined?(Legion::Logging)
47
+ log_read_context(full_path)
48
48
  lease = logical_client.read(full_path)
49
- add_session(path: lease.lease_id) if lease.respond_to? :lease_id
50
- lease.data
49
+ if lease.nil?
50
+ log_vault_debug("Vault read: #{full_path} returned nil")
51
+ return nil
52
+ end
53
+ add_session(path: lease.lease_id) if lease.respond_to?(:lease_id) && lease.lease_id && !lease.lease_id.empty?
54
+
55
+ data = lease.data
56
+ log_vault_debug("Vault read: #{full_path} returned keys=#{data&.keys&.inspect}")
57
+ unwrap_kv_v2(data, full_path)
51
58
  rescue StandardError => e
52
- Legion::Logging.warn "Vault read failed at #{full_path}: #{e.message}" if defined?(Legion::Logging)
59
+ Legion::Logging.warn "Vault read failed at #{full_path}: #{e.class}=#{e.message}" if defined?(Legion::Logging)
53
60
  raise
54
61
  end
55
62
 
56
63
  def get(path)
57
- Legion::Logging.debug "Vault kv get: #{path}" if defined?(Legion::Logging)
64
+ Legion::Logging.debug "Vault kv get: path=#{path}" if defined?(Legion::Logging)
58
65
  result = kv_client.read(path)
59
- return nil if result.nil?
66
+ if result.nil?
67
+ Legion::Logging.debug "Vault kv get: #{path} returned nil" if defined?(Legion::Logging)
68
+ return nil
69
+ end
60
70
 
71
+ Legion::Logging.debug "Vault kv get: #{path} returned keys=#{result.data&.keys&.inspect}" if defined?(Legion::Logging)
61
72
  result.data
62
73
  rescue StandardError => e
63
- Legion::Logging.warn "Vault kv get failed at #{path}: #{e.message}" if defined?(Legion::Logging)
74
+ Legion::Logging.warn "Vault kv get failed at #{path}: #{e.class}=#{e.message}" if defined?(Legion::Logging)
64
75
  raise
65
76
  end
66
77
 
@@ -159,6 +170,29 @@ module Legion
159
170
  ::Vault.logical
160
171
  end
161
172
  end
173
+
174
+ def log_read_context(full_path)
175
+ return unless defined?(Legion::Logging)
176
+
177
+ namespace = if respond_to?(:connected_clusters) && connected_clusters.any?
178
+ client = vault_client
179
+ client.respond_to?(:namespace) ? client.namespace : 'n/a'
180
+ else
181
+ 'n/a (global client)'
182
+ end
183
+ Legion::Logging.debug "Vault read: path=#{full_path}, namespace=#{namespace}"
184
+ end
185
+
186
+ def unwrap_kv_v2(data, full_path)
187
+ return data unless data.is_a?(Hash) && data.key?(:data) && data[:data].is_a?(Hash) && data.key?(:metadata)
188
+
189
+ log_vault_debug("Vault read: #{full_path} detected KV v2 envelope, unwrapping :data key")
190
+ data[:data]
191
+ end
192
+
193
+ def log_vault_debug(message)
194
+ Legion::Logging.debug(message) if defined?(Legion::Logging)
195
+ end
162
196
  end
163
197
  end
164
198
  end
@@ -40,8 +40,10 @@ module Legion
40
40
  end
41
41
 
42
42
  def connect_all_clusters
43
+ log_vault_debug("connect_all_clusters: #{clusters.size} cluster(s) configured")
43
44
  results = {}
44
45
  clusters.each do |name, config|
46
+ log_vault_debug("connect_all_clusters: #{name} (auth_method=#{config[:auth_method].inspect})")
45
47
  case config[:auth_method]&.to_s
46
48
  when 'kerberos'
47
49
  results[name] = connect_kerberos_cluster(name, config)
@@ -61,7 +63,9 @@ module Legion
61
63
  log_vault_error(name, e)
62
64
  end
63
65
 
64
- mark_vault_connected if results.any? { |_, v| v }
66
+ connected = results.select { |_, v| v }
67
+ log_vault_debug("connect_all_clusters: #{connected.size}/#{results.size} connected")
68
+ mark_vault_connected if connected.any?
65
69
  results
66
70
  end
67
71
 
@@ -82,8 +86,10 @@ module Legion
82
86
  def build_vault_client(config)
83
87
  return nil unless config.is_a?(Hash)
84
88
 
89
+ addr = "#{config[:protocol]}://#{config[:address]}:#{config[:port]}"
90
+ log_vault_debug("build_vault_client: address=#{addr}")
85
91
  client = ::Vault::Client.new(
86
- address: "#{config[:protocol]}://#{config[:address]}:#{config[:port]}",
92
+ address: addr,
87
93
  token: config[:token]
88
94
  )
89
95
  namespace =
@@ -94,6 +100,7 @@ module Legion
94
100
  crypt_settings.respond_to?(:dig) ? crypt_settings.dig(:vault, :vault_namespace) : nil
95
101
  end
96
102
  client.namespace = namespace if namespace
103
+ log_vault_debug("build_vault_client: namespace=#{namespace.inspect}")
97
104
  client
98
105
  end
99
106
 
@@ -108,6 +115,9 @@ module Legion
108
115
  def connect_kerberos_cluster(name, config)
109
116
  krb_config = config[:kerberos] || {}
110
117
  spn = krb_config[:service_principal]
118
+ auth_path = krb_config[:auth_path] || Legion::Crypt::KerberosAuth::DEFAULT_AUTH_PATH
119
+
120
+ log_vault_debug("connect_kerberos_cluster[#{name}]: SPN=#{spn}, auth_path=#{auth_path}, namespace=#{config[:namespace].inspect}")
111
121
 
112
122
  unless spn
113
123
  log_vault_warn(name, 'Kerberos auth missing service_principal, skipping')
@@ -116,10 +126,13 @@ module Legion
116
126
  end
117
127
 
118
128
  require 'legion/crypt/kerberos_auth'
129
+ client = vault_client(name)
130
+ log_vault_debug("connect_kerberos_cluster[#{name}]: client.namespace=#{client.respond_to?(:namespace) ? client.namespace.inspect : 'n/a'}")
131
+
119
132
  result = Legion::Crypt::KerberosAuth.login(
120
- vault_client: vault_client(name),
133
+ vault_client: client,
121
134
  service_principal: spn,
122
- auth_path: krb_config[:auth_path] || Legion::Crypt::KerberosAuth::DEFAULT_AUTH_PATH
135
+ auth_path: auth_path
123
136
  )
124
137
 
125
138
  config[:token] = result[:token]
@@ -127,6 +140,7 @@ module Legion
127
140
  config[:renewable] = result[:renewable]
128
141
  config[:connected] = true
129
142
  vault_client(name).token = result[:token]
143
+ log_vault_debug("connect_kerberos_cluster[#{name}]: policies=#{result[:policies].inspect}")
130
144
  log_cluster_connected(name, config)
131
145
  true
132
146
  rescue Legion::Crypt::KerberosAuth::GemMissingError => e
@@ -150,6 +164,10 @@ module Legion
150
164
  warn("Vault cluster #{name}: #{message}")
151
165
  end
152
166
  end
167
+
168
+ def log_vault_debug(message)
169
+ Legion::Logging.debug(message) if defined?(Legion::Logging)
170
+ end
153
171
  end
154
172
  end
155
173
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Crypt
5
- VERSION = '1.4.19'
5
+ VERSION = '1.4.20'
6
6
  end
7
7
  end
data/lib/legion/crypt.rb CHANGED
@@ -123,7 +123,13 @@ module Legion
123
123
  lease_manager = Legion::Crypt::LeaseManager.instance
124
124
  lease_manager.start(leases, vault_client: client)
125
125
  lease_manager.start_renewal_thread
126
- Legion::Logging.info "LeaseManager: #{leases.size} lease(s) initialized"
126
+ fetched = lease_manager.fetched_count
127
+ defined = leases.size
128
+ if fetched == defined
129
+ Legion::Logging.info "LeaseManager: #{fetched} lease(s) initialized"
130
+ else
131
+ Legion::Logging.warn "LeaseManager: #{fetched}/#{defined} lease(s) initialized (#{defined - fetched} failed)"
132
+ end
127
133
  rescue StandardError => e
128
134
  Legion::Logging.warn "LeaseManager startup failed: #{e.message}"
129
135
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-crypt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.19
4
+ version: 1.4.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity