]>
Commit | Line | Data |
---|---|---|
5695ee50 LP |
1 | --- |
2 | title: systemd-resolved and VPNs | |
3 | category: Networking | |
4 | layout: default | |
0aff7b75 | 5 | SPDX-License-Identifier: LGPL-2.1-or-later |
5695ee50 LP |
6 | --- |
7 | ||
8 | # `systemd-resolved.service` and VPNs | |
9 | ||
10 | `systemd-resolved.service` supports routing lookups for specific domains to specific | |
11 | interfaces. This is useful for hooking up VPN software with systemd-resolved | |
12 | and making sure the exact right lookups end up on the VPN and on the other | |
13 | interfaces. | |
14 | ||
15 | For a verbose explanation of `systemd-resolved.service`'s domain routing logic, | |
16 | see its [man | |
17 | page](https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html). This | |
18 | document is supposed to provide examples to use the concepts for the specific | |
19 | purpose of managing VPN DNS configuration. | |
20 | ||
21 | Let's first define two distinct VPN use-cases: | |
22 | ||
23 | 1. *Corporate* VPNs, i.e. VPNs that open access to a specific set of additional | |
24 | hosts. Only specific domains should be resolved via the VPN's DNS servers, | |
25 | and everything that is not related to the company's domain names should go | |
26 | to regular, non-VPN DNS instead. | |
27 | ||
28 | 2. *Privacy* VPNs, i.e. VPNs that should be used for basically all DNS traffic, | |
29 | once they are up. If this type of VPN is used, any regular, non-VPN DNS | |
30 | servers should not get any traffic anymore. | |
31 | ||
32 | Then, let's briefly introduce three DNS routing concepts that software managing | |
33 | a network interface may configure. | |
34 | ||
35 | 1. Search domains: these are traditional DNS configuration parameters and are | |
36 | used to suffix non-qualified domain names (i.e. single-label ones), to turn | |
37 | them into fully qualified domain names. Traditionally (before | |
38 | `systemd-resolved.service`), search domain names are attached to a system's | |
39 | IP configuration as a whole, in `systemd-resolved.service` they are | |
40 | associated to individual interfaces instead, since they are typically | |
41 | acquired through some network associated concept, such as a DHCP, IPv6RA or | |
42 | PPP lease. Most importantly though: in `systemd-resolved.service` they are | |
43 | not just used to suffix single-label domain names, but also for routing | |
44 | domain name lookups: if a network interface has a search domain `foo.com` | |
45 | configured on it, then any lookups for names ending in `.foo.com` (or for | |
46 | `foo.com` itself) are preferably routed to the DNS servers configured on the | |
47 | same network interface. | |
48 | ||
49 | 2. Routing domains: these are very similar to search domains, but are purely | |
50 | about DNS domain name lookup routing — they are not used for qualifying | |
121cb882 | 51 | single-label domain names. When it comes to routing, assigning a routing |
5695ee50 LP |
52 | domain to a network interface is identical to assigning a search domain to |
53 | it. | |
54 | ||
55 | Why the need to have both concepts, i.e. search *and* routing domains? | |
56 | Mostly because in many cases the qualifying of single-label names is not | |
121cb882 JF |
57 | desirable (as it has security implications), but needs to be supported for |
58 | specific use-cases. Routing domains are a concept `systemd-resolved.service` | |
5695ee50 LP |
59 | introduced, while search domains are traditionally available and are part of |
60 | DHCP/IPv6RA/PPP leases and thus universally supported. In many cases routing | |
61 | domains are probably the more appropriate concept, but not easily available, | |
121cb882 | 62 | since they are not part of DHCP/IPv6RA/PPP. |
5695ee50 LP |
63 | |
64 | Routing domains for `systemd-resolved.service` are usually presented along | |
65 | with search domains in mostly the same way, but prefixed with `~` to | |
66 | differentiate them. i.e. `~foo.com` is a configured routing domain, while | |
67 | `foo.com` would be a configured search domain. | |
68 | ||
f223fd6a | 69 | One routing domain is particularly interesting: `~.` — the catch-all routing |
5695ee50 LP |
70 | domain. (The *dot* domain `.` is how DNS denotes the "root" domain, i.e. the |
71 | parent domain of all domains, but itself.) When used on an interface any DNS | |
72 | traffic is preferably routed to its DNS servers. (A search domain – i.e. `.` | |
73 | instead of `~.` — would have the same effect, but given that it's mostly | |
74 | pointless to suffix an unqualified domain with `.`, we generally declare it | |
75 | as a routing domain, not a search domain). | |
76 | ||
77 | Routing domains also have particular relevance when it comes to the reverse | |
78 | lookup DNS domains `.in-addr.arpa` and `.ip6.arpa`. An interface that has | |
79 | these (or sub-domains thereof) defined as routing domains, will be preferably | |
80 | used for doing reverse IP to domain name lookups. e.g. declaring | |
81 | `~168.192.in-addr.arpa` on an interface means that all lookups to find the | |
121cb882 | 82 | domain names for IPv4 addresses 192.168.x.y are preferably routed to it. |
5695ee50 LP |
83 | |
84 | 3. The `default-route` boolean. This is a simple boolean value that may be set | |
85 | on an interface. If true (the default), any DNS lookups for which no | |
86 | matching routing or search domains are defined are routed to interfaces | |
87 | marked like this. If false then the DNS servers on this interface are not | |
88 | considered for routing lookups to except for the ones listed in the | |
89 | search/routing domain list. An interface that has no search/routing domain | |
90 | associated and also has this boolean off is not considered for *any* | |
91 | lookups. | |
92 | ||
93 | One more thing to mention: in `systemd-resolved.service` if lookups match the | |
94 | search/routing domains of multiple interfaces at once, then they are sent to | |
95 | all of them in parallel, and the first positive reply used. If all lookups fail | |
96 | the last negative reply is used. This means the DNS zones on the relevant | |
97 | interfaces are "merged": domains existing on one but not the other will "just | |
98 | work" and vice versa. | |
99 | ||
100 | And one more note: the domain routing logic implemented is a tiny bit more | |
101 | complex that what described above: if there two interfaces have search domains | |
102 | that are suffix of each other, and a name is looked up that matches both, the | |
103 | interface with the longer match will win and get the lookup routed to is DNS | |
104 | servers. Only if the match has the same length, then both will be used in | |
105 | parallel. Example: one interface has `~foo.example.com` as routing domain, and | |
106 | another one `example.com` has search domain. A lookup for | |
107 | `waldo.foo.example.com` is the exclusively routed to the first interface's DNS | |
108 | server, since it matches by three suffix labels instead of just two. The fact | |
109 | that the matching length is taken into consideration for the routing decision | |
110 | is particularly relevant if you have one interface with the `~.` routing domain | |
111 | and another one with `~corp.company.example` — both suffixes match a lookup for | |
112 | `foo.corp.company.example`, but the latter interface wins, since the match is | |
113 | for four labels, while the other is for zero labels. | |
114 | ||
818e46ae | 115 | ## Putting it Together |
5695ee50 LP |
116 | |
117 | Let's discuss how the three DNS routing concepts above are best used for a | |
118 | reasonably complex scenario consisting of: | |
119 | ||
120 | 1. One VPN interface of the *corporate* kind, maybe called `company0`. It makes | |
121 | available a bunch of servers, all in the domain `corp.company.example`. | |
122 | ||
123 | 2. One VPN interface of the *privacy* kind, maybe called `privacy0`. When it is | |
124 | up all DNS traffic shall preferably routed to its DNS servers. | |
125 | ||
126 | 3. One regular WiFi interface, maybe called `wifi0`. It has a regular DNS | |
127 | server on it. | |
128 | ||
129 | Here's how to best configure this for `systemd-resolved.service`: | |
130 | ||
131 | 1. `company0` should get a routing domain `~corp.company.example` | |
132 | configured. (A search domain `corp.company.example` would work too, if | |
133 | qualifying of single-label names is desired or the VPN lease information | |
134 | does not provide for the concept of routing domains, but does support search | |
135 | domains.) This interface should also set `default-route` to false, to ensure | |
136 | that really only the DNS lookups for the company's servers are routed there | |
137 | and nothing else. Finally, it might make sense to also configure a routing | |
138 | domain `~2.0.192.in-addr.arpa` on the interface, ensuring that all IPv4 | |
139 | addresses from the 192.0.2.x range are preferably resolved via the DNS | |
140 | server on this interface (assuming that that's the IPv4 address range the | |
141 | company uses internally). | |
142 | ||
143 | 2. `privacy0` should get a routing domain `~.` configured. The setting of | |
144 | `default-route` for this interface is then irrelevant. This means: once the | |
145 | interface is up, all DNS traffic is preferably routed there. | |
146 | ||
147 | 3. `wifi0` should not get any special settings, except possibly whatever the | |
148 | local WiFi router considers suitable as search domain, for example | |
149 | `fritz.box`. The default `true` setting for `default-route` is good too. | |
150 | ||
151 | With this configuration if only `wifi0` is up, all DNS traffic goes to its DNS | |
152 | server, since there are no other interfaces with better matching DNS | |
153 | configuration. If `privacy0` is then upped, all DNS traffic will exclusively go | |
154 | to this interface now — with the exception of names below the `fritz.box` | |
155 | domain, which will continue to go directly to `wifi0`, as the search domain | |
156 | there says so. Now, if `company0` is also upped, it will receive DNS traffic | |
157 | for the company's internal domain and internal IP subnet range, but nothing | |
158 | else. If `privacy0` is then downed again, `wifi0` will get the regular DNS | |
159 | traffic again, and `company0` will still get the company's internal domain and | |
160 | IP subnet traffic and nothing else. Everything hence works as intended. | |
161 | ||
818e46ae | 162 | ## How to Implement this in Your VPN Software |
5695ee50 LP |
163 | |
164 | Most likely you want to expose a boolean in some way that declares whether a | |
165 | specific VPN is of the *corporate* or the *privacy* kind: | |
166 | ||
167 | 1. If managing a *corporate* VPN, you configure any search domains the user or | |
168 | the VPN contact point provided. And you set `default-route` to false. If you | |
169 | have IP subnet information for the VPN, it might make sense to insert | |
170 | `~….in-addr.arpa` and `~….ip6.arpa` reverse lookup routing domains for it. | |
171 | ||
172 | 2. If managing a *privacy* VPN, you include `~.` in the routing domains, the | |
173 | value for `default-route` is actually irrelevant, but I'd set it to true. No | |
174 | need to configure any reverse lookup routing domains for it. | |
175 | ||
176 | (If you also manage regular WiFi/Ethernet devices, just configure them as | |
177 | traditional, i.e. with any search domains as acquired, do not set `~.` though, | |
178 | and do not disable `default-route`.) | |
179 | ||
818e46ae | 180 | ## The APIs |
5695ee50 LP |
181 | |
182 | Now we determined how we want to configure things, but how do you actually get | |
183 | the configuration to `systemd-resolved.service`? There are three relevant | |
184 | interfaces: | |
185 | ||
186 | 1. Ideally, you use D-Bus and talk to [`systemd-resolved.service`'s D-Bus | |
187 | API](https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html) | |
188 | directly. Use `SetLinkDomains()` to set the per-interface search and routing | |
189 | domains on the interfaces you manage, and `SetLinkDefaultRoute()` to manage | |
190 | the `default-route` boolean, all on the `org.freedesktop.resolve1.Manager` | |
191 | interface of the `/org/freedesktop/resolve1` object. | |
192 | ||
193 | 2. If that's not in the cards, you may shell out to | |
194 | [`resolvectl`](https://www.freedesktop.org/software/systemd/man/resolvectl.html), | |
195 | which is a thin wrapper around the D-Bus interface mentioned above. Use | |
196 | `resolvectl domain <iface> …` to set the search/routing domains and | |
197 | `resolvectl default-route <iface> …` to set the `default-route` boolean. | |
198 | ||
199 | Example use from a shell callout of your VPN software for a *corporate* VPN: | |
200 | ||
201 | resolvectl domain corporate0 '~corp-company.example' '~2.0.192.in-addr.arpa' | |
202 | resolvectl default-route corporate0 false | |
203 | resolvectl dns corporate0 192.0.2.1 | |
204 | ||
205 | Example use from a shell callout of your VPN software for a *privacy* VPN: | |
206 | ||
207 | resolvectl domain privacy0 '~.' | |
208 | resolvectl default-route privacy0 true | |
209 | resolvectl dns privacy0 8.8.8.8 | |
210 | ||
211 | 3. If you don't want to use any `systemd-resolved` commands, you may use the | |
212 | `resolvconf` wrapper we provide. `resolvectl` is actually a multi-call | |
213 | binary and may be symlinked to `resolvconf`, and when invoked like that | |
214 | behaves in a way that is largely compatible with FreeBSD's and | |
215 | Ubuntu's/Debian's | |
216 | [`resolvconf(8)`](https://manpages.ubuntu.com/manpages/trusty/man8/resolvconf.8.html) | |
217 | tool. When the `-x` switch is specified, the `~.` routing domain is | |
218 | automatically appended to the domain list configured, as appropriate for a | |
219 | *privacy* VPN. Note that the `resolvconf` interface only covers *privacy* | |
220 | VPNs and regular network interfaces (such as WiFi or Ethernet) well. The | |
221 | *corporate* kind of VPN is not well covered, since the interface cannot | |
222 | propagate the `default-route` boolean, nor can be used to configure the | |
223 | `~….in-addr.arpa` or `~.ip6.arpa` routing domains. | |
224 | ||
818e46ae | 225 | ## Ordering |
5695ee50 LP |
226 | |
227 | When configuring per-interface DNS configuration settings it is wise to | |
228 | configure everything *before* actually upping the interface. Once the interface | |
229 | is up `systemd-resolved.service` might start using it, and hence it's important | |
230 | to have everything configured properly (this is particularly relevant when | |
231 | LLMNR or MulticastDNS is enabled, since that works without any explicitly | |
232 | configured DNS configuration). It is also wise to configure search/routing | |
233 | domains and the `default-route` boolean *before* configuring the DNS servers, | |
234 | as the former without the latter has no effect, but the latter without the | |
235 | former will result in DNS traffic possibly being generated, in a non-desirable | |
236 | way given that the routing information is not set yet. | |
237 | ||
818e46ae | 238 | ## Downgrading Search Domains to Routing Domains |
5695ee50 LP |
239 | |
240 | Many VPN implementations provide a way how VPN servers can inform VPN clients | |
241 | about search domains to use. In some cases it might make sense to install those | |
242 | as routing domains instead of search domains. Unqualified domain names usually | |
243 | imply a context of locality: the same unqualified name typically is expected to | |
244 | resolve to one system in one local network, and to another one in a different | |
245 | network. Search domains thus generally come with security implications: they | |
246 | might cause that unqualified domains are resolved in a different (possibly | |
247 | remote) context, contradicting user expectations. Thus it might be wise to | |
248 | downgrade *search domains* provided by VPN servers to *routing domains*, so | |
249 | that local unqualified name resolution remains untouched and strictly maintains | |
250 | its local focus — in particular in the aforementioned less trusted *corporate* | |
251 | VPN scenario. | |
252 | ||
253 | To illustrate this further, here's an example for an attack scenario using | |
254 | search domains: a user assumes the printer system they daily contact under the | |
255 | unqualified name "printer" is the network printer in their basement (with the | |
256 | fully qualified domain name "printer.home"). Sometimes the user joins the | |
257 | corporate VPN of their employer, which comes with a search domain | |
258 | "foocorp.example", so that the user's confidential documents (maybe a job | |
259 | application to a competing company) might end up being printed on | |
260 | "printer.foocorp.example" instead of "printer.home". If the local VPN software | |
261 | had downgraded the VPN's search domain to a routing domain "~foocorp.example", | |
262 | this mismapping would not have happened. | |
263 | ||
264 | When connecting to untrusted WiFi networks it might be wise to go one step | |
265 | further even: suppress installation of search/routing domains by the network | |
266 | entirely, to ensure that the local DNS information is only used for name | |
267 | resolution of qualified names and only when no better DNS configuration is | |
268 | available. |