mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-10-12 01:58:46 +02:00
dns_intercept: don't write a GP sibling on failed NRPT handback
A failed handback restored ctrld's rule via addNRPTCatchAllRule, which also writes the GP-path rule whenever another GP rule exists - leaving CtrldCatchAll beside the administrator's catch-all. Restore only the local store while that child is still present.
This commit is contained in:
5 files changed
+586
-92
No files matched your search
@@ -100,9 +100,14 @@ The **Name Resolution Policy Table** is a Windows feature (originally for Direct
|
||||
| `ConfigOptions` | REG_DWORD | `0x8` | Standard DNS resolution (no DirectAccess) |
|
||||
| `Version` | REG_DWORD | `0x2` | NRPT rule version 2 |
|
||||
|
||||
**Registry path**: `HKLM\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\DnsPolicyConfig\CtrldCatchAll`
|
||||
**Registry path**: the primary key is the local store, written on every start:
|
||||
`HKLM\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters\DnsPolicyConfig\{B2E9A3C1-7F4D-4A8E-9D6B-5C1E0F3A2B8D}`.
|
||||
The GP-store key
|
||||
`HKLM\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\DnsPolicyConfig\CtrldCatchAll`
|
||||
is written only when foreign GP rules would otherwise hide the local store, and never when
|
||||
the foreign rule is an administrator catch-all for this listener.
|
||||
|
||||
**Group Policy refresh**: The DNS Client service only reads NRPT from registry during Group Policy processing cycles (default: every 90 minutes). ctrld calls `RefreshPolicyEx(bMachine=TRUE, dwOptions=RP_FORCE)` when activating or repairing rules it owns. While Group Policy remains the owner, ctrld does not run NRPT activation/heal signaling; the one transition that removes a ctrld fallback is signaled after the external rule has been proven.
|
||||
**Group Policy refresh**: The DNS Client service only reads NRPT from registry during Group Policy processing cycles (default: every 90 minutes). ctrld calls `RefreshPolicyEx(bMachine=TRUE, dwOptions=RP_FORCE)` when activating or repairing rules it owns. While Group Policy remains the owner, ctrld does not run NRPT activation/heal signaling. For the transition that removes a ctrld fallback, ctrld signals once after removing its keys and then probes. If the proof finds no route, ctrld writes back only its local key and signals again.
|
||||
|
||||
#### GP-managed NRPT ownership
|
||||
|
||||
@@ -120,7 +125,7 @@ The health monitor keeps using functional probes:
|
||||
- matching GP rule + failed probe: retry loopback WFP protection, then report the external policy as ineffective without running NRPT heal signals;
|
||||
- matching GP rule disappears: create the normal ctrld-owned fallback and verify it, unless another GP catch-all targets a different resolver;
|
||||
- GP catch-all targets another resolver: report the conflict and do not create a second ambiguous catch-all;
|
||||
- matching GP rule returns: prove it with a probe, remove only ctrld's deterministic fallback keys, and return ownership to Group Policy.
|
||||
- matching GP rule returns: prove it with a probe, remove only ctrld's deterministic fallback keys, and return ownership to Group Policy. If the proof finds no route while the same rule stays on disk, ctrld puts back only its local-store key, keeps ownership, and does not write its GP-store rule adjacent to the administrator's rule.
|
||||
|
||||
Deploy the GPO **before** starting or restarting ctrld if adapter DNS must remain completely untouched. Remove or unlink the GP rule before intentionally removing the ctrld service. A GP catch-all that remains pointed at loopback while no listener is running causes DNS failure by design; ctrld cannot safely delete an administrator-owned policy during uninstall.
|
||||
|
||||
@@ -174,7 +179,7 @@ A dedicated background goroutine (`nrptHealthMonitor`) runs every 30 seconds and
|
||||
|
||||
1. **Ownership check:** Distinguish a matching external GP child from ctrld's deterministic local/GP keys.
|
||||
2. **Active probe:** Verify Windows DNS Client still routes to the listener.
|
||||
3. **Transition:** If the external child disappears, activate ctrld's normal fallback. If it returns while the fallback is active, prove it before removing only ctrld's keys.
|
||||
3. **Transition:** If the external child disappears, activate ctrld's normal fallback. If it returns while the fallback is active, prove it before removing only ctrld's keys. If the proof finds no route while the same rule stays on disk, ctrld puts back only its local-store key, keeps ownership, and does not write its GP-store rule adjacent to the administrator's rule.
|
||||
4. **Owned recovery:** Restore/heal only when ctrld owns the NRPT rule.
|
||||
5. **(hard mode)** Verify the WFP sublayer exists and fully restart intercept state on loss.
|
||||
|
||||
@@ -235,7 +240,8 @@ DNS intercept: pf redirect active — all outbound DNS (port 53) redirected to 1
|
||||
### Windows
|
||||
|
||||
```powershell
|
||||
# Check NRPT rules (should show CtrldCatchAll with . → 127.0.0.1)
|
||||
# Check NRPT rules (should show ctrld's catch-all, . → 127.0.0.1; the primary key is the
|
||||
# local-store GUID rule, and CtrldCatchAll appears only when ctrld also wrote the GP store)
|
||||
Get-DnsClientNrptRule
|
||||
|
||||
# Check NRPT registry directly
|
||||
|
||||
@@ -82,7 +82,9 @@ ctrld uses an adaptive strategy (matching [Tailscale's approach](https://github.
|
||||
on all non-domain machines.
|
||||
2. **Check if other software has GP NRPT rules** (`otherGPRulesExist()`). If
|
||||
foreign GP rules are present (IT policy, VPN), DNS Client is already in GP mode
|
||||
and our local rule would be invisible — so we also write to the GP path.
|
||||
and our local rule would be invisible — so we also write to the GP path. ctrld does
|
||||
not write to the GP path when the foreign rule is an administrator catch-all for this
|
||||
listener: a second catch-all in that store is never written, whatever the cost.
|
||||
3. **If no foreign GP rules exist**, clean any stale ctrld GP rules and delete
|
||||
the empty GP parent key. This ensures DNS Client stays in "local mode" where
|
||||
the local-path rule activates immediately via `paramchange`.
|
||||
@@ -100,7 +102,7 @@ When the rule remains unchanged and the probe arrives, Group Policy owns NRPT:
|
||||
- `RefreshPolicyEx`, Dnscache `paramchange`, and cache flush are not used for that policy;
|
||||
- shutdown/uninstall leave the GP child untouched.
|
||||
|
||||
A matching GP child that remains present but fails its probe is reported as ineffective and is not rewritten. If the child disappears, ctrld activates its normal owned fallback. A GP catch-all that instead changes to another resolver is reported as a conflict; ctrld does not create a second ambiguous catch-all. If the matching rule later returns, ctrld probes first, removes only its deterministic fallback keys, signals the ownership transition once, and resumes observing Group Policy.
|
||||
A matching GP child that remains present but fails its probe is reported as ineffective and is not rewritten. If the proof finds no route while the same rule stays on disk, ctrld puts back only its local-store key, keeps ownership, and does not write its GP-store rule adjacent to the administrator's rule. If the child disappears, ctrld activates its normal owned fallback. A GP catch-all that instead changes to another resolver is reported as a conflict; ctrld does not create a second ambiguous catch-all. If the matching rule later returns, ctrld probes first, removes only its deterministic fallback keys, signals the ownership transition once, and resumes observing Group Policy.
|
||||
|
||||
**Deployment ordering:** apply the GPO before starting ctrld to guarantee adapter DNS is never reset. Remove/unlink it before intentionally stopping or uninstalling ctrld. A GP catch-all still targeting loopback with no listener running is a deliberate fail-closed state and will break DNS.
|
||||
|
||||
@@ -304,6 +306,8 @@ Every 30s:
|
||||
│
|
||||
├─ ctrld-owned owner
|
||||
│ ├─ Working matching GP child returns → remove only ctrld keys; adopt GP
|
||||
│ │ └─ Probe finds no route, rule still on disk → write back only the
|
||||
│ │ local-store key, keep ownership, write no GP sibling
|
||||
│ ├─ ctrld key missing → restore + signal + verify
|
||||
│ └─ ctrld key present → probe; run owned heal sequence on failure
|
||||
│
|
||||
|
||||
Reference in new issue
Block a user