Cron og Bash: Sådan fornyer og konverterer vi certifikat til V2's sky-app

Illustration: Jean Tinguely. Foto: André P. Meyer-Vitali
Der er mange små og store bump på vejen mod en stabil webservice. Nogle af problemerne kan løses med scripts og tidsstyrede cron-jobs.

Version2 er gået i skyen med vores nogenlunde simple machine learning, der giver bud på emneord, når journalisten har skrevet en artikel færdig.

Det foregår via en webtjeneste, som er syet ind i vores content management system med en browser-udvidelse. Webtjenesten er flyttet hjemmefra og ind i Amazons sky-kompleks, og målet er at holde omkostningerne på 0 kroner ved at bruge gratis prøveperioder, som er på 12 måneder. Og indtil videre går det fint.

I sidste episode fik vi et gratis SSL-certifikat med tjenesten Let’s Encrypt, der byder på den slags. Det er et foretagende, der har Mozilla, Akamai og Cisco i ryggen, så det er ikke nogen ubetydelig sidegade-butik. Certifikatet var en nødvendighed for at kunne strikke vores CMS sammen med webtjenesten.

Læs også: Sådan skaber du et gratis og gyldigt SSL-certifikat

Men certifikatet er kun gyldigt i tre måneder. Derfor skal vi automatisere fornyelsen med et tidsstyret script, så jeg ikke skal sætte et flueben i kalenderen og udføre det samme arbejde igen og igen.

Dernæst er der et lidt større, og nok også mere indviklet problem, der banker på. Amazon, som vores server-instans hører hjemme hos, giver nemlig ingen garanti for serverens overlevelse. Hvis disk eller hukommelse står af i datacenteret i Virginia på USA’s østkyst, er det hele væk. Amazon stiller en ny instans til rådighed, men den er ganske tom til at starte med, og jeg får formentlig også et nyt IP-nummer i den anledning.

Det bliver noget rod, hvis jeg skal sætte det hele op igen med håndarbejde. Jeg kan godt gå tilbage til de gamle artikler, for at se hvad jeg gjorde, men det tager alligevel noget tid, som jeg måske ikke har, den dag, hvor uheldet er ude.

Løsningen er, også her, at skrive et script, som kan udrulle systemet i en ruf. Webtjenesten gemmer ikke noget på disk, så der er ingen behov for backup.

Automatiseret fornyelse

Men først skal vi tilbage til den automatiske fornyelse af certifikater, der altså udløber efter tre måneder.

I sidste afsnit brugte vi Let’s Encrypt-programmet certbot-auto, der er super nemt at anvende. Certifikaterne fornyes med den simple kommando:

sudo certbot-auto renew

Programmet kvitterer med en masse linjer, der slutter med:

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/v2emneord.ddnsfree.com/fullchain.pem (success)

Men som vi var inde på i forrige artikel, kræver vores Java-webserver lidt mere end det.

Vi skal først samle to certifikater i pem-formatet til et nyt certifikat. Derefter skal vi konvertere det samlede certifikat til pkcs12-formatet, som Javas værktøj anvender. Til sidst skaber vi en Java-keystore ud fra certifikatet, som webserveren kan læse og anvende til at bekræfte domænets gyldighed med.

Vi samler alle kommandoerne fra den sidste artikel til et script:

#!/bin/sh
# Skal køre som root.
CERT_STI=/etc/letsencrypt/live/v2emneord.ddnsfree.com
JAVA11_STI=/usr/lib/jdk-11.0.2/bin 
WEBTJENESTE_STI=/home/ec2-user/webtjeneste/bin
/usr/local/bin/certbot-auto renew
cat $CERT_STI/privkey.pem $CERT_STI/fullchain.pem > \
 $CERT_STI/combined.pem
openssl pkcs12 -export -password pass:p1zzarest -out \
 $CERT_STI/combined.pkcs12 -in $CERT_STI/combined.pem
$JAVA11_STI/keytool -noprompt -importkeystore -srckeystore \
 $CERT_STI/combined.pkcs12 -srcstoretype pkcs12 -destkeystore \
 $WEBTJENESTE_STI/certifikat.jks -storepass p1zzarest \
 -srcstorepass p1zzarest

Først har vi den berømte 'shebang-linje,' der fortæller terminalprogrammet, hvad for et program der skal udføre scriptet. Bin/sh er system-fortolkeren, som i vores tilfælde blot er Bash.

Vores script skal køre som administrator-brugeren root - der skal ‘sudo’ foran, når det afvikles - og det skriver jeg lige i en kommentar til mig selv, så jeg ikke glemmer det til en anden gang.

Først definerer jeg nogle variable, CERT_STI, JAVA11_STI osv., så det hele er lidt nemmere at overskue.

Dernæst udfører jeg certbot-auto renew-kommandoen som nævnt før. Derefter kommer cat-kommandoen, der konkatenerer privkey.pem og fullchain.pem til en ny fil, combined.pem.

Med openssl konverterer vi formatet fra pem til pkcs12, og til forskel fra sidst skriver vi det password, der beskytter den nye fil, i selve kommandoen, så den ikke skal udføres interaktivt. Det sker med flaget -password pass:p1zzarest - Ja, jeg deler gerne mit password med dig, kære læser - det bliver imellem os, håber jeg.

Samme sag gør sig gældende med den efterfølgende kommando, som er Javas keytool, der i modsætning til sidste artikel her er udstyret med flaget -noprompt, som neddrosler kommandoens spørgelyst. Det medfører, at scriptet kan udføres i nattens mulm og mørke, mens devops-programmøren snorksover sin retfærdige søvn. Slutproduktet er certifikat.jks, som webtjenesten kan forstå.

Timing med cron

Næste punkt på dagsordenen er at sætte scriptet til at blive udført automatisk med jævne mellemrum, så vi ikke går glip af en fornyelse og lægger webtjenesten ned.

Linux benytter som regel programmet cron til at udføre programmer og kommandoer på bestemte tidspunkter. Cron-programmet udstyres med en tekstfil, hvor hver linje er en opgave, der skal udføres på et bestemt tidspunkt. I vores tilfælde ser det sådan ud:

0 0 * * 0 /home/ec2-user/cert-forny.sh >> /home/ec2-user/forny.log 2>&1

Det første tal angiver minuttallet, næste tal timetallet. Det næste tal er datoen i måneden, som her er en stjerne, der betyder ‘alle dage i måneden.’ Næste tal er måneden, hvor stjernen også betyder hver måned, og sidste tal er ugedagen, hvor 0 betyder søndag.

Det hele betyder: Udfør kommandoen til højre hver søndag kl. 00:00, hvor tidszonen på vores server er UTC, altså klokkeslættet ved Greenwich-observatoriet i London. Med sommertid betyder det klokken to om natten på disse egne, og det virker som et godt tidspunkt at genstarte vores webtjeneste på.

Kommandoen til højre afvikler scriptet cert-forny.sh, som var det vi skrev tidligere. Vi vil gerne opsnappe uddata til til en logfil. Det er godt at kigge i, hvis noget senere skulle gå galt. Normalt vil jeg blot tilføje til logfilen forny.log med ‘append’-operatoren >>, men det virker ikke - der kommer ikke noget ned i logfilen, når jeg tester.

Lidt søgning på nettet fortæller, at jeg skal tilføje 2>&1 efter kommandoen. Det er der en forklaring på, hvis man tørster efter svaret.

Samlet set kører vi disse tre kommandoer, med ‘sudo’ foran, altså som root-brugeren:

echo "0 0 * * 0 /home/ec2-user/cert-forny.sh \
 >> /home/ec2-user/forny.log 2>&1" > cronfil
crontab cronfil 
rm cronfil

Vi skriver strengen ovenfor med echo-kommandoen til en midlertidig tekstfil, cronfil. Dernæst kører vi crontab-programmet med tekstfilen som argument. Den overskriver alle andre jobs, og det kan jeg tillade mig i dette tilfælde, da min server lige nu ikke har andre tidsstyrede opgaver på programmet. Da jeg kører crontab med sudo, altså som root, udføres cert-forny-scriptet også som root, hvilket er påkrævet.

Jeg tester, ved at ændre klokkeslættet til om fem minutter, og det fungerer fint. Og så må jeg lige efter-tjekke på mandag, om det har kørt som det skal, søndag nat.

Jeg skal også genstarte webtjenesten, så det muligvis nye certifikat indlæses. Det gør jeg ved hårdhændet at køre killall-kommandoen, der råt og brutalt slukker for Java.

Jeg har nemlig ikke fået implementeret en stop-knap i webtjenesten endnu, men den voldsomme kommando gør ikke megen skade, da machine learning-programmet ikke skriver til disk eller andet.

Det bliver til et lille script, der ser sådan ud:

JAVA11_STI=/usr/lib/jdk-11.0.2/bin 
killall -9 java
cd /home/ec2-user/webtjeneste/bin
nohup $JAVA11_STI/java -cp /home/ec2-user/webtjeneste/bin -Xms800M \
 v2ml.WebService &>/dev/null & 

Dette script skal udføres efter at certifikatet er fornyet. Jeg laver et nyt cron-job, som skal udføres som en almindelig bruger, da webtjenesten ikke skal køre som root. Det ville være en sikkerhedsrisiko, da en fejl i programmet måske kunne give angribere administrator-adgang til systemet.

Scriptet kører jeg søndag nat, 15 minutter efter det andet cron-job. Det ser sådan ud:

echo "15 0 * * 0 /home/ec2-user/genstart-tjeneste.sh \
 >> /home/ec2-user/genstart.log 2>&1" > cronfil
crontab cronfil 
rm cronfil

Det var hvad vi nåede i dagens episode.

Næste gang ser vi på, hvordan vi kan udrulle hele moletjavsen på en blank instans - kode, scripts og cronjobs - fra et script på en lokal terminal og lige over på serveren, i en ruf. Så hæng på kanalen.

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

Denne artikel viser at det er fuldt ud muligt at udtrykke sig om tekniske emner uden at forfalde til dangelsk (prøv f.eks. at lytte til Radio24syv's "Millinærklubben" - det er ganske forfærdeligt. Efter 8 år har de endnu ikke fundet ud af at det hedder "halvleder" på dansk - ikke "semikondoktor").

Godt gået!

  • 0
  • 0
Log ind eller Opret konto for at kommentere
IT Company Rank
maximize minimize