1
1
package dns
2
2
3
3
import (
4
+ "errors"
4
5
"fmt"
5
6
"io"
6
7
"strings"
8
+ "syscall"
7
9
10
+ "github.com/hashicorp/go-multierror"
8
11
log "github.com/sirupsen/logrus"
9
12
"golang.org/x/sys/windows/registry"
10
13
14
+ nberrors "github.com/netbirdio/netbird/client/errors"
11
15
"github.com/netbirdio/netbird/client/internal/statemanager"
12
16
)
13
17
18
+ var (
19
+ userenv = syscall .NewLazyDLL ("userenv.dll" )
20
+
21
+ // https://learn.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-refreshpolicyex
22
+ refreshPolicyExFn = userenv .NewProc ("RefreshPolicyEx" )
23
+ )
24
+
14
25
const (
15
- dnsPolicyConfigMatchPath = `SYSTEM\CurrentControlSet\Services\Dnscache\Parameters\DnsPolicyConfig\NetBird-Match`
26
+ dnsPolicyConfigMatchPath = `SYSTEM\CurrentControlSet\Services\Dnscache\Parameters\DnsPolicyConfig\NetBird-Match`
27
+ gpoDnsPolicyRoot = `SOFTWARE\Policies\Microsoft\Windows NT\DNSClient`
28
+ gpoDnsPolicyConfigMatchPath = gpoDnsPolicyRoot + `\DnsPolicyConfig\NetBird-Match`
29
+
16
30
dnsPolicyConfigVersionKey = "Version"
17
31
dnsPolicyConfigVersionValue = 2
18
32
dnsPolicyConfigNameKey = "Name"
19
33
dnsPolicyConfigGenericDNSServersKey = "GenericDNSServers"
20
34
dnsPolicyConfigConfigOptionsKey = "ConfigOptions"
21
35
dnsPolicyConfigConfigOptionsValue = 0x8
22
- )
23
36
24
- const (
25
37
interfaceConfigPath = `SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces`
26
38
interfaceConfigNameServerKey = "NameServer"
27
39
interfaceConfigSearchListKey = "SearchList"
40
+
41
+ // RP_FORCE: Reapply all policies even if no policy change was detected
42
+ rpForce = 0x1
28
43
)
29
44
30
45
type registryConfigurator struct {
31
46
guid string
32
47
routingAll bool
48
+ gpo bool
33
49
}
34
50
35
51
func newHostManager (wgInterface WGIface ) (* registryConfigurator , error ) {
36
52
guid , err := wgInterface .GetInterfaceGUIDString ()
37
53
if err != nil {
38
54
return nil , err
39
55
}
40
- return newHostManagerWithGuid (guid )
41
- }
42
56
43
- func newHostManagerWithGuid (guid string ) (* registryConfigurator , error ) {
57
+ var useGPO bool
58
+ k , err := registry .OpenKey (registry .LOCAL_MACHINE , gpoDnsPolicyRoot , registry .QUERY_VALUE )
59
+ if err != nil {
60
+ log .Debugf ("failed to open GPO DNS policy root: %v" , err )
61
+ } else {
62
+ closer (k )
63
+ useGPO = true
64
+ log .Infof ("detected GPO DNS policy configuration, using policy store" )
65
+ }
66
+
44
67
return & registryConfigurator {
45
68
guid : guid ,
69
+ gpo : useGPO ,
46
70
}, nil
47
71
}
48
72
@@ -51,30 +75,23 @@ func (r *registryConfigurator) supportCustomPort() bool {
51
75
}
52
76
53
77
func (r * registryConfigurator ) applyDNSConfig (config HostDNSConfig , stateManager * statemanager.Manager ) error {
54
- var err error
55
78
if config .RouteAll {
56
- err = r .addDNSSetupForAll (config .ServerIP )
57
- if err != nil {
79
+ if err := r .addDNSSetupForAll (config .ServerIP ); err != nil {
58
80
return fmt .Errorf ("add dns setup: %w" , err )
59
81
}
60
82
} else if r .routingAll {
61
- err = r .deleteInterfaceRegistryKeyProperty (interfaceConfigNameServerKey )
62
- if err != nil {
83
+ if err := r .deleteInterfaceRegistryKeyProperty (interfaceConfigNameServerKey ); err != nil {
63
84
return fmt .Errorf ("delete interface registry key property: %w" , err )
64
85
}
65
86
r .routingAll = false
66
87
log .Infof ("removed %s as main DNS forwarder for this peer" , config .ServerIP )
67
88
}
68
89
69
- if err := stateManager .UpdateState (& ShutdownState {Guid : r .guid }); err != nil {
90
+ if err := stateManager .UpdateState (& ShutdownState {Guid : r .guid , GPO : r . gpo }); err != nil {
70
91
log .Errorf ("failed to update shutdown state: %s" , err )
71
92
}
72
93
73
- var (
74
- searchDomains []string
75
- matchDomains []string
76
- )
77
-
94
+ var searchDomains , matchDomains []string
78
95
for _ , dConf := range config .Domains {
79
96
if dConf .Disabled {
80
97
continue
@@ -86,91 +103,80 @@ func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager
86
103
}
87
104
88
105
if len (matchDomains ) != 0 {
89
- err = r .addDNSMatchPolicy (matchDomains , config .ServerIP )
106
+ if err := r .addDNSMatchPolicy (matchDomains , config .ServerIP ); err != nil {
107
+ return fmt .Errorf ("add dns match policy: %w" , err )
108
+ }
90
109
} else {
91
- err = removeRegistryKeyFromDNSPolicyConfig (dnsPolicyConfigMatchPath )
92
- }
93
- if err != nil {
94
- return fmt .Errorf ("add dns match policy: %w" , err )
110
+ if err := r .removeDNSMatchPolicies (); err != nil {
111
+ return fmt .Errorf ("remove dns match policies: %w" , err )
112
+ }
95
113
}
96
114
97
- err = r .updateSearchDomains (searchDomains )
98
- if err != nil {
115
+ if err := r .updateSearchDomains (searchDomains ); err != nil {
99
116
return fmt .Errorf ("update search domains: %w" , err )
100
117
}
101
118
102
119
return nil
103
120
}
104
121
105
122
func (r * registryConfigurator ) addDNSSetupForAll (ip string ) error {
106
- err := r .setInterfaceRegistryKeyStringValue (interfaceConfigNameServerKey , ip )
107
- if err != nil {
108
- return fmt .Errorf ("adding dns setup for all failed with error: %w" , err )
123
+ if err := r .setInterfaceRegistryKeyStringValue (interfaceConfigNameServerKey , ip ); err != nil {
124
+ return fmt .Errorf ("adding dns setup for all failed: %w" , err )
109
125
}
110
126
r .routingAll = true
111
127
log .Infof ("configured %s:53 as main DNS forwarder for this peer" , ip )
112
128
return nil
113
129
}
114
130
115
131
func (r * registryConfigurator ) addDNSMatchPolicy (domains []string , ip string ) error {
116
- _ , err := registry .OpenKey (registry .LOCAL_MACHINE , dnsPolicyConfigMatchPath , registry .QUERY_VALUE )
117
- if err == nil {
118
- err = registry .DeleteKey (registry .LOCAL_MACHINE , dnsPolicyConfigMatchPath )
119
- if err != nil {
120
- return fmt .Errorf ("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\ %s, error: %w" , dnsPolicyConfigMatchPath , err )
121
- }
132
+ // if the gpo key is present, we need to put our DNS settings there, otherwise our config might be ignored
133
+ // see https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpnrpt/8cc31cb9-20cb-4140-9e85-3e08703b4745
134
+ policyPath := dnsPolicyConfigMatchPath
135
+ if r .gpo {
136
+ policyPath = gpoDnsPolicyConfigMatchPath
122
137
}
123
138
124
- regKey , _ , err := registry .CreateKey (registry .LOCAL_MACHINE , dnsPolicyConfigMatchPath , registry .SET_VALUE )
125
- if err != nil {
126
- return fmt .Errorf ("unable to create registry key, key: HKEY_LOCAL_MACHINE\\ %s, error: %w" , dnsPolicyConfigMatchPath , err )
139
+ if err := removeRegistryKeyFromDNSPolicyConfig (policyPath ); err != nil {
140
+ return fmt .Errorf ("remove existing dns policy: %w" , err )
127
141
}
128
142
129
- err = regKey . SetDWordValue ( dnsPolicyConfigVersionKey , dnsPolicyConfigVersionValue )
143
+ regKey , _ , err := registry . CreateKey ( registry . LOCAL_MACHINE , policyPath , registry . SET_VALUE )
130
144
if err != nil {
131
- return fmt .Errorf ("unable to set registry value for %s, error : %w" , dnsPolicyConfigVersionKey , err )
145
+ return fmt .Errorf ("create registry key HKEY_LOCAL_MACHINE \\ %s : %w" , policyPath , err )
132
146
}
147
+ defer closer (regKey )
133
148
134
- err = regKey .SetStringsValue (dnsPolicyConfigNameKey , domains )
135
- if err != nil {
136
- return fmt .Errorf ("unable to set registry value for %s, error: %w" , dnsPolicyConfigNameKey , err )
149
+ if err := regKey .SetDWordValue (dnsPolicyConfigVersionKey , dnsPolicyConfigVersionValue ); err != nil {
150
+ return fmt .Errorf ("set %s: %w" , dnsPolicyConfigVersionKey , err )
137
151
}
138
152
139
- err = regKey .SetStringValue (dnsPolicyConfigGenericDNSServersKey , ip )
140
- if err != nil {
141
- return fmt .Errorf ("unable to set registry value for %s, error: %w" , dnsPolicyConfigGenericDNSServersKey , err )
153
+ if err := regKey .SetStringsValue (dnsPolicyConfigNameKey , domains ); err != nil {
154
+ return fmt .Errorf ("set %s: %w" , dnsPolicyConfigNameKey , err )
142
155
}
143
156
144
- err = regKey .SetDWordValue (dnsPolicyConfigConfigOptionsKey , dnsPolicyConfigConfigOptionsValue )
145
- if err != nil {
146
- return fmt .Errorf ("unable to set registry value for %s, error: %w" , dnsPolicyConfigConfigOptionsKey , err )
157
+ if err := regKey .SetStringValue (dnsPolicyConfigGenericDNSServersKey , ip ); err != nil {
158
+ return fmt .Errorf ("set %s: %w" , dnsPolicyConfigGenericDNSServersKey , err )
147
159
}
148
160
149
- log .Infof ("added %d match domains to the state. Domain list: %s" , len (domains ), domains )
150
-
151
- return nil
152
- }
153
-
154
- func (r * registryConfigurator ) restoreHostDNS () error {
155
- if err := removeRegistryKeyFromDNSPolicyConfig (dnsPolicyConfigMatchPath ); err != nil {
156
- log .Errorf ("remove registry key from dns policy config: %s" , err )
161
+ if err := regKey .SetDWordValue (dnsPolicyConfigConfigOptionsKey , dnsPolicyConfigConfigOptionsValue ); err != nil {
162
+ return fmt .Errorf ("set %s: %w" , dnsPolicyConfigConfigOptionsKey , err )
157
163
}
158
164
159
- if err := r .deleteInterfaceRegistryKeyProperty (interfaceConfigSearchListKey ); err != nil {
160
- return fmt .Errorf ("remove interface registry key: %w" , err )
165
+ if r .gpo {
166
+ if err := refreshGroupPolicy (); err != nil {
167
+ log .Warnf ("failed to refresh group policy: %v" , err )
168
+ }
161
169
}
162
170
171
+ log .Infof ("added %d match domains. Domain list: %s" , len (domains ), domains )
163
172
return nil
164
173
}
165
174
166
175
func (r * registryConfigurator ) updateSearchDomains (domains []string ) error {
167
- err := r .setInterfaceRegistryKeyStringValue (interfaceConfigSearchListKey , strings .Join (domains , "," ))
168
- if err != nil {
169
- return fmt .Errorf ("adding search domain failed with error: %w" , err )
176
+ if err := r .setInterfaceRegistryKeyStringValue (interfaceConfigSearchListKey , strings .Join (domains , "," )); err != nil {
177
+ return fmt .Errorf ("update search domains: %w" , err )
170
178
}
171
-
172
- log .Infof ("updated the search domains in the registry with %d domains. Domain list: %s" , len (domains ), domains )
173
-
179
+ log .Infof ("updated search domains: %s" , domains )
174
180
return nil
175
181
}
176
182
@@ -181,11 +187,9 @@ func (r *registryConfigurator) setInterfaceRegistryKeyStringValue(key, value str
181
187
}
182
188
defer closer (regKey )
183
189
184
- err = regKey .SetStringValue (key , value )
185
- if err != nil {
186
- return fmt .Errorf ("applying key %s with value \" %s\" for interface failed with error: %w" , key , value , err )
190
+ if err := regKey .SetStringValue (key , value ); err != nil {
191
+ return fmt .Errorf ("set key %s=%s: %w" , key , value , err )
187
192
}
188
-
189
193
return nil
190
194
}
191
195
@@ -196,43 +200,91 @@ func (r *registryConfigurator) deleteInterfaceRegistryKeyProperty(propertyKey st
196
200
}
197
201
defer closer (regKey )
198
202
199
- err = regKey .DeleteValue (propertyKey )
200
- if err != nil {
201
- return fmt .Errorf ("deleting registry key %s for interface failed with error: %w" , propertyKey , err )
203
+ if err := regKey .DeleteValue (propertyKey ); err != nil {
204
+ return fmt .Errorf ("delete registry key %s: %w" , propertyKey , err )
202
205
}
203
-
204
206
return nil
205
207
}
206
208
207
209
func (r * registryConfigurator ) getInterfaceRegistryKey () (registry.Key , error ) {
208
- var regKey registry.Key
209
-
210
210
regKeyPath := interfaceConfigPath + "\\ " + r .guid
211
-
212
211
regKey , err := registry .OpenKey (registry .LOCAL_MACHINE , regKeyPath , registry .SET_VALUE )
213
212
if err != nil {
214
- return regKey , fmt .Errorf ("unable to open the interface registry key, key: HKEY_LOCAL_MACHINE\\ %s, error : %w" , regKeyPath , err )
213
+ return regKey , fmt .Errorf ("open HKEY_LOCAL_MACHINE\\ %s: %w" , regKeyPath , err )
215
214
}
216
-
217
215
return regKey , nil
218
216
}
219
217
220
- func (r * registryConfigurator ) restoreUncleanShutdownDNS () error {
221
- if err := r .restoreHostDNS (); err != nil {
222
- return fmt .Errorf ("restoring dns via registry: %w" , err )
218
+ func (r * registryConfigurator ) restoreHostDNS () error {
219
+ if err := r .removeDNSMatchPolicies (); err != nil {
220
+ log .Errorf ("remove dns match policies: %s" , err )
221
+ }
222
+
223
+ if err := r .deleteInterfaceRegistryKeyProperty (interfaceConfigSearchListKey ); err != nil {
224
+ return fmt .Errorf ("remove interface registry key: %w" , err )
223
225
}
226
+
224
227
return nil
225
228
}
226
229
230
+ func (r * registryConfigurator ) removeDNSMatchPolicies () error {
231
+ var merr * multierror.Error
232
+ if err := removeRegistryKeyFromDNSPolicyConfig (dnsPolicyConfigMatchPath ); err != nil {
233
+ merr = multierror .Append (merr , fmt .Errorf ("remove local registry key: %w" , err ))
234
+ }
235
+
236
+ if err := removeRegistryKeyFromDNSPolicyConfig (gpoDnsPolicyConfigMatchPath ); err != nil {
237
+ merr = multierror .Append (merr , fmt .Errorf ("remove GPO registry key: %w" , err ))
238
+ }
239
+
240
+ if err := refreshGroupPolicy (); err != nil {
241
+ merr = multierror .Append (merr , fmt .Errorf ("refresh group policy: %w" , err ))
242
+ }
243
+
244
+ return nberrors .FormatErrorOrNil (merr )
245
+ }
246
+
247
+ func (r * registryConfigurator ) restoreUncleanShutdownDNS () error {
248
+ return r .restoreHostDNS ()
249
+ }
250
+
227
251
func removeRegistryKeyFromDNSPolicyConfig (regKeyPath string ) error {
228
252
k , err := registry .OpenKey (registry .LOCAL_MACHINE , regKeyPath , registry .QUERY_VALUE )
229
- if err == nil {
230
- defer closer (k )
231
- err = registry .DeleteKey (registry .LOCAL_MACHINE , regKeyPath )
232
- if err != nil {
233
- return fmt .Errorf ("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\ %s, error: %w" , regKeyPath , err )
253
+ if err != nil {
254
+ log .Debugf ("failed to open HKEY_LOCAL_MACHINE\\ %s: %v" , regKeyPath , err )
255
+ return nil
256
+ }
257
+
258
+ closer (k )
259
+ if err := registry .DeleteKey (registry .LOCAL_MACHINE , regKeyPath ); err != nil {
260
+ return fmt .Errorf ("delete HKEY_LOCAL_MACHINE\\ %s: %w" , regKeyPath , err )
261
+ }
262
+
263
+ return nil
264
+ }
265
+
266
+ func refreshGroupPolicy () error {
267
+ // refreshPolicyExFn.Call() panics if the func is not found
268
+ defer func () {
269
+ if r := recover (); r != nil {
270
+ log .Errorf ("Recovered from panic: %v" , r )
234
271
}
272
+ }()
273
+
274
+ ret , _ , err := refreshPolicyExFn .Call (
275
+ // bMachine = TRUE (computer policy)
276
+ uintptr (1 ),
277
+ // dwOptions = RP_FORCE
278
+ uintptr (rpForce ),
279
+ )
280
+
281
+ if ret == 0 {
282
+ if err != nil && ! errors .Is (err , syscall .Errno (0 )) {
283
+ return fmt .Errorf ("RefreshPolicyEx failed: %w" , err )
284
+ }
285
+ return fmt .Errorf ("RefreshPolicyEx failed" )
235
286
}
287
+
236
288
return nil
237
289
}
238
290
0 commit comments