legion-crypt 1.4.4 → 1.4.5

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: 5999b5f3ac22f39690f9ee509cec95793e39619498615c6c92c28a509561d072
4
- data.tar.gz: 942363ac4938f2f9f36436054d42be8202c16d81dfdfd9d5e66ed52763299fe4
3
+ metadata.gz: 2e448a5dd1e2c70e43cff0b6512e1b2af1d41f9a2af7b9719495ba5b10c90ec7
4
+ data.tar.gz: 6c270665c5a185baa844e5b7e80af9f2ac8922a520748e387a9d10363294061e
5
5
  SHA512:
6
- metadata.gz: 2ef2a270e3cb979f98dc0e0796234841799e5a702836db38ee64aa2b16d9d467d57e850971a9260d430993cd710b1afc51562ba6efc1203d67e35ad85220a063
7
- data.tar.gz: 58410cd7e95c2532f07969bc0a6150abc177249388db68080064311e9cb742633b30a45dbab5506731b30c29e520565ae53db6753d70fb5874322e8fbce4fbec
6
+ metadata.gz: bc43cab1939dee1cd1d6c28a07a2766953af52fc30230375d47e23e289f53beaf3a2be47d98c549bd9098c545d6dd540faa8a87143bb678429265a96b55d96cf
7
+ data.tar.gz: 1813d049d59f5bb81baeef8f10fb2e9f68d7540c8fb4858a75f2f40d881eb7b101a42ce4f89161a38ef14122da0e53e1cf0d2009716d7411ba16c0a77902504c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Legion::Crypt
2
2
 
3
+ ## [1.4.5] - 2026-03-20
4
+
5
+ ### Changed
6
+ - Refactored `Legion::Crypt::TLS` to standard `resolve` pattern: pure config normalizer with port auto-detect, vault URI resolution, legacy key migration, and three verification levels (none/peer/mutual)
7
+ - Removed consumer-specific `bunny_options` and `sequel_options` methods (moved to consuming gems)
8
+
9
+ ### Added
10
+ - `TLS.resolve(tls_config, port:)` — standard TLS config resolver
11
+ - `TLS.migrate_legacy(config)` — backwards-compat mapping for transport's old TLS keys
12
+ - `TLS::TLS_PORTS` — known TLS port auto-detection map (5671, 6380, 11207)
13
+ - Default `tls:` settings block in `Legion::Crypt::Settings`
14
+
3
15
  ## [1.4.4] - 2026-03-18
4
16
 
5
17
  ### Added
data/CLAUDE.md CHANGED
@@ -8,6 +8,7 @@
8
8
  Handles encryption, decryption, secrets management, JWT token management, and HashiCorp Vault connectivity for the LegionIO framework. Provides AES-256-CBC message encryption, RSA key pair generation, cluster secret management, JWT issue/verify operations, and Vault token lifecycle management.
9
9
 
10
10
  **GitHub**: https://github.com/LegionIO/legion-crypt
11
+ **Version**: 1.4.4
11
12
  **License**: Apache-2.0
12
13
 
13
14
  ## Architecture
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # legion-crypt
2
2
 
3
- Encryption, secrets management, JWT token management, and HashiCorp Vault integration for the [LegionIO](https://github.com/LegionIO/LegionIO) framework. Provides AES-256-CBC message encryption, RSA key pair generation, cluster secret management, JWT issue/verify operations, and Vault token lifecycle management.
3
+ Encryption, secrets management, JWT token management, and HashiCorp Vault integration for the [LegionIO](https://github.com/LegionIO/LegionIO) framework. Provides AES-256-CBC message encryption, RSA key pair generation, cluster secret management, JWT issue/verify operations, Vault token lifecycle management, and multi-cluster Vault connectivity.
4
+
5
+ **Version**: 1.4.4
4
6
 
5
7
  ## Installation
6
8
 
@@ -146,6 +148,48 @@ Both `username` and `password` come from a single Vault read — one lease, one
146
148
 
147
149
  Lease names are stable across environments. The actual Vault paths are deployment-specific config.
148
150
 
151
+ ## Multi-Cluster Vault
152
+
153
+ `VaultCluster` supports connecting to multiple Vault clusters simultaneously. Each cluster has its own `::Vault::Client` instance.
154
+
155
+ ```json
156
+ {
157
+ "crypt": {
158
+ "vault": {
159
+ "default": "primary",
160
+ "clusters": {
161
+ "primary": {
162
+ "protocol": "https",
163
+ "address": "vault.example.com",
164
+ "port": 8200,
165
+ "namespace": "my-namespace",
166
+ "auth_method": "ldap"
167
+ },
168
+ "secondary": {
169
+ "protocol": "https",
170
+ "address": "vault2.example.com",
171
+ "port": 8200,
172
+ "auth_method": "ldap"
173
+ }
174
+ }
175
+ }
176
+ }
177
+ }
178
+ ```
179
+
180
+ ```ruby
181
+ # Authenticate to all LDAP-configured clusters at once
182
+ Legion::Crypt.ldap_login_all(username: 'user', password: 'pass')
183
+
184
+ # Read from specific cluster
185
+ Legion::Crypt.read('secret/data/mykey', cluster: :secondary)
186
+
187
+ # Get a Vault client for a specific cluster
188
+ client = Legion::Crypt.vault_client(:primary)
189
+ ```
190
+
191
+ When `clusters` is empty, the legacy single-cluster path is used (backward compatible).
192
+
149
193
  ## Requirements
150
194
 
151
195
  - Ruby >= 3.4
@@ -3,10 +3,21 @@
3
3
  module Legion
4
4
  module Crypt
5
5
  module Settings
6
+ def self.tls
7
+ {
8
+ enabled: false,
9
+ verify: 'peer',
10
+ ca: nil,
11
+ cert: nil,
12
+ key: nil
13
+ }
14
+ end
15
+
6
16
  def self.default
7
17
  {
8
18
  vault: vault,
9
19
  jwt: jwt,
20
+ tls: tls,
10
21
  cs_encrypt_ready: false,
11
22
  dynamic_keys: true,
12
23
  cluster_secret: nil,
@@ -1,78 +1,92 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'openssl'
4
-
5
3
  module Legion
6
4
  module Crypt
7
5
  module TLS
8
- DEFAULT_CERT_DIR = '/etc/legion/tls'
6
+ TLS_PORTS = {
7
+ 5671 => 'amqp',
8
+ 6380 => 'redis',
9
+ 11_207 => 'memcached'
10
+ }.freeze
9
11
 
10
12
  class << self
11
- def enabled?
12
- settings_dig(:enabled) == true
13
- end
13
+ def resolve(tls_config, port: nil)
14
+ config = symbolize_keys(migrate_legacy(tls_config || {}))
14
15
 
15
- def ssl_context(role: :client) # rubocop:disable Lint/UnusedMethodArgument
16
- ctx = OpenSSL::SSL::SSLContext.new
17
- ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
18
- ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
16
+ enabled = config[:enabled]
17
+ auto_detected = false
19
18
 
20
- ctx.cert = OpenSSL::X509::Certificate.new(File.read(cert_path)) if cert_path && File.exist?(cert_path)
21
- ctx.key = OpenSSL::PKey.read(File.read(key_path)) if key_path && File.exist?(key_path)
22
- ctx.ca_file = ca_path if ca_path && File.exist?(ca_path)
19
+ if enabled.nil? && port && TLS_PORTS.key?(port.to_i)
20
+ enabled = true
21
+ auto_detected = true
22
+ log_warn("TLS auto-enabled for port #{port}")
23
+ end
23
24
 
24
- ctx
25
- end
25
+ enabled = false if enabled.nil?
26
26
 
27
- def bunny_options
28
- return {} unless enabled?
27
+ verify = normalize_verify(config[:verify])
28
+ ca = resolve_uri(config[:ca])
29
+ cert = resolve_uri(config[:cert])
30
+ key = resolve_uri(config[:key])
31
+
32
+ if verify == :mutual && (cert.nil? || key.nil?)
33
+ log_warn('TLS mutual requested but cert or key missing, downgrading to peer')
34
+ verify = :peer
35
+ end
29
36
 
30
37
  {
31
- tls: true,
32
- tls_cert: cert_path,
33
- tls_key: key_path,
34
- tls_ca_certificates: [ca_path].compact,
35
- verify_peer: true
38
+ enabled: enabled,
39
+ verify: verify,
40
+ ca: ca,
41
+ cert: cert,
42
+ key: key,
43
+ auto_detected: auto_detected
36
44
  }
37
45
  end
38
46
 
39
- def sequel_options
40
- return {} unless enabled?
47
+ def migrate_legacy(config)
48
+ config = symbolize_keys(config)
49
+ return config unless config.key?(:use_tls) && !config.key?(:enabled)
41
50
 
42
51
  {
43
- sslmode: 'verify-full',
44
- sslcert: cert_path,
45
- sslkey: key_path,
46
- sslrootcert: ca_path
52
+ enabled: config[:use_tls],
53
+ verify: config[:verify_peer] ? 'peer' : 'none',
54
+ ca: config[:ca_certs],
55
+ cert: config[:tls_cert],
56
+ key: config[:tls_key]
47
57
  }
48
58
  end
49
59
 
50
- def cert_path
51
- settings_dig(:cert_path) || File.join(DEFAULT_CERT_DIR, 'legion.crt')
52
- end
60
+ private
53
61
 
54
- def key_path
55
- settings_dig(:key_path) || File.join(DEFAULT_CERT_DIR, 'legion.key')
62
+ def symbolize_keys(hash)
63
+ hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
56
64
  end
57
65
 
58
- def ca_path
59
- settings_dig(:ca_path) || File.join(DEFAULT_CERT_DIR, 'ca-bundle.crt')
66
+ def normalize_verify(value)
67
+ case value.to_s
68
+ when 'none' then :none
69
+ when 'mutual' then :mutual
70
+ else :peer
71
+ end
60
72
  end
61
73
 
62
- private
63
-
64
- def settings_dig(*keys)
65
- return nil unless defined?(Legion::Settings)
74
+ def resolve_uri(value)
75
+ return nil if value.nil?
66
76
 
67
- result = Legion::Settings[:crypt]
68
- [:tls, *keys].each do |key|
69
- return nil unless result.is_a?(Hash)
77
+ if defined?(Legion::Settings::Resolver)
78
+ Legion::Settings::Resolver.resolve_value(value)
79
+ else
80
+ value
81
+ end
82
+ end
70
83
 
71
- result = result[key]
84
+ def log_warn(msg)
85
+ if defined?(Legion::Logging)
86
+ Legion::Logging.warn(msg)
87
+ else
88
+ warn msg
72
89
  end
73
- result
74
- rescue StandardError
75
- nil
76
90
  end
77
91
  end
78
92
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Crypt
5
- VERSION = '1.4.4'
5
+ VERSION = '1.4.5'
6
6
  end
7
7
  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.4
4
+ version: 1.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity