net/dns, util/winutil: improve detection of group policy affecting NRPT
Due to a customer issue, I investigated the Windows Dnscache service more intensively. I learned that the only time it attempts to read the NRPT from group policy is in response to a group policy change notification. Under the hypothesis that policy refresh is not effectively delivering GP notifications due to its dependency on reaching a DC, I replaced our use of the RefreshPolicyEx with the quasi-documented GenerateGPNotification API. Tests have been updated to ensure they check that they are running as LocalSystem, which is required for GenerateGPNotification. Fixes #20187 Signed-off-by: Aaron Klotz <aaron@tailscale.com>
This commit is contained in:
@@ -85,23 +85,23 @@ func TestHostFileChanged(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestManagerWindowsLocal(t *testing.T) {
|
||||
if !isWindows10OrBetter() || !winutil.IsCurrentProcessElevated() {
|
||||
t.Skipf("test requires running as elevated user on Windows 10+")
|
||||
if !winutil.IsCurrentProcessLocalSystem() {
|
||||
t.Skipf("test requires running as LocalSystem on Windows 10+")
|
||||
}
|
||||
|
||||
runTest(t, true)
|
||||
}
|
||||
|
||||
func TestManagerWindowsGP(t *testing.T) {
|
||||
if !isWindows10OrBetter() || !winutil.IsCurrentProcessElevated() {
|
||||
t.Skipf("test requires running as elevated user on Windows 10+")
|
||||
if !winutil.IsCurrentProcessLocalSystem() {
|
||||
t.Skipf("test requires running as LocalSystem on Windows 10+")
|
||||
}
|
||||
|
||||
checkGPNotificationsWork(t)
|
||||
|
||||
// Make sure group policy is refreshed before this test exits but after we've
|
||||
// Make sure group policy is current before this test exits but after we've
|
||||
// cleaned everything else up.
|
||||
defer gp.RefreshMachinePolicy(true)
|
||||
defer gp.NotifyMachinePolicyChange()
|
||||
|
||||
err := createFakeGPKey()
|
||||
if err != nil {
|
||||
@@ -113,8 +113,8 @@ func TestManagerWindowsGP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestManagerWindowsGPCopy(t *testing.T) {
|
||||
if !isWindows10OrBetter() || !winutil.IsCurrentProcessElevated() {
|
||||
t.Skipf("test requires running as elevated user on Windows 10+")
|
||||
if !winutil.IsCurrentProcessLocalSystem() {
|
||||
t.Skipf("test requires running as LocalSystem on Windows 10+")
|
||||
}
|
||||
|
||||
checkGPNotificationsWork(t)
|
||||
@@ -179,9 +179,9 @@ func TestManagerWindowsGPCopy(t *testing.T) {
|
||||
t.Fatalf("regWatcher.watch: %v\n", err)
|
||||
}
|
||||
|
||||
err = gp.RefreshMachinePolicy(true)
|
||||
err = gp.NotifyMachinePolicyChange()
|
||||
if err != nil {
|
||||
t.Fatalf("testDoRefresh: %v\n", err)
|
||||
t.Fatalf("NotifyMachinePolicyChange: %v\n", err)
|
||||
}
|
||||
|
||||
err = regWatcher.wait()
|
||||
@@ -203,9 +203,9 @@ func TestManagerWindowsGPCopy(t *testing.T) {
|
||||
t.Fatalf("regWatcher.watch: %v\n", err)
|
||||
}
|
||||
|
||||
err = gp.RefreshMachinePolicy(true)
|
||||
err = gp.NotifyMachinePolicyChange()
|
||||
if err != nil {
|
||||
t.Fatalf("testDoRefresh: %v\n", err)
|
||||
t.Fatalf("NotifyMachinePolicyChange: %v\n", err)
|
||||
}
|
||||
|
||||
err = regWatcher.wait()
|
||||
@@ -236,13 +236,13 @@ func checkGPNotificationsWork(t *testing.T) {
|
||||
}
|
||||
defer trk.Close()
|
||||
|
||||
err = gp.RefreshMachinePolicy(true)
|
||||
err = gp.NotifyMachinePolicyChange()
|
||||
if err != nil {
|
||||
t.Fatalf("RefreshPolicyEx error: %v\n", err)
|
||||
t.Fatalf("NotifyMachinePolicyChange error: %v\n", err)
|
||||
}
|
||||
|
||||
timeout := uint32(10000) // Milliseconds
|
||||
if !trk.DidRefreshTimeout(timeout) {
|
||||
if !trk.DidGroupPolicyChangeTimeout(timeout) {
|
||||
t.Skipf("GP notifications are not working on this machine\n")
|
||||
}
|
||||
}
|
||||
@@ -338,8 +338,8 @@ func runTest(t *testing.T, isLocal bool) {
|
||||
}
|
||||
validateRegistry(t, regBaseValidate, caseDomains)
|
||||
ensureNoRulesInSubkey(t, regBaseEnsure)
|
||||
if !isLocal && !trk.DidRefresh(true) {
|
||||
t.Fatalf("DidRefresh false, want true\n")
|
||||
if !isLocal && !trk.DidGroupPolicyChange(true) {
|
||||
t.Fatalf("DidGroupPolicyChange false, want true\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,10 +347,6 @@ func runTest(t *testing.T, isLocal bool) {
|
||||
runCase(n)
|
||||
}
|
||||
|
||||
if isLocal && trk.DidRefresh(false) {
|
||||
t.Errorf("DidRefresh true, want false\n")
|
||||
}
|
||||
|
||||
t.Logf("Test case: nil resolver\n")
|
||||
err = mgr.setSplitDNS(nil, domains)
|
||||
if err != nil {
|
||||
@@ -573,7 +569,7 @@ var (
|
||||
)
|
||||
|
||||
// gpNotificationTracker registers with the Windows policy engine and receives
|
||||
// notifications when policy refreshes occur.
|
||||
// notifications when policy change notifications occur.
|
||||
type gpNotificationTracker struct {
|
||||
event windows.Handle
|
||||
}
|
||||
@@ -602,7 +598,7 @@ func newGPNotificationTracker() (*gpNotificationTracker, error) {
|
||||
return &gpNotificationTracker{evt}, nil
|
||||
}
|
||||
|
||||
func (trk *gpNotificationTracker) DidRefresh(isExpected bool) bool {
|
||||
func (trk *gpNotificationTracker) DidGroupPolicyChange(isExpected bool) bool {
|
||||
// If we're not expecting a refresh event, then we need to use a timeout.
|
||||
timeout := uint32(1000) // 1 second (in milliseconds)
|
||||
if isExpected {
|
||||
@@ -610,10 +606,10 @@ func (trk *gpNotificationTracker) DidRefresh(isExpected bool) bool {
|
||||
timeout = windows.INFINITE
|
||||
}
|
||||
|
||||
return trk.DidRefreshTimeout(timeout)
|
||||
return trk.DidGroupPolicyChangeTimeout(timeout)
|
||||
}
|
||||
|
||||
func (trk *gpNotificationTracker) DidRefreshTimeout(timeout uint32) bool {
|
||||
func (trk *gpNotificationTracker) DidGroupPolicyChangeTimeout(timeout uint32) bool {
|
||||
waitCode, _ := windows.WaitForSingleObject(trk.event, timeout)
|
||||
return waitCode == windows.WAIT_OBJECT_0
|
||||
}
|
||||
@@ -625,6 +621,8 @@ func (trk *gpNotificationTracker) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
const dnsBaseGP = `SOFTWARE\Policies\Microsoft\Windows NT\DNSClient`
|
||||
|
||||
type regKeyWatcher struct {
|
||||
keyGP registry.Key
|
||||
evtGP windows.Handle
|
||||
|
||||
Reference in New Issue
Block a user