feat(tls): TLS-root owner gate + leaf-rotation hardening + self-heal timeout#43
Merged
Merged
Conversation
…timeout Batches four #624/#42-arc follow-ups in dig-node-service. - #661: verify the TLS root is SYSTEM/Administrators-owned (Windows) or root-owned + not group/world-writable (unix) before reading the leaf or ca.key; fail CLOSED to plaintext otherwise. Extracts the spawn-free Win32 owner-SID reader from self_heal (#565) into a shared `security` module reused by both the LPE spawn gate and this TLS-root gate (DRY). - #660: set MissedTickBehavior::Delay on the daily leaf-rotation interval so a host sleep/suspend coalesces into one catch-up pass, never a burst. - #659: unit-test the rotation cadence, missed-tick behaviour, and the rotation action (fresh leaf untouched; leaf inside its 30-day window re-issued). - #693: bound each self-heal child spawn to a 120s timeout so a hung dig-updater/dig-installer child cannot stall future 6h passes; document the Tauri app-origin wallet-read CORS exposure in SPEC §4.3 (custody/sign stay token-gated; a route-scoped gate is not currently cheap). SemVer: minor (0.34.0 -> 0.35.0) — the #661 owner gate is a new backwards-compatible capability; the rest are hardening + tests. Closes #661 Closes #660 Closes #659 Closes #693 Co-Authored-By: Claude <noreply@anthropic.com>
The spawn_process function spawned privileged siblings (dig-updater, dig-installer) without kill_on_drop(true), so on CHILD_TIMEOUT elapse the child process was orphaned instead of killed. The with_child_timeout doc comment and error message claimed the child was "cancelled" but it was actually still running. Added .kill_on_drop(true) to the Command builder so tokio terminates the child when the timeout future is dropped. Updated doc comments and error text to accurately state the child IS killed on timeout. Fixes accuracy (accuracy/LOW per gate agents) and prevents slow orphan accumulation under wedged dependencies. Not a vulnerability (trusted absolute-path siblings, hang not attacker-triggerable), but hardens the implementation. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Batches four cohesive follow-ups from the #624 (v0.33.0 HTTPS/TLS) and #42 (v0.34.0 CORS/self-heal) arcs, all in
crates/dig-node-service(tls.rs+self_heal.rs+ a new sharedsecurity.rs+ SPEC).Closes #661
Closes #660
Closes #659
Closes #693
Fixes + tests
load_https_materialreads the leaf (and, on the rotation path,ca.key), it verifies the TLS root dir is owned by a privileged principal (Windows: well-knownSYSTEMS-1-5-18/AdministratorsS-1-5-32-544SID by exact equality; unix:root+ not group/world-writable) and fails closed to plaintext otherwise. A user-writable root could hold an attacker-swappedca.keythis privileged service would sign with.read_owner_sid_string/windows_owner_is_privileged/owner_sid_is_privilegedviaGetNamedSecurityInfoW+ConvertSidToStringSidW) fromself_heal.rsinto a new sharedcrates/dig-node-service/src/security.rs(dir_is_privileged), reused by BOTH the self-heal LPE spawn gate (#565) and this new TLS-root gate. The Win32 tests moved with it.tls::tests::load_refuses_when_the_tls_root_is_not_privileged_owned(a provisioned-but-user-owned tempdir →None).Delay.spawn_leaf_rotation's daily interval now usesMissedTickBehavior::Delay(vianew_renewal_ticker), so a host sleep/suspend across intervals coalesces into ONE catch-up pass instead of a redundant burst.tls::tests::renewal_ticker_uses_delay_missed_tick_behavior(paused-clock; counts exactly 1 immediate catch-up tick after a 3-interval stall vsBurst's 3).run_rotation_pass(manager, now)(behaviour-preserving) so the rotation action is testable without a day's wait.rotation_pass_leaves_a_fresh_leaf_untouchedandrotation_pass_reissues_a_leaf_inside_its_renewal_window(clock +61d past the 90d−30d window → leaf re-issued on disk).CHILD_TIMEOUT= 120s) viawith_child_timeout, applied to both the kick spawns and thechannel getread, so a hungdig-updater/dig-installerchild can never stall future 6h passes. Test:self_heal::tests::kick_times_out_a_hung_child_so_the_pass_never_blocks(paused-clock hung child →SpawnFailed)./wsnever reflects desktop-app origins). A route/method-scoped CORS gate is not currently cheap (one router-wideCorsLayer;POST /multiplexes content + wallet reads by method), so it is documented rather than enforced, with the future split noted.Version
minor: 0.34.0 → 0.35.0 — the #661 owner gate is a new backwards-compatible capability (
feat); the rest are hardening + tests. Root[workspace.package].versionbumped (the releaseddig-nodebinary tracks it).Verification
cargo test -p dig-node-service --lib→ 189 passed; 0 failed (incl. the 5 new tests).cargo fmt --all -- --checkclean;cargo clippy -p dig-node-service --all-targets --all-features -- -D warningsclean.tokiotest-utildev-dependency for the paused-clock tests.gitnexus blast radius
spawn_leaf_rotation/load_https_material— sole callerserver::bring_up_local_https; signatures unchanged. Owner gate returnsNone(fail-soft), same type the caller already handles.dir_is_privilegedextraction — call sites:self_heal::resolve_privileged_sibling_in(unchanged behaviour) + newtls::load_https_material. Win32 FFI unchanged (byte-moved).spawn_process— now wrapped bywith_child_timeoutat both call sites; return type unchanged.Do NOT merge — orchestrator re-gates (#661 + #693 security-adjacent → adversarial + loop-security) then squash-merges + releases (nightlies manual stable dispatch) + bumps the pointer.
🤖 Generated with Claude Code