Terug naar hoofdinhoud
Linux concept: DNS-resolver
# Topics

Linux concept: DNS-resolver

22 augustus 2026

Twee mensen zoeken dezelfde naam op, op dezelfde laptop, in dezelfde minuut, en krijgen twee verschillende antwoorden. Geen van beiden heeft ongelijk en er is niets kapot. Op Linux is "een naam opzoeken" niet één ding: het is een stapel van vier lagen, elk met een eigen configuratiebestand, een eigen cache en een eigen mening, en een DNS-tool op de commandoregel slaat de meeste ervan over. Zolang je niet weet welke laag het antwoord gaf, kun je niet zeggen of het probleem bij je domein ligt, bij je netwerk, of bij één regel in een bestand dat je nog nooit hebt geopend.

Dit artikel gaat over de clientkant: de machine die voor je staat. Wat /etc/resolv.conf op een modern systeem echt is, waarom /etc/nsswitch.conf meer bepaalt dan DNS zelf, wat resolvectl je kan vertellen dat dig niet kan, hoe containers namen anders opzoeken dan hun host, en hoe je je eigen resolver draait zonder het aanvalswapen van iemand anders te worden.

Hoe een Linux-machine een naam in een adres omzet, laag voor laag.

Doel: na het lezen kun je precies zeggen welke laag een lookup beantwoordde, en pas je de instelling aan op de plek waar die echt gegenereerd wordt.

1. De basis

Het Domain Name System (DNS) is de gids van het internet: het zet namen om in adressen, en in een stuk of tien andere soorten informatie. Dat geldt overal. Wat specifiek is voor Linux, is hoe een programma op jouw machine die vraag stelt, want het vraagt het bijna nooit rechtstreeks aan DNS.

Een applicatie roept een functie in de C-bibliotheek aan. Die bibliotheek raadpleegt een configuratiebestand om te bepalen welke bronnen in welke volgorde geprobeerd worden. DNS is er daar één van. Pas daarna verlaat een pakket de machine, en op de meeste moderne systemen verlaat het de machine eerst helemaal niet: het gaat naar een lokale stub op een loopback-adres, die het doorstuurt.

1.1 De vier lagen op je machine

Tussen "de browser wil een naam" en "een DNS-vraag gaat het netwerk op" zitten vier lagen. Elke laag wordt ergens anders geconfigureerd.

LaagWat die doetGeconfigureerd in
De applicatie Roept getaddrinfo() aan. Sommige applicaties slaan alles hieronder over en zoeken namen zelf op. De applicatie zelf
NSS (Name Service Switch) Bepaalt welke bronnen in welke volgorde geprobeerd worden: lokale bestanden, mDNS, DNS en andere. /etc/nsswitch.conf
De stub-resolver Stelt de eigenlijke DNS-vraag op en stuurt die naar de servers die hij gekregen heeft. /etc/resolv.conf, meestal gegenereerd
De lokale caching-daemon Luistert op een loopback-adres, cachet, en stuurt per netwerkinterface door naar boven. /etc/systemd/resolved.conf, NetworkManager, DHCP

Het juiste mentale model: op Linux wordt DNS als laatste geraadpleegd, niet als eerste. Alles erboven kan het antwoord geven, overrulen of omzeilen. Daarom zijn "wat zegt DNS" en "wat doet deze machine" twee verschillende vragen die twee verschillende commando's nodig hebben.

1.2 Waarom twee tools twee antwoorden geven

Dit is de onenigheid, op de machine waarop dit artikel geschreven is. De naam dev01 is de hostnaam van deze machine zelf:

$ getent hosts dev01
127.0.1.1       dev01

$ dig +search +short dev01
192.168.1.42

Twee antwoorden, allebei correct. getent liep de NSS-lijst af, vond de naam in /etc/hosts, en stopte voordat DNS ooit aan bod kwam. dig negeerde /etc/hosts volledig en vroeg het aan een naamserver, die het zoekdomein erachter plakte en het adres teruggaf dat het netwerk voor deze machine heeft.

Elke applicatie op de machine gedraagt zich als getent. Elke DNS-diagnosetool gedraagt zich als dig. Dat ene feit verklaart het meeste van de verwarring rond dit onderwerp, en daarom zijn beide commando's het waard om paraat te hebben.

1.3 Wat dit artikel behandelt

Dit artikel gaat over de clientkant: de resolver op een Linux-machine, van de configuratiebestanden tot het draaien van je eigen caching-daemon.

Twee aangrenzende onderwerpen staan elders, zodat niets twee keer wordt uitgelegd. Voor DNS als systeem - delegatie, zones, en waar elk recordtype voor dient - zie het DNS-artikel in de Focus On Web-sectie (Engelstalig). Voor het gereedschap, het lezen van dig-uitvoer blok voor blok, het volgen van een delegatie en het vergelijken van een cache met de bron, zie het dig-commando. Waar de DNS-servers op je machine überhaupt vandaan komen is meestal een DHCP-vraag, behandeld in het DHCP-artikel.

Elk commando en elke uitvoer hieronder is uitgevoerd op Ubuntu 24.04.4 LTS met systemd 255 in augustus 2026. Details verschillen per distributie, en het artikel zegt het waar dat zo is. De enige aanpassingen zijn cosmetisch: de hostnaam van deze machine staat er als dev01 en de privé-netwerkadressen als 192.168.1.x.

Naar boven

2. Waar komen de namen vandaan?

Drie bestandsnamen dragen het grootste deel van dit onderwerp, en elk ervan is een afkorting waarvan de uitgeschreven vorm verklaart waar het bestand voor dient.

NaamStaat voorWat dat je vertelt
resolv.conf resolver-configuratie Het configureert de resolver-bibliotheek, geen server. Het was nooit bedoeld om je netwerk te beschrijven
nsswitch.conf Name Service Switch Een schakelaar tussen naamdiensten. DNS is er slechts één van
gai.conf getaddrinfo-configuratie Het configureert één C-bibliotheekfunctie, en alleen het sorteergedrag daarvan

Het woord stub is het ook waard om vast te pinnen, want het duikt op in elk diagnosebericht op een modern systeem. Een stub-resolver is een client die bijna niets weet: hij vraagt het aan één server en gelooft het antwoord. Hij volgt geen verwijzingen vanaf de root, hij weet niet wat een autoritatieve server is, en hij kan zelf niets oplossen. Dat werk hoort bij een recursieve resolver, en dat is wat je provider of 1.1.1.1 draait.

Het verwarrende op Linux is dat systemd-resolved zichzelf een "stub listener" noemt en op 127.0.0.53 draait. Vanuit je applicaties gezien is het een stub, vanuit het netwerk gezien een client, en hij lost niets op vanaf de root. Het is een doorstuurserver met een cache.

En resolvectl is simpelweg resolve control, volgens hetzelfde patroon als systemctl, journalctl en hostnamectl: een besturingsprogramma voor één systemd-dienst.

Naar boven

3. Een korte geschiedenis

Elke laag uit paragraaf 1.1 bestaat omdat de laag eronder ontoereikend bleek, en geen van de oude lagen is ooit verwijderd. De geschiedenis lezen is de snelste manier om te begrijpen waarom een moderne Linux-machine vier plekken heeft om te kijken.

PeriodeWat er kwamHet probleem dat het oploste
Tot 1983 /etc/hosts, gekopieerd van een centrale HOSTS.TXT Nog niets: dit was naamresolutie
1983 DNS wordt gepubliceerd als RFC 882 en RFC 883 Eén bestand kon niet meeschalen met een groeiend netwerk
1986 BIND komt mee met 4.3BSD, met de resolver-bibliotheek en /etc/resolv.conf Programma's hadden een standaardmanier nodig om een naamserver te vinden
1992 Solaris 2 introduceert /etc/nsswitch.conf Namen konden uit NIS, bestanden of DNS komen, en de volgorde moest instelbaar zijn
1997 glibc 2.0 neemt de Name Service Switch over op Linux Linux erft het Solaris-ontwerp, en gebruikt het nog steeds
Jaren 2000 nscd, dnsmasq en resolvconf verschijnen Caching, en meerdere programma's die vochten om één gegenereerd bestand
2004 NetworkManager Laptops wisselen van netwerk, en elk netwerk heeft eigen DNS-servers
28 mei 2014 systemd-resolved verschijnt in systemd 213 DNS per interface: een VPN en een wifi-netwerk hebben verschillende antwoorden nodig
Vandaag Alles hierboven, nog steeds aanwezig, op dezelfde machine -

Twee dingen volgen uit die tabel. Ten eerste is /etc/hosts ouder dan DNS en wint het nog altijd standaard, wat geen fout is maar een bewuste volgorde die nooit is gewijzigd. Ten tweede voegde elke generatie een laag toe zonder er één te verwijderen, dus documentatie uit de afgelopen veertig jaar is deels nog correct en deels gevaarlijk verouderd. Het advies om "gewoon /etc/resolv.conf aan te passen" was uitstekend in 1995.

Het ontwerp dat blijft winnen, is dat van Solaris. nsswitch.conf is vierendertig jaar oud, is bedacht voor een ander besturingssysteem, en is nog steeds het bestand dat bepaalt waar je browser een naam naartoe stuurt.

Naar boven

4. Eenvoudige toepassingen

Vijf commando's beantwoorden bijna elke dagelijkse vraag. Leer deze eerst, want meestal staat het antwoord in één ervan en hoef je helemaal niets aan te passen.

4.1 Wat gebruikt deze machine eigenlijk?

De voor de hand liggende eerste stap is /etc/resolv.conf lezen. Doe dat, maar lees wat er staat:

$ cat /etc/resolv.conf
# This is /run/systemd/resolve/stub-resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
...
nameserver 127.0.0.53
options edns0 trust-ad
search localdomain

Die 127.0.0.53 is geen DNS-server op het internet. Het is de lokale stub, en de echte servers zitten erachter. Het bestand vertelt je, in een commentaarregel, dat het je vraag niet kan beantwoorden. Het commando dat dat wel kan, is resolvectl status:

$ resolvectl status
Global
           Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
    resolv.conf mode: stub
Fallback DNS Servers: 9.9.9.9

Link 3 (wlp0s20f3)
    Current Scopes: DNS
         Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 192.168.1.1
       DNS Servers: 192.168.1.1
        DNS Domain: localdomain

Link 6 (docker0)
    Current Scopes: none
         Protocols: -DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported

Lees die uitvoer als een tabel per interface, want dat is het. De wifi-interface heeft een DNS-server; docker0 heeft er geen. De minnetjes zijn functies die uitstaan: geen DNS over TLS, geen DNSSEC-validatie, geen multicast DNS. resolv.conf mode: stub bevestigt dat het bestand dat je net las de gegenereerde stub-versie is en niet een statisch bestand dat jij geschreven hebt.

Alleen de servers, zonder de rest:

$ resolvectl dns
Global:
Link 3 (wlp0s20f3): 192.168.1.1
Link 6 (docker0):

4.2 Waar zet deze machine een naam naartoe om?

resolvectl query volgt hetzelfde pad als een applicatie, en vertelt je daarna dingen die geen enkele andere tool meldt:

$ resolvectl query petermartin.nl
petermartin.nl: 23.88.98.40                                 -- link: wlp0s20f3
                2a01:4f8:c0c:6848::1                        -- link: wlp0s20f3

-- Information acquired via protocol DNS in 22.3ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: cache network

De laatste drie regels zijn de reden om dit commando te gebruiken. Het meldt welke interface antwoordde, of het antwoord met DNSSEC gevalideerd is, of het versleuteld reisde, en of het uit de cache of van het netwerk kwam. Dat zijn vier vragen waar mensen normaal naar raden. Hier kwam het antwoord deels uit de cache, was het niet gevalideerd, en reisde het onversleuteld: de gewone toestand van een laptop die met een thuisrouter praat.

4.3 getent hosts tegenover getent ahosts

getent bevraagt de NSS-databases precies zoals een applicatie dat doet. Voor namen heeft het twee vormen, en die zijn niet hetzelfde:

$ getent hosts petermartin.nl
2a01:4f8:c0c:6848::1 petermartin.nl

$ getent ahosts petermartin.nl
23.88.98.40     STREAM petermartin.nl
23.88.98.40     DGRAM
23.88.98.40     RAW
2a01:4f8:c0c:6848::1 STREAM
2a01:4f8:c0c:6848::1 DGRAM
2a01:4f8:c0c:6848::1 RAW

Let op de volgorde. hosts gaf het IPv6-adres terug; ahosts zette IPv4 vooraan. Dezelfde machine, dezelfde naam, dezelfde seconde. Paragraaf 7.1 legt uit waarom, en dat is de nuttigste verrassing in dit artikel.

De korte versie voor nu: ahosts is degene die je vertelt wat een applicatie werkelijk gaat doen. Gebruik die, en negeer hosts tenzij je specifiek het oude gedrag wilt.

4.4 Waar kwam dat antwoord vandaan?

Drie tools, drie verschillende lagen. Weten welke welke is, verandert een verwarrende bug in een diagnose van één regel:

CommandoVraagt het aanGebruik het voor
getent ahosts NAAM De hele NSS-keten, als een applicatie "Waar maakt mijn software echt verbinding mee?"
resolvectl query NAAM systemd-resolved, met voorbijgaan aan /etc/hosts "Wat heeft de lokale resolver, en waarvandaan?"
dig NAAM Rechtstreeks een naamserver, met voorbijgaan aan alles lokaal "Wat zegt DNS zelf?"

Als die drie het oneens zijn, is die onenigheid het antwoord. Verschilt getent van dig, dan onderschept iets lokaals de naam: een regel in /etc/hosts, een mDNS-responder, of een zoekdomein. Verschilt resolvectl van dig, dan is het meestal een cache, en paragraaf 4.5 leegt die.

4.5 De cache legen

Nadat een record gewijzigd is, kan de lokale daemon het oude antwoord nog voor de rest van zijn TTL vasthouden:

$ resolvectl flush-caches

Het drukt niets af en keert meteen terug. Dat is succes. Let op wat het niet doet: het leegt alleen de cache op deze machine. Je router, de resolver van je provider en je browser hebben allemaal hun eigen cache, en geen daarvan wordt geraakt. Lost een naam na dit commando nog steeds op naar het oude adres, dan staat de verouderde kopie niet op jouw machine.

Op een systeem zonder lokale caching-daemon valt er niets te legen. Genoeg servers draaien zo, en juist het ontbreken van een cache is waarom een wijziging daar meteen zichtbaar is en op jouw laptop vijf minuten later.

Naar boven

5. Gemiddelde toepassingen

5.1 /etc/nsswitch.conf lezen

Eén regel in dit bestand bepaalt meer over naamresolutie dan alle DNS-instellingen op de machine bij elkaar:

$ grep '^hosts:' /etc/nsswitch.conf
hosts:          files mdns4_minimal [NOTFOUND=return] dns mymachines

Lees van links naar rechts. Die machine kijkt eerst in /etc/hosts (files), dan naar multicast DNS voor .local-namen, dan pas naar echt DNS, en tot slot naar systemd's containernamen. De eerste bron die antwoordt wint, en alles daarna wordt nooit geraadpleegd.

Het [NOTFOUND=return] tussen haakjes is een actie, geen bron. Het zegt: geeft mDNS een definitief "deze naam bestaat niet", stop dan hier in plaats van door te vallen naar DNS. Daarom kan een .local-naam falen op een machine waar DNS prima werkt.

BronWaar die kijkt
files /etc/hosts
dns De servers in /etc/resolv.conf
mdns4_minimal Multicast DNS op het lokale netwerk, alleen IPv4, .local-namen
myhostname Gesynthetiseerd: de eigen naam van de machine, lost altijd op
mymachines Namen van lokale systemd-nspawn-containers
resolve systemd-resolved rechtstreeks, met voorbijgaan aan de stub op 127.0.0.53

5.2 /etc/hosts wint, en dat is de bedoeling

Omdat files vooraan staat, overrulet een regel in /etc/hosts DNS volledig, voor elke applicatie op de machine:

$ head -3 /etc/hosts
127.0.0.1 localhost
127.0.1.1 dev01

Dit is in gelijke mate een functie en een valkuil. Het is de juiste manier om een site op een nieuwe server te testen voordat je het DNS-record verhuist, en het is de reden dat de laptop van een ontwikkelaar de enige machine in het bedrijf kan zijn waar een site werkt.

/etc/hosts is ouder dan DNS en staat er nog steeds boven. Elke "waarom gebeurt dit alleen op mijn machine"-vraag in dit onderwerp heeft hetzelfde eerste antwoord, en dat is een bestand dat op één scherm past.

De valkuil heeft een vaste vorm: een regel die tijdens een migratie is toegevoegd, nooit is verwijderd, en nog steeds wijst naar een server die twee jaar geleden is uitgezet. Niets waarschuwt je, want het bestand doet precies wat het opgedragen kreeg. Gedraagt een naam zich op één machine anders dan op alle andere, dan is /etc/hosts de eerste plek om te kijken en het kost vijf seconden.

5.3 De zoeklijst en namen zonder punt

De search-regel in /etc/resolv.conf gaat over namen zonder punten erin. Vraag je om server1 terwijl search localdomain actief is, dan probeert de resolver eerst server1.localdomain voordat hij het opgeeft.

Dat is handig binnen een bedrijfsnetwerk en verwarrend daarbuiten, want het betekent dat een naam voor je applicaties kan oplossen en voor je DNS-tool niet. dig gebruikt de zoeklijst niet, tenzij je erom vraagt:

$ dig +short dev01          # geen zoeklijst toegepast
127.0.1.1

$ dig +search +short dev01  # zoeklijst toegepast
192.168.1.42

Een verwante optie is ndots, die instelt hoeveel punten een naam moet bevatten voordat de resolver hem als zodanig probeert in plaats van er een zoekdomein achter te plakken. De standaard is 1. Kubernetes zet hem vaak op 5, en daarom kan een lookup binnen een cluster meerdere mislukte vragen opleveren voordat de geslaagde komt. DNS-latency in Kubernetes is een onderwerp op zich.

5.4 DNS per interface is de hele reden voor systemd-resolved

De reden voor een lokale daemon is dat één machine op meerdere netwerken tegelijk kan zitten, en elk daarvan heeft een eigen naamserver die dingen weet die de andere niet weten. Een VPN lost interne namen op; de wifi niet. Vóór DNS per interface betekende verbinden met een VPN dat /etc/resolv.conf werd overschreven, met de hoop dat de vorige inhoud daarna hersteld werd.

$ resolvectl domain
Global:
Link 3 (wlp0s20f3): localdomain
Link 6 (docker0):

Een domein dat hier staat is een zoekdomein. Een domein met een tilde ervoor, zoals ~corp.example.com, is een routeringsdomein: het wordt niet achter korte namen geplakt, het zegt alleen "stuur vragen voor dit domein naar de server van deze interface". Dat ene teken is hoe split DNS wordt uitgedrukt, en het is de instelling die een VPN-client namens jou zet.

5.5 Pas de instelling aan waar die gegenereerd wordt

/etc/resolv.conf is een symlink naar een map die bij het opstarten opnieuw wordt opgebouwd:

$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Apr 24  2024 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf

Alles wat je erin typt is weg bij de eerstvolgende herstart, of eerder. Meerdere programma's kunnen het genereren: systemd-resolved, NetworkManager, een DHCP-client, resolvconf, een VPN-client, of een container-runtime. Het bestand aanpassen is een oplossing die het tot dinsdag volhoudt.

Pas het in plaats daarvan aan waar het geproduceerd wordt:

Je wiltPas het aan in
De DNS-servers voor één wifi- of bekabeld netwerk zetten Het NetworkManager-verbindingsprofiel (nmcli, of de bureaubladinstellingen)
Een globale terugvaloptie voor de hele machine zetten /etc/systemd/resolved.conf, sleutel DNS=
De servers voor elke machine op het netwerk zetten De DHCP-server, want daar kwamen ze vandaan
Een wijziging testen zonder die te bewaren resolvectl dns <link> <server>, ongedaan met resolvectl revert <link>
Eén naam overrulen voor elk programma op de machine /etc/hosts

Om te zien wat NetworkManager denkt, wat vaak de echte bron is:

$ nmcli dev show | grep -E 'GENERAL.DEVICE|IP4.DNS|IP4.DOMAIN'
GENERAL.DEVICE:                         wlp0s20f3
IP4.DNS[1]:                             192.168.1.1
IP4.DOMAIN[1]:                          localdomain

5.6 resolvectl leest gestructureerde records voor je

Dit is het deel van resolvectl dat vrijwel niemand gebruikt, en het is voor een paar recordtypen echt beter dan dig, omdat het ze decodeert in plaats van ze af te drukken.

Een SRV-lookup betekent normaal vier getallen lezen en daarna zelf het doel opzoeken. resolvectl service doet allebei:

$ resolvectl service _sip._udp sip.voice.google.com
_sip._udp/sip.voice.google.com: sip-anycast-1.voice.google.com:5060 [priority=10, weight=1]
                                216.239.32.1                -- link: wlp0s20f3
                                sip-anycast-2.voice.google.com:5060 [priority=20, weight=1]
                                216.239.32.2                -- link: wlp0s20f3

Het benoemde de prioriteit en het gewicht, sorteerde op voorkeur, en loste elk doel op naar een adres in dezelfde uitvoer. En voor DANE-records, waar de ruwe vorm drie onverklaarde getallen gevolgd door een hash is:

$ resolvectl tlsa tcp mx1.freebsd.org:25
_25._tcp.mx1.freebsd.org IN TLSA 3 1 1 0a7e2f469913ea64ca98af1f31bbbcaf...
        -- Cert. usage: Domain-issued certificate
        -- Selector: SubjectPublicKeyInfo
        -- Matching type: SHA-256 -- link: wlp0s20f3

Die drie commentaarregels zijn precies wat je anders in RFC 6698 zou opzoeken. Er is een bijbehorende resolvectl openpgp voor OPENPGPKEY-records. Voor al het andere blijft dig het betere gereedschap, omdat het je het pakket laat zien in plaats van een interpretatie ervan.

Naar boven

6. Gevorderde toepassingen

6.1 DNS over TLS en DNSSEC-validatie aanzetten

Klassiek DNS reist onversleuteld, dus iedereen op het pad kan lezen wat je opzoekt en het antwoord veranderen. systemd-resolved kan de stap naar je bovenliggende server versleutelen en handtekeningen valideren, en op de meeste systemen staan beide uit. De uitvoer van resolvectl status in paragraaf 4.1 zegt het al: -DNSOverTLS en DNSSEC=no/unsupported.

Beide stel je in via /etc/systemd/resolved.conf. Het meegeleverde bestand staat volledig in commentaar, wat een handige referentie voor de standaardwaarden is:

$ grep -E '^#(DNS|FallbackDNS|DNSSEC|DNSOverTLS|Cache)' /etc/systemd/resolved.conf
#DNS=
#DNSSEC=no
#DNSOverTLS=no
#Cache=no-negative

Om te versleutelen noem je een server die het ondersteunt en pin je de hostnaam waar het certificaat op moet matchen:

[Resolve]
DNS=1.1.1.1#cloudflare-dns.com 1.0.0.1#cloudflare-dns.com
DNSOverTLS=yes
DNSSEC=allow-downgrade

Daarna systemctl restart systemd-resolved en met resolvectl status bevestigen dat de minnetjes plusjes zijn geworden.

Twee waarschuwingen die je serieus moet nemen. DNSOverTLS=yes is streng: ondersteunt de server het niet, dan mislukt de resolutie in plaats van terug te vallen. Dat is het juiste gedrag en ook een prima manier om alle naamresolutie kwijt te raken op wifi met een inlogportaal. En DNSSEC=yes maakt een kapotte handtekening ergens in een keten fataal, ook bij domeinen waarvan de eigenaar een fout heeft gemaakt die jij niet kunt herstellen. allow-downgrade valideert waar het kan en breekt niet waar het niet kan, en dat is de instelling die de meeste mensen eigenlijk willen.

6.2 Split DNS, en waarom een VPN niet alles moet overnemen

Een VPN heeft doorgaans twee dingen nodig: interne namen moeten door de tunnel oplossen, en al het andere moet het lokale netwerk blijven gebruiken. De oude aanpak - /etc/resolv.conf herschrijven en de VPN-server vooraan zetten - stuurt elke lookup die je doet door de resolver van het bedrijf, inclusief je privé-browsen, en breekt wat het lokale netwerk aan het oplossen was.

Routeringsdomeinen zijn het juiste mechanisme. Een domein met een ~ ervoor wordt niet achter korte namen geplakt; het routeert alleen vragen voor dat achtervoegsel naar die interface:

# route alleen corp.example.com via de VPN-interface
$ sudo resolvectl domain tun0 '~corp.example.com'
$ sudo resolvectl dns tun0 10.0.0.53

$ resolvectl domain
Link 3 (wlp0s20f3): localdomain
Link 8 (tun0): ~corp.example.com

Nu gaat db.corp.example.com naar 10.0.0.53 en al het andere naar de server van de wifi. Om elke vraag aan een interface te geven is het routeringsdomein ~., een tilde en een punt: de root, oftewel "alles". Zie je ~. op een VPN-interface, dan weet je dat die al je DNS heeft opgeëist. Soms is dat de bedoeling, en het is hoe dan ook goed om te weten.

resolvectl revert tun0 maakt instellingen per interface die zo gezet zijn weer ongedaan.

6.3 Containers lossen namen anders op dan hun host

Een container erft de resolver-instellingen van je machine niet, en het verschil bijt precies tijdens de debugsessie waarin je een verrassing het minst kunt gebruiken. Wat hij krijgt, hangt af van het Docker-netwerk waar hij op zit, en de twee gevallen gedragen zich totaal verschillend.

Op de standaard bridge krijgt de container de bovenliggende server van de host en verder niets:

$ docker run --rm alpine cat /etc/resolv.conf
# Generated by Docker Engine.
...
nameserver 192.168.1.1

$ docker run -d --name dnsdb2 alpine sleep 60
$ docker run --rm alpine getent hosts dnsdb2
$ echo $?
2                             # niets: containernamen lossen hier niet op

Op een zelfgemaakt netwerk zet Docker zijn eigen ingebouwde resolver ertussen en gaan containernamen werken:

$ docker network create dnstest
$ docker run --rm --network dnstest alpine cat /etc/resolv.conf
# Generated by Docker Engine.
...
nameserver 127.0.0.11

$ docker run -d --network dnstest --name dnsdb alpine sleep 60
$ docker run --rm --network dnstest alpine getent hosts dnsdb
172.18.0.2        dnsdb  dnsdb

Dat adres, 127.0.0.11, is de ingebouwde DNS-server van Docker, en die lost de ene containernaam naar de andere op. Daarom werken servicenamen in een Docker Compose-stack zonder dat iemand DNS instelt: Compose maakt automatisch een zelfgemaakt netwerk voor het project.

Merk ook op dat de host 127.0.0.53 gebruikte en de container op de standaard bridge 192.168.1.1 kreeg. Docker geeft bewust geen loopback-adres door aan een container, want binnen de netwerknaamruimte van de container zou dat naar de container zelf wijzen.

Twee praktische gevolgen. Ten eerste: kan een applicatie in een container zijn database niet bereiken, test de resolutie dan in de container, niet op de host, want ze krijgen antwoord van verschillende resolvers. Ten tweede: lossen namen in een stack niet op, kijk dan eerst op welk netwerk de containers zitten voordat je naar iets anders kijkt:

$ docker exec -it mycontainer cat /etc/resolv.conf
$ docker exec -it mycontainer getent hosts database

Hetzelfde idee schaalt op. Kubernetes draait een cluster-DNS-dienst, meestal CoreDNS, en geeft elke service een naam als database.default.svc.cluster.local zodat applicaties elkaar kunnen vinden zonder vaste adressen. Binnen moderne platformen is DNS de laag voor service-discovery in plaats van een internetgids.

6.4 Je eigen resolver draaien, zonder een open resolver te worden

Er zijn goede redenen om een echte recursieve resolver op Linux te draaien: caching voor een heel kantoor, je eigen DNSSEC-validatie, onafhankelijkheid van een derde partij, en logs die je zelf beheert. unbound is de gebruikelijke keuze; BIND kan het ook, en dnsmasq is een lichtere doorstuurserver-met-cache die veel thuisrouters al draaien.

Er is één manier om dit ernstig verkeerd te doen, en die is het waard om ronduit te benoemen. Een recursieve resolver die iedereen op het internet antwoord geeft, is een open resolver, en die wordt binnen enkele dagen gevonden en misbruikt.

Het misbruik heet amplificatie. DNS draait van oudsher over UDP, dat niet controleert wie een pakket verstuurde. Een aanvaller stuurt dus kleine vragen met het adres van het slachtoffer als vervalste afzender. Jouw server stuurt gehoorzaam veel grotere antwoorden naar het slachtoffer. Een bescheiden verzoek wordt een stortvloed, de aanvaller betaalt bijna niets, en het verkeer komt van jouw adres in plaats van het zijne.

Het voorkomen is niet ingewikkeld:

  • Beperk recursie tot clients die je vertrouwt, dus je eigen netwerken. Deze ene instelling haalt het hele probleem weg.
  • Houd de twee rollen gescheiden. Een server die je zones aan de wereld publiceert is autoritatief en heeft helemaal geen recursie nodig. Een server die voor je medewerkers oplost is recursief en hoort van buiten niet bereikbaar te zijn. Ze combineren op één publiek adres is hoe dit meestal per ongeluk gebeurt.
  • Zet response rate limiting aan op alles wat wel aan het internet hangt.
  • Bind aan de juiste interface. Een resolver die op 0.0.0.0 luistert op een machine met een publiek adres staat open, ook al dacht je dat hij intern was.

Het algemene principe reikt verder dan DNS: weet voordat je een server blootstelt welke rol hij hoort te spelen, want de juiste configuratie verschilt per rol en de faalmodus hier is dat iemand anders jouw bandbreedte gebruikt om een vreemde aan te vallen.

6.5 Bewust een laag overslaan

Verdenk je één laag, haal die dan uit beeld in plaats van erover te redeneren. Elk van deze commando's slaat iets specifieks over:

$ resolvectl query --cache=no petermartin.nl    # sla de lokale cache over
$ resolvectl query --protocol=dns NAAM          # sla mDNS en LLMNR over
$ dig @192.168.1.1 petermartin.nl               # sla de lokale daemon helemaal over
$ dig @1.1.1.1 petermartin.nl                   # sla ook de server van het netwerk over
$ getent ahosts NAAM                            # inclusief elke laag, als een applicatie

Werk die lijst af, en het eerste commando waarvan het antwoord verandert vertelt je welke laag verantwoordelijk was. Dat gaat sneller dan configuratiebestanden lezen, en het werkt op een machine waarvan je de configuratie nog nooit gezien hebt.

Naar boven

7. Iets wat de meeste gebruikers niet weten

7.1 getent hosts en getent ahosts roepen verschillende functies aan

Paragraaf 4.3 liet de twee commando's dezelfde adressen in een andere volgorde teruggeven. Dat is niet cosmetisch. Ze gebruiken verschillende aanroepen in de C-bibliotheek, en de man-pagina zegt het:

  • getent hosts NAAM roept gethostbyname2() aan, de oude interface uit de jaren tachtig.
  • getent ahosts NAAM roept getaddrinfo() aan met AF_UNSPEC, en dat is wat elke moderne applicatie gebruikt.

Alleen getaddrinfo() past bestemmingsselectie toe, de regels die bepalen of IPv4 of IPv6 als eerste geprobeerd wordt. getent ahosts geeft de adressen dus terug in de volgorde waarin je software ze echt gaat proberen, en getent hosts geeft ze in de volgorde die de lookup toevallig opleverde.

Dat is belangrijk als je een verbinding debugt die voor sommige gebruikers mislukt. De vraag is nooit "lost de naam op", maar "welk adres wordt als eerste geprobeerd", en maar één van deze twee commando's beantwoordt dat.

7.2 /etc/gai.conf bepaalt IPv4 of IPv6, en staat volledig in commentaar

De sorteerregels die getaddrinfo() toepast komen uit een beleidstabel, en die tabel staat in een bestand dat de meeste beheerders nog nooit geopend hebben:

$ grep -vE '^\s*#|^\s*$' /etc/gai.conf
                              # niets: elke regel is commentaar

Een lege uitkomst betekent niet dat het bestand niets doet. Het betekent dat glibc zijn ingebouwde standaardwaarden gebruikt, en die geven IPv6 voorrang op IPv4. Het bestand bestaat zodat je ze kunt overrulen, en het meegeleverde commentaar vertelt je precies hoe:

#precedence  2002::/16     30
#precedence ::/96          20
#precedence ::ffff:0:0/96  10
#
#    For sites which prefer IPv4 connections change the last line to
#
#precedence ::ffff:0:0/96  100

Haal die laatste regel uit commentaar en de machine geeft overal voorrang aan IPv4, voor elk programma, zonder dat je iets aan DNS verandert. ::ffff:0:0/96 is het bereik van IPv4-gemapte adressen, en de voorrang ervan boven de IPv6-regels zetten draait de volgorde om.

De volgorde waarin een machine IPv4 en IPv6 probeert is geen DNS-instelling, geen routeringsinstelling en geen applicatie-instelling. Het is een beleidstabel in een glibc-configuratiebestand, en op een standaardsysteem staat elke regel erin in commentaar.

Dit is de echte oplossing voor de klassieke klacht "de site laadt traag maar is snel zodra hij eenmaal geladen is" op een netwerk met kapot IPv6. De machine probeert het AAAA-adres, wacht op een time-out, en valt pas dan terug. Mensen grijpen naar het verwijderen van het AAAA-record, en dat is de verkeerde kant van het probleem en raakt elke bezoeker ter wereld; de goede kant is één regel op de getroffen machine.

7.3 De cache is er wel, en je kunt hem niet lezen zonder root

systemd-resolved heeft commando's om je de inhoud van zijn cache en zijn statistieken te tonen. Draai je ze als gewone gebruiker, dan krijg je dit:

$ resolvectl show-cache
Failed to connect to query monitoring service
/run/systemd/resolve/io.systemd.Resolve.Monitor: Permission denied

De monitoring-socket vraagt om rechten, want de inhoud van een DNS-cache is een verslag van alles wat elke gebruiker op de machine heeft bekeken. Hetzelfde geldt voor resolvectl statistics en resolvectl monitor, die vragen live doorgeeft en het dichtst in de buurt komt van naamresolutie zien gebeuren. Alle drie werken onder sudo.

Geweigerd worden is op zichzelf informatief: het bevestigt dat er een caching-daemon draait en bereikbaar is. Op een machine zonder lokale cache ziet de fout er compleet anders uit.

7.4 Negatieve antwoorden worden standaard niet gecachet

De standaard is Cache=no-negative, en de handleiding is er duidelijk over: alleen positieve antwoorden worden gecachet.

"Deze naam bestaat niet" wordt dus elke keer opnieuw gevraagd. Publiceer je een record dat er eerder niet was, dan werkt het op deze machine meteen, zonder cache om te legen, terwijl een positief antwoord dat je gewijzigd hebt de volle TTL de oude waarde blijft geven. Twee wijzigingen, twee totaal verschillende wachttijden, uit één instelling waarvan niemand wist dat hij bestond.

In hetzelfde bestand staat nog een standaardwaarde die het weten waard is: CacheFromLocalhost=no. Zit je bovenliggende server op 127.0.0.1 - een lokale unbound of dnsmasq - dan cachet systemd-resolved die antwoorden bewust niet, want die server heeft al een cache en twee caches zouden alleen maar verwarring toevoegen.

7.5 Een browser kan dit allemaal negeren

Alles in dit artikel beschrijft wat er gebeurt als een programma het aan het besturingssysteem vraagt. Een moderne browser hoeft dat niet te doen. Met DNS over HTTPS aan in zijn eigen instellingen stuurt hij lookups naar een resolver van eigen keuze over poort 443, en omzeilt daarmee tegelijk nsswitch.conf, systemd-resolved, je /etc/hosts-bestand en de routeringsdomeinen van je VPN.

Dat levert een oprecht verwarrende storing op: getent, dig en resolvectl zijn het allemaal eens, en de browser laadt toch iets anders. Of je voegt een regel toe aan /etc/hosts om een nieuwe server te testen, elk commandoregeltool respecteert die, en de browser niet.

Gedraagt een naam zich in één browser anders dan overal elders op dezelfde machine, controleer dan eerst de DNS-instelling van die browser voordat je iets van het besturingssysteem controleert.

7.6 Weten waar de clientkant ophoudt

Een deel van vakmanschap is weten bij welke laag een probleem hoort. Deze tabel is het waard om te bewaren:

SymptoomLigt het aan deze machine?Waar te kijken
Eén machine lost een naam anders op dan alle andere Ja /etc/hosts, daarna de zoeklijst
Een naam werkt in de shell en niet in een browser Ja De eigen DNS-over-HTTPS-instelling van die browser
Een naam werkt op de host en niet in een container Ja Op welk Docker-netwerk de container zit
Trage eerste verbinding, daarna snel Vaak IPv6 dat een time-out oploopt; zie /etc/gai.conf in 7.2
Instellingen zijn na een herstart weer weg Ja Je paste /etc/resolv.conf aan; pas de generator aan
Interne namen lossen niet meer op na het verbinden met een VPN Ja Routeringsdomeinen: een interface kan ~. hebben opgeëist
Een record dat je wijzigde geeft overal nog de oude waarde Nee De TTL. Niets op deze machine helpt daarbij
Het domein lost ineens nergens meer op Nee Het domein: verlopen, DNSSEC, of een dode delegatie
Mail wordt geweigerd of belandt in spam Nee De MX-, SPF-, DKIM- en DMARC-records van de zone

De laatste drie rijen zijn de grens. Is het antwoord overal hetzelfde en simpelweg verkeerd, dan verandert geen enkele lokale instelling daar iets aan, en is de vraag verschoven van jouw machine naar het domein en zijn records (Engelstalig).

Naar boven

8. Best practices

  • Pas nooit /etc/resolv.conf aan. Op de meeste systemen is het een gegenereerde symlink. Pas de instelling aan waar die geproduceerd wordt: het NetworkManager-verbindingsprofiel, /etc/systemd/resolved.conf, of de DHCP-server die hem uitdeelde.
  • Gebruik resolvectl status om de echte servers te vinden. De nameserver-regel in resolv.conf is bijna altijd 127.0.0.53, en die zegt niets over het netwerk.
  • Gebruik liever getent ahosts dan getent hosts. Het roept getaddrinfo() aan en geeft adressen in de volgorde waarin applicaties ze echt gaan proberen.
  • Gebruik het juiste tool voor de vraag. getent voor "wat doet deze machine", resolvectl query voor "wat heeft de lokale resolver en waarvandaan", dig voor "wat zegt DNS zelf". Zijn ze het oneens, dan is die onenigheid de diagnose.
  • Controleer eerst /etc/hosts als één machine afwijkt van alle andere. Het kost vijf seconden en het is vaker het antwoord dan wat dan ook.
  • Verwijder regels uit het hosts-bestand als de migratie klaar is. Een override die naar een uitgezette server wijst geeft een storing die niemand kan verklaren, want er is niets mis met het domein.
  • Test container-DNS in de container. Een container krijgt antwoord van een andere resolver dan zijn host, en containernamen lossen alleen op een zelfgemaakt netwerk op, niet op de standaard bridge.
  • Gebruik routeringsdomeinen voor een VPN, geen globale override. ~corp.example.com op de tunnelinterface routeert alleen wat daar hoort. Een interface met ~. heeft elke lookup die je doet overgenomen.
  • Kies liever DNSSEC=allow-downgrade dan DNSSEC=yes, tenzij je erop voorbereid bent dat de kapotte handtekening van een vreemde jouw resolutie breekt.
  • Test DNSOverTLS=yes voordat je erop vertrouwt. Het valt niet terug, wat correct is en je ook afsluit op een netwerk met een inlogportaal.
  • Stel nooit een recursieve resolver bloot aan het internet. Beperk recursie tot je eigen netwerken, houd autoritatieve en recursieve rollen op aparte servers, en controleer waar je resolver aan gebonden is in plaats van het aan te nemen.
  • Onthoud dat het legen van de lokale cache alleen de lokale cache leegt. Je router, je provider en je browser houden hun eigen kopie, en geen daarvan wordt geraakt.
  • Lees de documentatie op de machine zelf. man resolved.conf noemt elke instelling met de standaardwaarde, man nsswitch.conf legt de actiesyntaxis tussen haakjes uit, man gai.conf behandelt de adresvolgorde, en resolvectl --help toont twintig subcommando's die de meeste mensen nooit ontdekken.
Naar boven

9. Veelgemaakte fouten

9.1 Mythe tegenover werkelijkheid

MytheWerkelijkheid
"Zet je DNS-servers in /etc/resolv.conf." Op de meeste moderne systemen is dat een gegenereerde symlink. Je wijziging verdwijnt bij de eerstvolgende herstart, of eerder.
"127.0.0.53 betekent dat er iets kapot is." Het is de normale stub-listener van systemd-resolved. De echte servers zitten erachter, per interface.
"dig laat zien wat mijn machine gaat doen." dig praat rechtstreeks met een naamserver. Het leest /etc/hosts nooit en gebruikt de zoeklijst niet, tenzij je +search meegeeft.
"getent hosts en getent ahosts zijn hetzelfde." Verschillende aanroepen in de C-bibliotheek. Alleen ahosts gebruikt getaddrinfo() en geeft adressen in de volgorde waarin applicaties ze proberen.
"DNS is het eerste wat Linux voor een naam raadpleegt." Het is bijna het laatste. /etc/nsswitch.conf zet files ervoor, en mDNS kan de zoektocht stoppen voordat DNS bereikt wordt.
"De cache legen laat een gewijzigd record verschijnen." Het leegt alleen deze machine. Je router, de resolver van je provider en je browser houden elk hun eigen kopie.
"Een negatief antwoord wordt net als elk ander gecachet." Niet door systemd-resolved: Cache=no-negative is de standaard, dus "bestaat niet" wordt elke keer opnieuw gevraagd.
"IPv6-voorkeur is een DNS-instelling." Het is /etc/gai.conf, een glibc-beleidstabel die niets met DNS te maken heeft en volledig in commentaar wordt meegeleverd.
"Verbinden met de VPN heeft mijn DNS gebroken." De VPN-interface heeft waarschijnlijk het routeringsdomein ~. opgeëist en beantwoordt nu elke vraag, ook die waar zijn server niets van weet.
"De container gebruikt de DNS-instellingen van de host." Hij krijgt een eigen resolv.conf, en op een zelfgemaakt Docker-netwerk is dat 127.0.0.11, een compleet andere resolver.
"Als elk tool op de machine het eens is, doet de browser dat ook." Niet met DNS over HTTPS aan in de browser, die het besturingssysteem volledig omzeilt.

9.2 Valkuilen om te vermijden

  • Het doel van de symlink onder /run aanpassen. Dat is een tijdelijk bestandssysteem. Het bestand is bij de volgende start weg en je bent vergeten dat je het gewijzigd had.
  • De symlink vervangen door een statisch bestand "zodat het blijft staan". Het blijft staan, en het zet ook DNS per interface uit, dus de interne namen van je VPN lossen niet meer op en niets verklaart waarom.
  • Een testregel in /etc/hosts laten staan. Zes maanden later is de server weg en kan één machine in het bedrijf een site niet bereiken die overal elders werkt.
  • Naamresolutie van een container vanaf de host debuggen. Ze krijgen antwoord van verschillende resolvers, dus de host bewijst niets over de container.
  • Aannemen dat een naam die in een container faalt een DNS-probleem is. Op de standaard bridge zijn containernamen helemaal niet op te lossen; dat is het ontwerp van het netwerk, geen storing.
  • DNSSEC=yes op een laptop zetten. Een validatiefout in de zone van iemand anders wordt jouw storing, en inlogportalen breken validatie standaard met opzet.
  • Vertrouwen op één ping om te bewijzen dat resolutie werkt. ping volgt NSS, dus het kan antwoord geven uit /etc/hosts of mDNS terwijl DNS volledig kapot is.
  • Vergeten dat dig de zoeklijst niet toepast. Een naam zonder punt die voor elke applicatie werkt, lijkt dan niet te bestaan.
  • Een resolver draaien die aan 0.0.0.0 gebonden is op een machine met een publiek adres. Dat is een open resolver, en die wordt binnen enkele dagen gevonden en misbruikt.
  • Uit een trage pagina concluderen "het ligt aan DNS". Resolutie gebeurt één keer en wordt daarna gecachet. Een trage pagina ligt bijna altijd aan de applicatie of aan het netwerkpad.
Naar boven

10. Samenvatting

Op Linux is een naam opzoeken een stapel, geen lookup. Bijna alles wat eraan verwarrend is, komt doordat je de ene laag een vraag stelt die een andere laag beantwoordde.

  • Vier lagen zitten tussen een applicatie en het netwerk: de applicatie zelf, NSS (/etc/nsswitch.conf), de stub-resolver (/etc/resolv.conf) en een lokale caching-daemon zoals systemd-resolved.
  • DNS wordt bijna als laatste geraadpleegd, niet als eerste. files staat vóór dns in nsswitch.conf, dus /etc/hosts wint voor elke applicatie op de machine.
  • /etc/resolv.conf is een gegenereerde symlink met 127.0.0.53 erin, de lokale stub. Het kan je niet vertellen welke servers de machine echt gebruikt; resolvectl status wel, per interface.
  • Drie tools vragen het aan drie verschillende lagen. getent ahosts gedraagt zich als een applicatie, resolvectl query vraagt het aan de lokale daemon en meldt waar het antwoord vandaan kwam, en dig vraagt het rechtstreeks aan een naamserver. Hun onenigheid is de diagnose.
  • getent hosts roept gethostbyname2() aan; getent ahosts roept getaddrinfo() aan. Alleen de tweede geeft adressen in de volgorde waarin software ze echt gaat proberen.
  • /etc/gai.conf bepaalt de voorkeur voor IPv4 of IPv6 voor elk programma op de machine. Het wordt volledig in commentaar meegeleverd, en één regel uit commentaar halen lost het probleem van de trage eerste verbinding door kapot IPv6 op.
  • DNS per interface is de hele reden voor de daemon. Een routeringsdomein geschreven als ~corp.example.com routeert alleen dat achtervoegsel via een interface; ~. routeert alles, en dat is meestal wat een VPN die "DNS brak" gedaan heeft.
  • Containers lossen anders op. De standaard bridge geeft de bovenliggende server van de host en geen containernamen; een zelfgemaakt netwerk geeft Docker's eigen resolver op 127.0.0.11, en namen werken.
  • systemd-resolved cachet alleen positieve antwoorden (Cache=no-negative), en de cache is alleen als root te lezen.
  • Een recursieve resolver aan het internet is een open resolver en wordt binnen enkele dagen een amplificatiewapen. Beperk recursie, en houd autoritatieve en recursieve rollen gescheiden.
  • Een browser met DNS over HTTPS negeert dit allemaal, en daarom kan hij het oneens zijn met elk commandoregeltool op dezelfde machine.

De commando's om binnen handbereik te houden:

WAT GEBRUIKT DEZE MACHINE
  resolvectl status              servers per interface, DNSSEC- en DoT-status
  resolvectl dns                 alleen de servers
  resolvectl domain              zoekdomeinen, en ~routeringsdomeinen
  nmcli dev show | grep DNS      wat NetworkManager doorgaf
  grep '^hosts:' /etc/nsswitch.conf    de volgorde die alles bepaalt

WAAR LOST EEN NAAM NAARTOE OP
  getent ahosts NAAM             als een applicatie, met de juiste volgorde
  resolvectl query NAAM          + welke interface, cache of niet, gevalideerd of niet
  dig NAAM                       DNS zelf, met voorbijgaan aan elke lokale laag
  dig +search NAAM               met de zoeklijst, als een applicatie

INPERKEN
  resolvectl query --cache=no NAAM     sla de lokale cache over
  dig @192.168.1.1 NAAM                sla de lokale daemon over
  dig @1.1.1.1 NAAM                    sla het lokale netwerk over
  sudo resolvectl monitor              zie vragen langskomen

AANPASSEN, WAAR HET GEGENEREERD WORDT
  nmcli connection modify ...          per netwerkprofiel
  /etc/systemd/resolved.conf           DNS=, DNSSEC=, DNSOverTLS=, Cache=
  /etc/hosts                           overrule een naam voor alles
  resolvectl dns LINK SERVER           tijdelijk; resolvectl revert LINK

DOCUMENTATIE
  man resolved.conf   man nsswitch.conf   man gai.conf   resolvectl --help

En lost een naam op elke machine goed op behalve op één, dan ligt de oorzaak vrijwel nooit bij het domein. Het is een regel die iemand aan /etc/hosts toevoegde tijdens een migratie die allang klaar is.

Naar boven
Linux concept: DNS-resolver
Peter Martin
Peter Martin
Joomla Specialist

Peter is Joomla specialist en Linux admin voor snelle, veilige en schaalbare websites.

Gerelateerde artikelen