legion-data 1.10.7 → 1.10.8
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 +4 -0
- data/lib/legion/data/auth_failure_handler.rb +80 -0
- data/lib/legion/data/connection.rb +33 -1
- data/lib/legion/data/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 869153710ece6717285edf2de7483f4c9f5fe5b493837681b80d05b77b014971
|
|
4
|
+
data.tar.gz: 2813fd707f0f7a80858802046b7ba2a2f23611f94a1515c66e5c7a5db1771dd6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 41aff7bbce675a95d8a2ec6683eb0350decde2cf66854971b0c43fed1d4a09bdc50563e922bfe29d5a01660c522d579be7f2719495150d136ba262f433ba4ac4
|
|
7
|
+
data.tar.gz: c766be4f0a13cf3ac3dc91e305d275742d5c49206ec90819631d05739137fb1c9eadbc550feeef1d6e635961ec8bf439b9b5fc19dfc334d1798859573d87902a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Legion::Data Changelog
|
|
2
2
|
|
|
3
|
+
## [1.10.8] - 2026-07-23
|
|
4
|
+
### Fixed
|
|
5
|
+
- Detect auth failures (`role does not exist`, `password authentication failed`) on Sequel pool connections and trigger immediate Vault lease reissue via LeaseManager instead of retrying dead credentials forever. 30-second cooldown prevents reissue storms during bulk credential rotation.
|
|
6
|
+
|
|
3
7
|
## [1.10.7] - 2026-07-15
|
|
4
8
|
### Fixed
|
|
5
9
|
- Fail loud with actionable error when unresolved lease:// credentials reach connection
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/logging/helper'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Data
|
|
7
|
+
module AuthFailureHandler
|
|
8
|
+
extend Legion::Logging::Helper
|
|
9
|
+
|
|
10
|
+
AUTH_FAILURE_PATTERNS = [
|
|
11
|
+
/role .* does not exist/i,
|
|
12
|
+
/password authentication failed/i,
|
|
13
|
+
/authentication failed/i,
|
|
14
|
+
/no pg_hba\.conf entry/i,
|
|
15
|
+
/permission denied for table/i,
|
|
16
|
+
/permission denied for relation/i
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
REISSUE_COOLDOWN = 30
|
|
20
|
+
|
|
21
|
+
@last_reissue_at = nil
|
|
22
|
+
@mutex = Mutex.new
|
|
23
|
+
|
|
24
|
+
module SequelHook
|
|
25
|
+
def connect(server)
|
|
26
|
+
super
|
|
27
|
+
rescue StandardError => e
|
|
28
|
+
Legion::Data::AuthFailureHandler.handle(e)
|
|
29
|
+
raise
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def raise_error(exception, opts = Sequel::OPTS)
|
|
33
|
+
Legion::Data::AuthFailureHandler.handle(exception)
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
def install(sequel_db)
|
|
40
|
+
sequel_db.singleton_class.prepend(SequelHook)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def handle(error)
|
|
44
|
+
return unless auth_failure?(error)
|
|
45
|
+
return if on_cooldown?
|
|
46
|
+
|
|
47
|
+
request_reissue(error)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def auth_failure?(error)
|
|
51
|
+
message = error.message.to_s
|
|
52
|
+
AUTH_FAILURE_PATTERNS.any? { |pattern| message.match?(pattern) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def on_cooldown?
|
|
56
|
+
@mutex.synchronize do
|
|
57
|
+
return false unless @last_reissue_at
|
|
58
|
+
|
|
59
|
+
(Time.now - @last_reissue_at) < REISSUE_COOLDOWN
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def request_reissue(error, adapter: :postgresql)
|
|
64
|
+
@mutex.synchronize { @last_reissue_at = Time.now }
|
|
65
|
+
log.error("Legion::Data auth failure detected: #{error.message} — requesting lease reissue")
|
|
66
|
+
|
|
67
|
+
return unless defined?(Legion::Crypt::LeaseManager)
|
|
68
|
+
|
|
69
|
+
Legion::Crypt::LeaseManager.instance.reissue_lease(adapter)
|
|
70
|
+
rescue StandardError => e
|
|
71
|
+
handle_exception(e, level: :error, handled: true, operation: :auth_failure_reissue)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def reset!
|
|
75
|
+
@mutex.synchronize { @last_reissue_at = nil }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'legion/logging/helper'
|
|
4
|
+
require 'legion/data/auth_failure_handler'
|
|
4
5
|
|
|
5
6
|
require 'fileutils'
|
|
6
7
|
require 'sequel'
|
|
@@ -299,6 +300,7 @@ module Legion
|
|
|
299
300
|
@sequel.opts[:password] = new_pass
|
|
300
301
|
|
|
301
302
|
@sequel.disconnect
|
|
303
|
+
expire_all_pooled_connections
|
|
302
304
|
|
|
303
305
|
@sequel.test_connection
|
|
304
306
|
log.info("reconnect_with_fresh_creds: rotated credentials (#{old_user} → #{new_user})")
|
|
@@ -309,6 +311,16 @@ module Legion
|
|
|
309
311
|
false
|
|
310
312
|
end
|
|
311
313
|
|
|
314
|
+
def expire_all_pooled_connections
|
|
315
|
+
pool = @sequel.pool
|
|
316
|
+
return unless pool.instance_variable_defined?(:@connection_expiration_timestamps)
|
|
317
|
+
|
|
318
|
+
timestamps = pool.instance_variable_get(:@connection_expiration_timestamps)
|
|
319
|
+
timestamps.each_key { |conn| timestamps[conn] = [0, 0].freeze }
|
|
320
|
+
rescue StandardError => e
|
|
321
|
+
handle_exception(e, level: :warn, handled: true, operation: :expire_all_pooled_connections)
|
|
322
|
+
end
|
|
323
|
+
|
|
312
324
|
def connect_with_replicas
|
|
313
325
|
return unless adapter == :postgres
|
|
314
326
|
|
|
@@ -397,10 +409,22 @@ module Legion
|
|
|
397
409
|
conn_host = actual[:host] || '127.0.0.1'
|
|
398
410
|
conn_port = actual[:port]
|
|
399
411
|
conn_db = actual[:database] || actual[:db]
|
|
400
|
-
|
|
412
|
+
lease_id = current_lease_id
|
|
413
|
+
msg = "Connected to #{adapter}://#{conn_user}@#{conn_host}:#{conn_port}/#{conn_db}"
|
|
414
|
+
msg += " lease_id=#{lease_id}" if lease_id
|
|
415
|
+
log.info msg
|
|
401
416
|
end
|
|
402
417
|
end
|
|
403
418
|
|
|
419
|
+
def current_lease_id
|
|
420
|
+
return unless defined?(Legion::Crypt::LeaseManager)
|
|
421
|
+
|
|
422
|
+
Legion::Crypt::LeaseManager.instance.active_leases.dig(:postgresql, :lease_id)
|
|
423
|
+
rescue StandardError => e
|
|
424
|
+
handle_exception(e, level: :warn, handled: true, operation: :current_lease_id)
|
|
425
|
+
nil
|
|
426
|
+
end
|
|
427
|
+
|
|
404
428
|
def dev_fallback?
|
|
405
429
|
data_settings = Legion::Settings[:data]
|
|
406
430
|
data_settings[:dev_mode] == true && data_settings[:dev_fallback] != false
|
|
@@ -608,10 +632,18 @@ module Legion
|
|
|
608
632
|
@sequel.extension(:connection_expiration)
|
|
609
633
|
@sequel.pool.connection_expiration_timeout = data[:connection_expiration_timeout]
|
|
610
634
|
end
|
|
635
|
+
|
|
636
|
+
install_auth_failure_hook
|
|
611
637
|
rescue StandardError => e
|
|
612
638
|
handle_exception(e, level: :warn, handled: true, operation: :configure_extensions, adapter: adapter)
|
|
613
639
|
end
|
|
614
640
|
|
|
641
|
+
def install_auth_failure_hook
|
|
642
|
+
Legion::Data::AuthFailureHandler.install(@sequel)
|
|
643
|
+
rescue StandardError => e
|
|
644
|
+
handle_exception(e, level: :warn, handled: true, operation: :install_auth_failure_hook)
|
|
645
|
+
end
|
|
646
|
+
|
|
615
647
|
def build_data_logger
|
|
616
648
|
tagged = if defined?(Legion::Logging::TaggedLogger) && respond_to?(:tagged_logger_settings, true)
|
|
617
649
|
Legion::Logging::TaggedLogger.new(
|
data/lib/legion/data/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: legion-data
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.10.
|
|
4
|
+
version: 1.10.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -124,6 +124,7 @@ files:
|
|
|
124
124
|
- lib/legion/data/archiver.rb
|
|
125
125
|
- lib/legion/data/audit_log_hash_chain.rb
|
|
126
126
|
- lib/legion/data/audit_record.rb
|
|
127
|
+
- lib/legion/data/auth_failure_handler.rb
|
|
127
128
|
- lib/legion/data/connection.rb
|
|
128
129
|
- lib/legion/data/encryption/cipher.rb
|
|
129
130
|
- lib/legion/data/encryption/key_provider.rb
|