Sådan skaber du et gratis og gyldigt SSL-certifikat

Illustration: Bigstock/Ffmr
Tjenesten Let’s Encrypt vil kryptere alverdens webservere med gratis certifikater, der fornyes automatisk. Vi bruger det til Version2 emneords-tjeneste.

Al webtrafik skal krypteres. Det er visionen fra Let’s Encrypt, som er et non-profit-foretagende med store spillere bag sig, blandt andre Mozilla, Akamai og Cisco.

Derfor kan tjenesten tilbyde gratis certifikater, der generes og fornyes automatisk. Til gengæld er certifikaterne kortlivede - de holder kun i tre måneder.

Og et gratis certifikat er lige noget, vi står og har brug for, fordi Version2 er gået i skyen. Ved hjælp af nogenlunde simpel maskinlæring har vi rigget en webtjeneste sammen, som kan give bud på emneord, når journalisten har skrevet en artikel færdig.

Læs også: Version2 bliver Devops-hajer i skygge-skyen (sådan da)

Webtjenesten er syet ind i vores content management system med en browser-udvidelse, og da vores cms kører HTTPS - naturligvis - skal webtjenesten også bruge krypteret trafik til det ajax-kald, der maler emneord op med gult i en dialog. Man kan nemlig ikke blande krypterede og ukrypterede kilder.

I sin tid, da vi flikkede den første spæde prototype sammen, kørte serveren bare på min egen pc, og certifikatet var hjemmelavet. Der var tale om et ‘self-signed’ certifikat, som ikke er underskrevet at et rod-certifikat. Det betyder, at browseren kun vil slippe brugeren ind på tjenesten, hvis man foretager en såkaldt sikkerhedsundtagelse.

Nu er vores lille webtjeneste flyttet hjemmefra og ind i Amazons sky-kompleks, så det går ikke længere med det interimistiske certifikat. Målsætningen er at holde omkostningerne på 0 kroner ved at bruge gratis tilbud, og indtil videre går det fint. Let’s Encrypt passer lige ind i planen.

Lad os kryptere

Et ‘rigtigt’ certifikat som dem, Let’s Encrypt udsteder, bekræfter at kommunikationen er krypteret, og at det domænenavn, der står i browserens adresselinje, er korrekt. Vi har allerede et domænenavn, nemlig det, som Amazon har udstyret vores instans med:

ec2-18-223-114-109.us-east-2.compute.amazonaws.com

Mon ikke det er godt nok til en foreløbig løsning? Vi prøver i hvert fald. Så kan vi jo svinge betalingskortet på et senere tidspunkt og købe vores helt eget domæne, hvis det skulle være.

Let’s Encrypt byder på en række muligheder. Hvis man benytter en gængs webserver som Apache eller Nginx på en populær Linux-server, er det nemt som 1-2-3. Let’s Encrypts programmer findes i distributionernes software-biblioteker og der kræves ikke mere end lidt ubøvlet konfiguration.

Men så nemt skal det ikke være for os. Vi har implementeret vores webtjeneste i Java, og benyttet en ganske lille webserver. Vi har også valgt Amazons egen Linux som platform. Begge dele betyder, at vi må installere Let’s Encrypts certifikat-robot på egen hånd.

Det gør vi efter tjenestens anvisninger med kommandoerne

wget https://dl.eff.org/certbot-auto
sudo mv certbot-auto /usr/local/bin/certbot-auto
sudo chown root /usr/local/bin/certbot-auto
sudo chmod 0755 /usr/local/bin/certbot-auto

Kort fortalt fungerer Let’s Encrypt ved at genere en underskrevet fil, en såkaldt nonce, via terminalprogrammet. Derefter prøver Let’s Encrypts server at få fat i filen, ved at slå det angivne domænenavn op i internettets telefonbog, dns-systemet, og hente noncen via HTTP eller HTTPS.

Hvis nonce-filen kan findes og er gyldig, konstaterer Let’s Encrypt, at man har kontrol over domænet, og kvitterer med et certifikat, der altså har en løbetid på tre måneder.

Porten med hængelåsen

Den udgave af kommandoen, som vi benytter, kaldes ‘standalone.’ Her benytter Let’s Encrypt HTTP på port 80 til at finde noncen, og certbot-programmet kommer med sin egen indbyggede webserver, så det er nemt.

Først åbner vi port 80 i Amazons webkonsol, som tidligere beskrevet. Derefter kører vi denne kommando, hvor argumentet til -d-flaget er vores Amazon-domæne:

sudo /usr/local/bin/certbot-auto certonly --standalone --preferred-challenges http -d ec2-18-223-114-109.us-east-2.compute.amazonaws.com

Det giver os denne sure kommentar fra programmet:

FATAL: Amazon Linux support is very experimental at present...
if you would like to work on improving it, please ensure you have backups
and then run this script again with the --debug flag!
Alternatively, you can install OS dependencies yourself and run this script
again with --no-bootstrap.

Vi prøver at køre kommandoen igen med --no-bootstrap-flaget, og se bare: Nu går det hele bedre.

Creating virtual environment...
Installing Python packages...
Installation succeeded.
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None

Programmet beder om min email-adresse, og det lyder fornuftigt nok:

Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): tan@ing.dk

Og så går det galt.

An unexpected error occurred:
Error creating new order :: Policy forbids issuing for name
Please see the logfiles in /var/log/letsencrypt for more details.

Amazon på den sorte liste

Et kig i logfilen siger blot det samme, som der står i fejlmeddelelsen, men lidt søgning på nettet fortæller, at Amazons sky er på en liste over domæner og subdomæner, som Let’s Encrypt ikke vil udstede domæner til.

Argumentationen er fornuftig nok: Amazons subdomæner er automatisk genereret ud fra ip-nummeret og det datacenter, instansen hører hjemme i. Hvis min instans går i udu, kan Amazon give mig et andet, og nogle andre får på et senere tidspunkt mit gamle subdomæne. Der er altså ikke den store klarhed om, hvordan kontrol over subdomænet hænger sammen med certifikatet.

Så vi skal bruge et domæne. Som sædvanlig vil vi ikke betale for noget som helst, så vi benytter en gratis ‘dynamisk’ dns-tjeneste, hvor man kan skifte ip-nummer via et api - noget, som vi får brug for senere, når vi skal automatisere vores opsætning.

Denne slags tjenester er der flere af. Vi benytter den, der hedder Dynu.com. Her registrerer vi subdomænet

v2emneord.ddnsfree.com 

og peger den på vores Amazon-instans, ved at indtaste ip-nummeret. Vi tjekker lige, at domænet spiller, ved at indtaste adressen

https://v2emneord.ddnsfree.com:8000/apps/emneord/test

– i en browser, og så dukker test-siden op (efter at vi har foretaget en sikkerhedsundtagelse, da vores rigtige certifikat ikke er installeret endnu.) Alt er godt.

Vi kører certbot-kommandoen fra før, men nu med vores sprit-nye domæne:

sudo /usr/local/bin/certbot-auto certonly --standalone --preferred-challenges http -d v2emneord.ddnsfree.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for v2emneord.ddnsfree.com
Waiting for verification...
Cleaning up challenges
 
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/v2emneord.ddnsfree.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/v2emneord.ddnsfree.com/privkey.pem
   Your cert will expire on 2019-08-08. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot-auto
   again. To non-interactively renew <em>all</em> of your certificates, run
   "certbot-auto renew"

Succesen er hjemme - vi har fået vores certifikat. Der er dog en sidste bakketop, vi skal over.

Bøvl med formater

Certbot producerer en række certifikater i pem-formatet. Det er blot en tekstfil, der indeholder data i base64-indkodning. Vores webserver kører Java, så certifikaterne skal samles og omskabes til formatet pkcs12, som Javas kryptografiske biblioteker benytter. Endeligt skal det nye certifikat importeres i vores webservers keystore.

For mange år siden automatiserede jeg adgangen til en webtjeneste, hvor jeg pillede et klient-certifikat ud af en browser og puttede det ind i en Java-keystore. Det var temmelig lige ud af landevejen, men min nye situation er en del mere kompleks.

Internettet har en række bud på fremgangsmåden. Det forslag, jeg har mest fidus til, går ud på at kombinere, eller konkatenere, som det hedder på programmør-sprog, privat-nøglen privkey.pem med filen fullchain.pem, der indeholder den offentlige nøgle samt certifikat-udstedernes offentlige nøgle - den såkaldte ‘kæde’, hvor hver af nøglerne er underskrevet af udstederen i laget ovenfor. Til sidst ender man med et rod-certifikat, som også er installeret i selve browseren, og det er denne ‘kæde af tillid’, der borger for et domænes rigtighed.

Jeg smækker de to filer sammen i en teksteditor - privat-nøglen skal først, lyder det. Derefter benytter jeg openssl til at konvertere den samlede fil til pkcs12-formatet:

openssl pkcs12 -export -out combined.pkcs12 -in combined.pem

Jeg skal angive et kodeord, som krypterer certifikatet. Kodeordet skal jeg bruge i næste skridt, hvor jeg bruger Javas keytool-program til at importere nøglen.

Jeg vil teste det lokalt på min udviklings-webserver, for at gøre det lidt nemmere at finde og rette fejl. Så jeg kopierer certifikatet fra Amazon til min Windows-maskine og sletter først mit gamle hjemmelavede certifikat i keystoren:

C:\Program Files\Java\openjdk-11_windows-x64_bin\jdk-11\bin>keytool -delete -alias webservice -keystore "C:\Users\tan\eclipse-workspace\V2 Maskinlæring\minkeystore.jks"
Enter keystore password:

Jeg indtaster kodeordet for keystoren - det er ‘p1zzarest’ - nu kender du det også, kære læser.

Så importerer jeg certifikatet i keystoren:

C:\Program Files\Java\openjdk-11_windows-x64_bin\jdk-11\bin>keytool -v -importkeystore -srckeystore "C:\Users\tan\AppData\Local\Temp\combined.pkcs12" -destkeystore "C:\Users\tan\eclipse-workspace\V2 Maskinlæring\minkeystore.jks" -deststoretype JKS
Importing keystore C:\Users\tan\AppData\Local\Temp\combined.pkcs12 to C:\Users\tan\eclipse-workspace\V2 Maskinlæring\minkeystore.jks...
Enter destination keystore password:
Enter source keystore password:
Entry for alias 1 successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled
[Storing C:\Users\tan\eclipse-workspace\V2 Maskinlæring\minkeystore.jks] 

'Source keystore password' er det kodeord, vi brugte, da vi skabte pkcs12-filen med openssl.

Jeg starter min lokale webserver op, og… den er hel gal!

Jeg får en masse hæslige fejlmeddelelser fra Javas kryptografiske biblioteker. Det er noget med ‘padding’. En søgning på nettet viser, at den fejlmeddelelse kan komme af mange forskellige årsager. Det gør det svært at fejlsøge.

Jeg sidder og småbander lidt for mig selv i den næste times tid. Jeg prøver det ene, og det andet, og lige lidt hjælper det. Så tæt på målet, og dog så langt fra…

Det er sidst på eftermiddagen, og jeg er lige ved at smide håndklædet i ringen og vende næsen hjemad.

Mit sidste skud i tågen er at benytte en variant af keytool-kommandoen, der kan omskabe certifikatet til en keystore i et hug. Det ser sådan ud:

keytool -importkeystore -srckeystore "C:\Users\tan\AppData\Local\Temp\certs\combined.pkcs12" -srcstoretype pkcs12 -destkeystore "C:\Users\tan\AppData\Local\Temp\certs\minkeystore.jks"

Jeg tester, og tjuhej, mit lokale serverprogram æder certifikatet og starter op! Dagen (og nattesøvnen) er reddet.

Derefter kopierer jeg min keystore over på Amazon-instansen og genstarter mit sky-serverprogram.

Og så ind på test-siden, https://v2emneord.ddnsfree.com:8000/apps/emneord/test:

Illustration: Version2

Hængelåsen er på plads i browserens adresselinje. Herligt, herligt.

(Ja, medgivet, testsiden er ikke det mest æstetiske, man kan forestille sig, men det er min baby. Og tekst-indkodningsfejlen findes ikke i webtjenesten.)

Jeg finder måske aldrig ud af, hvorfor den ene kommando fejlede og den anden virker. Men jeg er egentlig også ligeglad. Det vigtigste er, at sikkerheden nu er på plads.

Næste skridt handler om at automatisere. Der er i hvert fald to punkter på dagsordenen:

Mit Let’s Encrypt-certifikat udløber om tre måneder, så jeg skal have sat automatisk fornyelse op.

Og, som visse har bemærket, giver Amazon ingen garanti for instansens overlevelsesevne. Det hele kan blive skrottet og jeg må starte op med et nyt ip-nummer. Jeg skal altså kunne ‘provisionere’ instansen, som det vist hedder, uden at det tager for megen tid. Det kræver et script, og det kigger vi på i et kommende afsnit af denne tilsyneladende uendelige saga. Stay tuned, folkens.

Tips og korrekturforslag til denne historie sendes til tip@version2.dk
Følg forløbet
Kommentarer (8)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Povl H. Pedersen

Enterpriser løsninger fra Amazon giver vist mulighed for at lade Amazon selv udsteder certifikater.
CloudFlare har også sin egen CA - Men man bør stadig trække et cert til sin backend server.

Derudover skal man være opmærksom på, at LetsEncrypt certifikater lever 3 måneder, så skal de fornyes. Det er trivielt fra et cronjob.

Det store problem er Microsoft. De har ikke nogen CA, eller gratis service i deres cloud. Og det er også sværere med letsencrypt og autorenew.

Simon Mikkelsen

Derudover skal man være opmærksom på, at LetsEncrypt certifikater lever 3 måneder, så skal de fornyes. Det er trivielt fra et cronjob.

Let's Encrypt har deres CertBot som man kan installere og så tager den sig af fornyelsen. På min webserver med Plesk skal jeg bare sætte et kryds og angive min e-mail. Så sker det helt automatisk og virker fantastisk.

Lars Petersen

Til Windows bruger jeg WACS også kaldet win-acme der ledes af hollænderen Tinus Wouter. Frisk med nye releases og tager gerne imod bug reports.

På Linux har certbot nogle gange drillet så jeg bruger oftere acme.sh - et shell-script, der dog ikke har sit ophav fra Saint Helena som man måske skulle tro, men fra kineseren Neilpang, og vi håber så ikke at det 180kB store shellscript er inficeret af den kinesiske efterretningstjeneste.

Hvad bruger du selv?

Jens Beltofte

CloudFlare har også sin egen CA - Men man bør stadig trække et cert til sin backend server.

Hos en af vores kunder der benytter Cloudflare Enterprise benytter Cloudflare certififaceter fra Comodo og ikke deres egen CA.

Som med LetsEnrypt løber Cloudflares certifikater heller ikke i evigheder. Så vidt jeg kan se løber de i 6 måneder, men fornys dog automatisk af Cloudflare.

En træls (er jo jyde) ting ved Cloudflare er, at verificering af domænet ved oprettelse af SSL certifikaterne sker ved at opsætte 2-3 CNAMEs på domænet. Disse CNAMEs er ikke tilgængelige i deres UI, men skal hentes fra deres API....... det er simpelthen for dumt....

Morten Christensen

Jeg bruger Certbots script letsencrypt-auto.sh, men det er på en mailserver med 4 maildomæneruden tilhørende webserver.
Derfor kan Lets Encrypt ikke slå identifikationen, kaldet "nonce" ovenfor, op via http.

Ved fornyelse danner scriptet istedet 4 nye nøgler som jeg manuelt skal kopiere over i et txt-felt hos GratisDNS på de 4 domæner og vente på, det er publiceret ud i DNS-systemet, før sidste del af fornyelsen kan ske.

En simplere håndtering kunne bruges.

Maciej Szeliga

Jeg bruger Certbots script letsencrypt-auto.sh, men det er på en mailserver med 4 maildomæneruden tilhørende webserver.
Derfor kan Lets Encrypt ikke slå identifikationen, kaldet "nonce" ovenfor, op via http.

Ved fornyelse danner scriptet istedet 4 nye nøgler som jeg manuelt skal kopiere over i et txt-felt hos GratisDNS på de 4 domæner og vente på, det er publiceret ud i DNS-systemet, før sidste del af fornyelsen kan ske.

En simplere håndtering kunne bruges

Hvad er din tid værd?

Et std. *certifikat koster 2000 kr hvert andet år - og alm. certifikater koster en del mindre - det er under 100 kr om måneden - er din tid værd så lidt at du vil spilde tiden på at skifte certifikater ?

Det er IKKE fordi jeg har noget imod LetsEncrypt, jeg stiller samme type spørgsmål til folk som kører 30 km for at spare 5 øre literen på benzin...

Sune Marcher

Først: hvorfor bruge en eller anden random dyndns service, i stedet for at få oprettet et *.version2.dk subdomæne?

I stedet for at udstille din Java servlet (går jeg ud fra det er) direkte til internettet, og derudover have bøvl med de forbistrede Java keystores... så vil jeg anbefale at sætte enten nginx eller Caddy op foran som reverse proxy.

Det er ret simpelt, og specielt Caddy er meget nemt – ikke mindst fordi den har integreret LetsEncrypt support. Basal Caddy opsætning er ekstremt simpel, med følgende directory struktur (hvor 'dotcaddy' bliver administreret af Caddy, og bruges til LetsEncrypt filer):

/srv/http  
├── dotcaddy  
│   ├── acme  
│   ├── ocsp  
│   └── uuid  
└── sites  
    ├── Caddyfile  
    ├── eksempel.dk

Og følgende indhold af 'Caddyfile':

eksempel.dk {  
    log stdout  
    tls hejsa@eksempel.dk  
    mime .json application/json  
    gzip  
    import common  
    root ./eksempel.dk  
}

Er man i luften.

Reverse proxying til en backend applikation (forudsat de kører på samme server) burde være noget i stil med...

emneord.version2.dk {  
    log stdout  
    tls hejsa@version2.dk  
    proxy / localhost:8000  
}

Derudover er Caddy skrevet i Go, så det er en enkelt binary på ~20meg uden dependencies. Hvis man vil køre det i en Docker container behøver det ikke noget Linux base image – det er super lightweight :)

Log ind eller Opret konto for at kommentere