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 +4 -4
- data/CHANGELOG.md +13 -0
- data/lib/legion/crypt/kerberos_auth.rb +16 -0
- data/lib/legion/crypt/lease_manager.rb +8 -1
- data/lib/legion/crypt/vault.rb +41 -7
- data/lib/legion/crypt/vault_cluster.rb +22 -4
- data/lib/legion/crypt/version.rb +1 -1
- data/lib/legion/crypt.rb +7 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6b78a171584f4ccb627b63cd35cad927a6773bd1802e0da432dd445dbceb85e4
|
|
4
|
+
data.tar.gz: b1806202e526aa259d1491a108f3d3b94b1c4564a05ea7b4afa702778069e715
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
data/lib/legion/crypt/vault.rb
CHANGED
|
@@ -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
|
-
|
|
47
|
+
log_read_context(full_path)
|
|
48
48
|
lease = logical_client.read(full_path)
|
|
49
|
-
|
|
50
|
-
|
|
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:
|
|
64
|
+
Legion::Logging.debug "Vault kv get: path=#{path}" if defined?(Legion::Logging)
|
|
58
65
|
result = kv_client.read(path)
|
|
59
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
133
|
+
vault_client: client,
|
|
121
134
|
service_principal: spn,
|
|
122
|
-
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
|
data/lib/legion/crypt/version.rb
CHANGED
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
|
-
|
|
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
|