legion-settings 1.3.16 → 1.3.18
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 +17 -0
- data/lib/legion/settings/validators/tls.rb +97 -0
- data/lib/legion/settings/version.rb +1 -1
- data/lib/legion/settings.rb +40 -10
- 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: 6864083fb9f9f31a4019e85d6e1798afa56fc0bb53123b8388002024803bc11d
|
|
4
|
+
data.tar.gz: 843ad56f1af0c905c4f53335d20b4300513b97b82e8f1b65aaf971c7c21f7b7f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d1116e961f4b82d4d5380a27c3f798e45022b72792e44108eff17f1d5ad74b4a93d744e245690ef90895d2a320c862ee646fb4b72f0bf3bc3df218a0886549ed
|
|
7
|
+
data.tar.gz: 88ebea0500835477eb45550cb48e39b202c1c54eb8bddd6af9e09c93548ccf6864ca366bae9b3dd352c29d0b322c8c00f0949ad2ace90f2229ae8f7a7017c993
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Legion::Settings Changelog
|
|
2
2
|
|
|
3
|
+
## [1.3.18] - 2026-03-24
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `Legion::Settings::Validators::Tls` — validates TLS settings blocks (transport, data, api, security) with warnings for weak verify modes, errors for insecure sslmode in production, and missing cert paths
|
|
7
|
+
|
|
8
|
+
## [1.3.17] - 2026-03-24
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- `load` is now idempotent: reuses existing Loader, only sets `@loaded` when config files are provided, skips on subsequent calls unless `force: true`
|
|
12
|
+
- `[]`, `dig`, `merge_settings`, `set_prop`, `validate!`, `resolve_secrets!`, `errors` now use lightweight `ensure_loader` (env vars only, no DNS bootstrap) instead of triggering full `load`
|
|
13
|
+
- Module merges via `merge_settings` at require-time no longer trigger DNS bootstrap or create a new Loader
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- `loaded?` class method to check if settings have been fully loaded with config files
|
|
17
|
+
- `reset!` class method to clear all state (loader, schema, cross-validations) for testing
|
|
18
|
+
- `ensure_loader` private method: creates minimal Loader with env vars only, no DNS bootstrap
|
|
19
|
+
|
|
3
20
|
## [1.3.16] - 2026-03-24
|
|
4
21
|
|
|
5
22
|
### Fixed
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Settings
|
|
5
|
+
module Validators
|
|
6
|
+
module Tls
|
|
7
|
+
TLS_BLOCKS = %i[transport data cache security api].freeze
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def validate(settings)
|
|
11
|
+
warnings = []
|
|
12
|
+
errors = []
|
|
13
|
+
|
|
14
|
+
validate_transport_tls(settings, warnings)
|
|
15
|
+
validate_data_tls(settings, warnings, errors)
|
|
16
|
+
validate_security_mtls(settings, warnings)
|
|
17
|
+
validate_api_tls(settings, warnings, errors)
|
|
18
|
+
|
|
19
|
+
{ valid: errors.empty?, warnings: warnings, errors: errors }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def validate_transport_tls(settings, warnings)
|
|
25
|
+
tls = dig_tls(settings, :transport)
|
|
26
|
+
return unless tls[:enabled]
|
|
27
|
+
|
|
28
|
+
warnings << 'transport.tls: verify is none — peer verification disabled, connections are unauthenticated' if tls[:verify].to_s == 'none'
|
|
29
|
+
|
|
30
|
+
check_cert_path(tls[:cert], 'transport.tls.cert', warnings)
|
|
31
|
+
check_cert_path(tls[:key], 'transport.tls.key', warnings)
|
|
32
|
+
check_cert_path(tls[:ca], 'transport.tls.ca', warnings)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def validate_data_tls(settings, warnings, errors)
|
|
36
|
+
tls = dig_tls(settings, :data)
|
|
37
|
+
return unless tls[:enabled]
|
|
38
|
+
|
|
39
|
+
sslmode = tls[:sslmode].to_s
|
|
40
|
+
return if sslmode.empty? || sslmode == 'verify-full'
|
|
41
|
+
|
|
42
|
+
env = settings[:env].to_s
|
|
43
|
+
msg = "data.tls: sslmode '#{sslmode}' should be 'verify-full' to prevent MITM attacks"
|
|
44
|
+
if env == 'production'
|
|
45
|
+
errors << msg
|
|
46
|
+
else
|
|
47
|
+
warnings << msg
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def validate_security_mtls(settings, warnings)
|
|
52
|
+
mtls = settings.dig(:security, :mtls) || {}
|
|
53
|
+
mtls = symbolize_keys(mtls)
|
|
54
|
+
return unless mtls[:enabled]
|
|
55
|
+
|
|
56
|
+
check_cert_path(mtls[:cert], 'security.mtls.cert', warnings)
|
|
57
|
+
check_cert_path(mtls[:key], 'security.mtls.key', warnings)
|
|
58
|
+
check_cert_path(mtls[:ca], 'security.mtls.ca', warnings)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate_api_tls(settings, _warnings, errors)
|
|
62
|
+
tls = dig_tls(settings, :api)
|
|
63
|
+
return unless tls[:enabled]
|
|
64
|
+
|
|
65
|
+
cert = tls[:cert]
|
|
66
|
+
key = tls[:key]
|
|
67
|
+
|
|
68
|
+
errors << 'api.tls: enabled but api.tls.cert is not set' if cert.nil? || cert.to_s.empty?
|
|
69
|
+
|
|
70
|
+
errors << 'api.tls: enabled but api.tls.key is not set' if key.nil? || key.to_s.empty?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def dig_tls(settings, component)
|
|
74
|
+
raw = settings.dig(component, :tls) || {}
|
|
75
|
+
symbolize_keys(raw)
|
|
76
|
+
rescue StandardError
|
|
77
|
+
{}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def check_cert_path(path, label, warnings)
|
|
81
|
+
return if path.nil? || path.to_s.empty?
|
|
82
|
+
return if path.to_s.start_with?('vault://', 'env://', 'lease://')
|
|
83
|
+
return if ::File.exist?(path.to_s)
|
|
84
|
+
|
|
85
|
+
warnings << "#{label}: path '#{path}' does not exist"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def symbolize_keys(hash)
|
|
89
|
+
return {} unless hash.is_a?(Hash)
|
|
90
|
+
|
|
91
|
+
hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
data/lib/legion/settings.rb
CHANGED
|
@@ -16,25 +16,40 @@ module Legion
|
|
|
16
16
|
attr_accessor :loader
|
|
17
17
|
|
|
18
18
|
def load(options = {})
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
has_config = options[:config_file] || options[:config_dir] || options[:config_dirs]&.any?
|
|
20
|
+
|
|
21
|
+
# Already fully loaded with config files — skip unless forced
|
|
22
|
+
return @loader if @loaded && !options[:force]
|
|
23
|
+
|
|
24
|
+
# Create Loader once; reuse for subsequent calls (preserves module merges)
|
|
25
|
+
if @loader.nil? || options[:force]
|
|
26
|
+
@loader = Legion::Settings::Loader.new
|
|
27
|
+
@loader.load_env
|
|
28
|
+
@loader.load_dns_bootstrap
|
|
29
|
+
end
|
|
30
|
+
|
|
22
31
|
@loader.load_file(options[:config_file]) if options[:config_file]
|
|
23
32
|
@loader.load_directory(options[:config_dir]) if options[:config_dir]
|
|
24
33
|
options[:config_dirs]&.each do |directory|
|
|
25
34
|
@loader.load_directory(directory)
|
|
26
35
|
end
|
|
36
|
+
|
|
37
|
+
@loaded = true if has_config
|
|
27
38
|
logger.info("Settings loaded from #{@loader.loaded_files.size} files")
|
|
28
39
|
@loader
|
|
29
40
|
end
|
|
30
41
|
|
|
42
|
+
def loaded?
|
|
43
|
+
@loaded == true
|
|
44
|
+
end
|
|
45
|
+
|
|
31
46
|
def get(options = {})
|
|
32
47
|
@loader || @loader = load(options)
|
|
33
48
|
end
|
|
34
49
|
|
|
35
50
|
def [](key)
|
|
36
51
|
logger.info('Legion::Settings was not loading, auto loading now!') if @loader.nil?
|
|
37
|
-
|
|
52
|
+
ensure_loader
|
|
38
53
|
@loader[key]
|
|
39
54
|
rescue NoMethodError, TypeError => e
|
|
40
55
|
Legion::Logging.debug("Legion::Settings#[] key=#{key} failed: #{e.message}") if defined?(Legion::Logging)
|
|
@@ -42,7 +57,7 @@ module Legion
|
|
|
42
57
|
end
|
|
43
58
|
|
|
44
59
|
def dig(*keys)
|
|
45
|
-
|
|
60
|
+
ensure_loader
|
|
46
61
|
@loader.dig(*keys)
|
|
47
62
|
rescue NoMethodError, TypeError => e
|
|
48
63
|
Legion::Logging.debug("Legion::Settings#dig keys=#{keys.inspect} failed: #{e.message}") if defined?(Legion::Logging)
|
|
@@ -50,12 +65,12 @@ module Legion
|
|
|
50
65
|
end
|
|
51
66
|
|
|
52
67
|
def set_prop(key, value)
|
|
53
|
-
|
|
68
|
+
ensure_loader
|
|
54
69
|
@loader[key] = value
|
|
55
70
|
end
|
|
56
71
|
|
|
57
72
|
def merge_settings(key, hash)
|
|
58
|
-
|
|
73
|
+
ensure_loader
|
|
59
74
|
thing = {}
|
|
60
75
|
thing[key.to_sym] = hash
|
|
61
76
|
@loader.load_module_settings(thing)
|
|
@@ -90,7 +105,7 @@ module Legion
|
|
|
90
105
|
end
|
|
91
106
|
|
|
92
107
|
def validate!
|
|
93
|
-
|
|
108
|
+
ensure_loader
|
|
94
109
|
revalidate_all_modules
|
|
95
110
|
run_cross_validations
|
|
96
111
|
detect_unknown_keys
|
|
@@ -108,7 +123,7 @@ module Legion
|
|
|
108
123
|
end
|
|
109
124
|
|
|
110
125
|
def resolve_secrets!
|
|
111
|
-
|
|
126
|
+
ensure_loader
|
|
112
127
|
require 'legion/settings/resolver'
|
|
113
128
|
Resolver.resolve_secrets!(@loader.to_hash)
|
|
114
129
|
logger.debug('Secret resolution complete')
|
|
@@ -119,10 +134,17 @@ module Legion
|
|
|
119
134
|
end
|
|
120
135
|
|
|
121
136
|
def errors
|
|
122
|
-
|
|
137
|
+
ensure_loader
|
|
123
138
|
@loader.errors
|
|
124
139
|
end
|
|
125
140
|
|
|
141
|
+
def reset!
|
|
142
|
+
@loader = nil
|
|
143
|
+
@loaded = nil
|
|
144
|
+
@schema = nil
|
|
145
|
+
@cross_validations = nil
|
|
146
|
+
end
|
|
147
|
+
|
|
126
148
|
def logger
|
|
127
149
|
@logger = if ::Legion.const_defined?('Logging')
|
|
128
150
|
::Legion::Logging
|
|
@@ -138,6 +160,14 @@ module Legion
|
|
|
138
160
|
|
|
139
161
|
private
|
|
140
162
|
|
|
163
|
+
def ensure_loader
|
|
164
|
+
return @loader if @loader
|
|
165
|
+
|
|
166
|
+
@loader = Legion::Settings::Loader.new
|
|
167
|
+
@loader.load_env
|
|
168
|
+
@loader
|
|
169
|
+
end
|
|
170
|
+
|
|
141
171
|
def cross_validations
|
|
142
172
|
@cross_validations ||= []
|
|
143
173
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: legion-settings
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.18
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -57,6 +57,7 @@ files:
|
|
|
57
57
|
- lib/legion/settings/resolver.rb
|
|
58
58
|
- lib/legion/settings/schema.rb
|
|
59
59
|
- lib/legion/settings/validation_error.rb
|
|
60
|
+
- lib/legion/settings/validators/tls.rb
|
|
60
61
|
- lib/legion/settings/version.rb
|
|
61
62
|
- sonar-project.properties
|
|
62
63
|
homepage: https://github.com/LegionIO/legion-settings
|