yamine 0.10.0 → 0.10.2
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 +28 -0
- data/lib/yamine/cli/system.rb +18 -2
- data/lib/yamine/doctor.rb +37 -3
- data/lib/yamine/version.rb +1 -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: 06f020588bc63954dd44b5a99b598995ec3e6f562c03756b867d26ff44178af8
|
|
4
|
+
data.tar.gz: 97f1613f7ad01d1362e4098651048addd3a4a738912c6bd83427e3ea9453ad8d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3f03809157db470e905542751e16d8a6cb6edd6b91a78637f20e85fb5670e6b30e15f717d1bbaed714c70b077a756fe97744bfb27521304606b2d661dea57eab
|
|
7
|
+
data.tar.gz: 83bb42d5eb49f6ee4007d04fa3e1b13024a46044d1afc7756a126f43384f60cb9c6ca57d7440d77ffcbacd37b9d95b04dbf8eb933bbf1676a10440e4951bdba7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.10.2] — 2026-09-10
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `doctor` reports trusted CAs that are no longer the one on disk
|
|
8
|
+
(`[warn] ca: CA trusted, but N superseded CA(s) are still trusted —
|
|
9
|
+
run: yamine trust`). Pruning only runs inside `trust`, so after a CA
|
|
10
|
+
regeneration (including the rename) a superseded root would sit in the
|
|
11
|
+
keychain waiting for the next missing-CA event — possibly forever.
|
|
12
|
+
Unrelated trusted roots are never counted, and an unreadable keychain
|
|
13
|
+
stays silent: doctor is a read-only probe and must not fail.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [0.10.1] — 2026-09-10
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **The CA is regenerated before trust is judged, not after.** Both
|
|
21
|
+
trust paths asked `Certs.trusted?` first, which compares the trust
|
|
22
|
+
marker against the certificate ON DISK. A proxy regenerates the CA at
|
|
23
|
+
boot when the on-disk one is missing, expiring, or renamed — so after
|
|
24
|
+
a rename the marker still matched the OLD certificate, trust was
|
|
25
|
+
skipped as "already done", and the proxy then came up serving a CA the
|
|
26
|
+
keychain did not trust: TLS verification fails for every route. The
|
|
27
|
+
check now brings the CA up to date first (`ca_current_and_trusted?`,
|
|
28
|
+
used by both paths).
|
|
29
|
+
|
|
30
|
+
|
|
3
31
|
## [0.10.0] — 2026-09-10
|
|
4
32
|
|
|
5
33
|
The ask-local rename is finished: the two user-visible names it left
|
data/lib/yamine/cli/system.rb
CHANGED
|
@@ -334,13 +334,29 @@ module Yamine
|
|
|
334
334
|
puts "Installed root LaunchDaemon on port 443 (state: #{state_dir})."
|
|
335
335
|
end
|
|
336
336
|
|
|
337
|
+
# True when the trust store covers the CA this machine should be
|
|
338
|
+
# serving with — regenerating the CA first if it is missing,
|
|
339
|
+
# expiring, or renamed.
|
|
340
|
+
#
|
|
341
|
+
# The ordering is the whole point, so it lives in one place rather
|
|
342
|
+
# than at each call site. `trusted?` compares a marker against the
|
|
343
|
+
# fingerprint of the certificate ON DISK, and a proxy regenerates
|
|
344
|
+
# the CA at boot when the name changes. Checking the marker before
|
|
345
|
+
# the CA is current therefore reads a stale match, skips trust, and
|
|
346
|
+
# lets the proxy come up serving a CA the keychain does not trust —
|
|
347
|
+
# breaking TLS for every route.
|
|
348
|
+
def ca_current_and_trusted?(dir = Certs.state_dir)
|
|
349
|
+
Certs.ensure_ca(dir)
|
|
350
|
+
Certs.trusted?(dir)
|
|
351
|
+
end
|
|
352
|
+
|
|
337
353
|
# Root-only CA trust: the System keychain (all users, no prompt).
|
|
338
354
|
# Safe to call repeatedly — once the marker is set it is a no-op.
|
|
339
355
|
# Prints before doing work: the System keychain add can take a few
|
|
340
356
|
# seconds, and this runs in the root half of setup where silence
|
|
341
357
|
# reads as a hang.
|
|
342
358
|
def ensure_system_ca_trust
|
|
343
|
-
return if
|
|
359
|
+
return if ca_current_and_trusted?(Certs.state_dir)
|
|
344
360
|
|
|
345
361
|
puts " Trusting the CA into the System keychain..."
|
|
346
362
|
result = Trust.trust
|
|
@@ -760,7 +776,7 @@ module Yamine
|
|
|
760
776
|
# those users had already chosen to avoid.
|
|
761
777
|
def ensure_workstation!(ctx)
|
|
762
778
|
# 1. CA
|
|
763
|
-
unless
|
|
779
|
+
unless ca_current_and_trusted?(ctx.store.dir)
|
|
764
780
|
result = Yamine::Trust.trust
|
|
765
781
|
unless result[:trusted]
|
|
766
782
|
abort_setup("CA trust failed: #{result[:error]}",
|
data/lib/yamine/doctor.rb
CHANGED
|
@@ -174,11 +174,45 @@ module Yamine
|
|
|
174
174
|
unless File.file?(paths[:cert])
|
|
175
175
|
return Check.new(name: "ca", ok: false, message: "no CA yet — run: yamine trust")
|
|
176
176
|
end
|
|
177
|
-
|
|
178
|
-
Check.new(name: "ca", ok:
|
|
177
|
+
unless Certs.trusted?(dir)
|
|
178
|
+
return Check.new(name: "ca", ok: false, message: "CA not trusted — run: yamine trust")
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Trusted CAs that are not the one now on disk. They accumulate from
|
|
182
|
+
# CA regeneration (missing, expiring, or renamed — the ask-local
|
|
183
|
+
# rename regenerates every machine's CA), and a trusted root whose
|
|
184
|
+
# superseded private key is still on disk is a liability. Pruning
|
|
185
|
+
# only happens when `trust` runs, so after a rename it would wait
|
|
186
|
+
# for the next missing-CA event — i.e. possibly forever. Surface it
|
|
187
|
+
# here with the one command that clears it.
|
|
188
|
+
stale = stale_ca_count(dir)
|
|
189
|
+
if stale.positive?
|
|
190
|
+
Check.new(name: "ca", ok: true, warn: true,
|
|
191
|
+
message: "CA trusted, but #{stale} superseded CA(s) are still trusted" \
|
|
192
|
+
" — run: yamine trust")
|
|
179
193
|
else
|
|
180
|
-
Check.new(name: "ca", ok:
|
|
194
|
+
Check.new(name: "ca", ok: true, message: "CA trusted")
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# How many trusted certificates carry one of our CA names without
|
|
199
|
+
# being the CA currently on disk. Best-effort: 0 when the keychain
|
|
200
|
+
# cannot be read (doctor must never fail on a read-only probe).
|
|
201
|
+
def stale_ca_count(dir = Certs.state_dir)
|
|
202
|
+
return 0 unless Trust.respond_to?(:keychain_certs)
|
|
203
|
+
|
|
204
|
+
current = Trust.fingerprint_of(Certs.ca_paths(dir)[:cert])
|
|
205
|
+
return 0 unless current
|
|
206
|
+
|
|
207
|
+
names = Certs.ca_common_names
|
|
208
|
+
Trust.keychains.sum do |keychain|
|
|
209
|
+
Trust.keychain_certs(keychain).count do |entry|
|
|
210
|
+
entry[:fingerprint] != current &&
|
|
211
|
+
names.any? { |n| entry[:subject].include?(n) }
|
|
212
|
+
end
|
|
181
213
|
end
|
|
214
|
+
rescue StandardError
|
|
215
|
+
0
|
|
182
216
|
end
|
|
183
217
|
|
|
184
218
|
def print(checks, out: $stdout, json: false)
|
data/lib/yamine/version.rb
CHANGED