IDLJ: Let's Encrypt sjekker bare IPv6

Publisert 11. desember 2025

Jeg bruker nginx og certbot for noen tjenester jeg verter. Sistnevnte er et Python script som kan skaffe og automatisk fornye TLS-sertifikater, fortrinnsvis ved å bruke Let’s Encrypt sin HTTP-01 Challenge. Dette fungerer ved at den endrer nginx sin konfigurasjon til å levere en fil på en bestemt URL, som skal bevise at du som forespør et sertifikat også har kontroll over nettsiden.

I morges opplevde jeg at et par av tjenestene hadde utløpte sertifikater. Rettere sagt fikk jeg en tekstmelding fra en forvirret bruker. Jeg gikk på server og prøvde å fornye sertifikatene manuelt med sudo certbot renew. Jeg fikk en feilmelding som så omtrent slik ut:

Certbot failed to authenticate some domains (authenticator: nginx). 
  The Certificate Authority reported these problems:
  Domain: <DOMENENAVN>
  Type:   unauthorized
  Detail: <IPv6-adresse>: Invalid response from 
    http://<DOMENENAVN>/.well-known/acme-challenge/<CHALLENGE-ID>: 404
 

Da gir det mening at sertifikatene er utløpt, certbot ikke klarer å fornye dem.

En ting som slo meg var at den prøvde å gjøre det til serverens IPv6-adresse. Jeg hadde nylig lagt inn en AAAA (altså IPv6) rad i DNS for de påvirkede tjenestene, uten noen videre testing på om det fungerte. Dette var kilden til feilen. I nginx må du eksplistitt spesifisere at en virtual host skal gjelde begge adressefamilier.

Om du bruker certbot har du sannsynligvis denne typen blokker i nginx-konfigurasjonen din, virtual hosts som lytter for HTTP-trafikk, og omdirigerer det til HTTPS:

server {
    if ($host = <DOMENENAVN>) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;

    server_name <DOMENENAVN>;
    return 404; # managed by Certbot
}
 

Denne spesifiserer listen 80;, hvilket betyr at den mottar trafikk til IPv4 på port 80. For at blokken også skal være gjeldene for IPv6-trafikk, må du også legge inn listen [::]:80;. Dette løste problemet og gjorde det mulig for Let’s Encrypt å nå utfordringsfilen.

En ganske amatørmessig feil å gjøre, som kunne lett vært unngått med enten bedre testing av konfigurasjonsendringer, eller kontinuerilg overvåkning av sertifikater som er i ferd med å utløpe. Uansett var det ikke katastrofalt, og god lærdom. Jeg skal sette opp bedre overvåkning snart, jeg lover.

P.S.

En ting å legge merke til er at på den aktuelle serveren var default virtual hosten i nginx slått på. Denne lytter eksplistitt på begge adressefamilier, så nginx-prosessen måtte også gjøre det. Om den hadde vært slått av, kan det være nginx ikke hadde lyttet på IPv6 i det hele tatt, og Let’s Encrypt hadde fått en TCP-rejection fra serveren. Da kan det være Let’s Encrypt hadde falt tilbake til IPv4, og fornyelsen hadde gått gjennom. Dette er spekulasjon, jeg har ikke testet.

Tips: legger du til en liten -v så det blir sudo certbot renew -v, forteller den deg også hva den gjør. Slik var det klart for meg at den skrev om nginx sin konfigurasjon på en måte som burde gjøre det mulig å aksessere HTTP-01 utfordringslenken i HTTP virtual hosten til nettsiden.