published on 19 December 2023
I’m currently using systemd-networkd, systemd-resolved and iwd together with my ad blocking DNS-over-TLS server for my networking, and it works quite well for most networks, even the pesky 802.1x types such as eduroam. But now picture this: you’re at a hotel, a public place or in a train and you want to use the free Wi-Fi but you can’t seem to get online because your custom config using some minimal network configuration doesn’t work, and you’re on the brink of just installing NetworkManager and rolling with the defaults. Fear not! This is how to make an exception for your DNS-over-TLS config for certain networks.
But first, let’s talk about why we need to do this.
First you need to know that Wi-Fi access points always provide connected devices with a default DNS server over DHCP, which is usually your access point itself. Using this default DNS server, the AP can block all traffic to external sites by just making every domain name resolve to the captive portal login page (it then keeps track of your login status using your device’s MAC address). Note that there are also captive portals using HTTP traffic interception instead of DNS redirection, however, I found that these aren’t that common anymore, because they have issues with HTTPS traffic, which is most traffic nowadays.
Your config files probably look something like this if your setup is similar to mine:
Note that by default iwd does network configuration such as handling DHCP itself, but I chose to let systemd-networkd do this, because I’m not sure if it’s possible to do such high-level config distinctions (Wi-Fi access point names, etc.) in the iwd config. So here’s my iwd config, too:
We need to create a config file that overrides the default interface settings shown above. To do this, just create the following file:
And exclude the SSID in your default config file:
If you want to connect to a new open Wi-Fi, just add its SSID to the two config files, explicitly including it in one file and explicitly excluding it in the other, like so:
Then restart all the relevant services:
And go to http://captive.apple.com, which should be intercepted by the captive portal and redirect to its login page.
You should now also be able to see that you’re using a different DNS server when you run resolvectl status
.
Now that you’re authenticated to the captive portal, you should be able to change back your DNS server using this command:
To verify this, run sudo ngrep port 853
(853 is the port used for DNS-over-TLS) and see the encrypted DNS traffic to the server address you used.