Sådan skaber du et gratis og gyldigt SSL-certifikat
Verdens 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.
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 *all* 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.
Det er 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:
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.
