De afgelopen maanden zijn we druk geweest met een grote schoonmaak van infrastructuur en broncode. De resultaten daarvan delen we in een serie artikelen, waarvan dit de eerste is. Deze artikelen zijn bedoeld voor mensen die geïnteresseerd zijn in hoe Basisbeveiliging en Security Baseline achter de schermen werken.
In dit artikel zoomen we in op grote twee stukken infrastructuur: de database en het besturingssysteem. We migreerden van MySQL naar PostgreSQL en van Ubuntu naar NixOS. Het voordeel is niet alleen dat beheer makkelijker is omdat alle omgevingen technisch hetzelfde zijn. Er zitten allerlei voor- en nadelen aan deze nieuwe technieken die we in dit artikel belichten.
Vind je transparantie, veiligheid, soevereiniteit, toegankelijkheid en privacy belangrijk? Word dan deelnemer van de Internet Cleanup Foundation en ondersteun onze missie om het internet transparant te maken. We meten al meer dan 10.000 organisaties en 300.000 adressen en maken deze informatie beschikbaar voor iedereen. Meer over deelnemerschap.
I love… LAMP, I love… LNPPD
Net als veel web-applicaties uit het vorige decennium begon Basisbeveiliging als LAMP stack. Dat is een combinatie producten: Linux, Apache, MySQL en PHP, wat destijds dé technieken waren om snel een applicatie in de lucht te krijgen. Wij ondervonden dat dit onvoldoende kon meegroeien met het project.
Alle producten in de LAMP stack zijn inmiddels vervangen. Als eerste werd webserver Apache vervangen door nginx. Dat leverde meer performance, caching en configureerbaarheid. De programmeertaal PHP werd vervangen door Python in combinatie met het Django framework. Python leest prettiger dan PHP en het Django Framework is behoorlijk robuust (‘saai’) en biedt templating, ORM, database-migraties en een standaard beheeromgeving. Alles daarvan, behalve templating, gebruiken we dagelijks.
Linux is de enige overblijver uit de stack, maar de smaak is wel veranderd naar NixOS. Dat is een Linux met een aantal bijzonderheden waar we in het tweede deel van het artikel naar kijken. De vierde en laatste pilaar, de database MySQL, is na tien jaar trouwe dienst vervangen door PostgreSQL. Beide migraties komen niet uit de lucht vallen: we waren hier al enkele jaren naartoe aan het werken.
Het vervangen van deze technieken levert weinig hype- of hipsterpunten op. Alle vervangers zijn open source en er wordt ten minste 14 jaar aan gesleuteld. De luchthoorns kunnen weer in de tas. Als we de huidige stack moeten afkorten dan komen we nu uit op LNPPD, wat een stuk minder fijn bekt.
| Component | Oude techniek | Eerste versie | Nieuwe techniek | Eerste versie |
| Besturingssysteem | L-inux (Ubuntu) | 2004 | L-inux (NixOS) | 2012 |
| Webserver | A-pache | 1995 | n-ginx | 2004 |
| Database | M-ySQL / MariaDB | 1995 | P-ostgreSQL | 1996 |
| Programmeertaal | P-HP | 1995 | P-ython | 1991 |
| Framework | – | – | D-jango | 2005 |
Moeten we nou echt naar PostgreSQL…?
Wij wisten dat PostgreSQL in veel opzichten een betere en professionelere database is dan MySQL, maar door de leercurve hebben we dit afgehouden. Gezien we geen database-beheerders zijn en de ORM-laag van Django de achterliggende database minder relevant maakt konden we blijven bij wat we al kenden.
De laatste jaren gaf MySQL ons steeds meer problemen qua performance en beheer. Telkens bood PostgreSQL hiervoor een betere oplossing. Onze nieuwe websites, basisbeveiliging.be, basistoegankelijkheid.nl, securitybaseline.eu, gaven de kans om hiermee te experimenteren. Nu zijn we op het punt gekomen dat we afscheid hebben genomen van MySQL, of specifieker MariaDB (iets met licenties). Al onze productie-databases draaien nu op PostgreSQL.
Een belangrijk detail is dat we altijd al de vrijheid tussen databases wilde behouden. Onze CI-teststraat voerde namelijk sinds het begin van het project al tests uit op zowel MySQL als PostgreSQL: zo werd er nooit software voor één specifieke achterliggende database ontwikkeld. Dit principe heeft ons heel veel onverwachte problemen bespaard. Maar we laten dit principe nu los omdat er geen betere open source database is dan Postgres voor ons soort werk.
Besparen van schijfruimte
De overstap bij nieuwe sites was heel makkelijk, want de database was nog leeg. Maar voor bestaande sites was dat lastiger: Basisbeveiliging bevat ongeveer 200 gigabyte aan data. Het verhuizen van gegevens tussen databases is gedaan met pgloader. Dat project kan gegevens van allerlei databases naar PostgreSQL overzetten. Het biedt uitgebreide instellingen over hoe de data verplaatst moet worden, zoals wat wel en niet meegenomen wordt en hoe gegevens tussen databases vertaald moeten worden. Na uitgebreid testen op de demo- en acceptatie-omgevingen hebben we eerst onze Belgische site gemigreerd. Deze is met enkele honderden organisaties kleiner dan Nederland wat omzetten snel maakt, maar belangrijker: het is echte data.
Basisbeveiliging.nl heeft 10 jaar aan meethistorie, dus deze situatie was anders. Eerst moest er veel schijfruimte worden vrijgemaakt zodat de database twee keer zou passen: 1x in MySQL en 1x in PostgreSQL. Gezien we niet in de cloud draaien maar op gesponsorde hardware prikken we er niet zomaar even een schijfje bij. De benodigde schijfruimte kon worden vrijgemaakt door wat achterstallig huishouden te doen en tijdelijke opgeslagen gegevens, cache, weg te gooien. Dat we dit uiteindelijk niet nodig hadden wisten we toen nog niet.
Een van de eigenschappen van PostgreSQL is datacompressie. We worstelden al langere tijd met schijfruimte en traagheid van grote tabellen: deze moeten namelijk grotendeels in het geheugen worden geladen, en dat moet allemaal passen om snel te blijven. De uiteindelijke grote van de PostgreSQL database kwam uit rond de 75GB, een besparing van ~60%.
Zo min mogelijk downtime
Voor en tijdens de migratie willen we zo min mogelijk verandering op de database hebben. Dat levert alleen verstoring op en resultaten die misschien maar half mee gemigreerd of niet gebackupt worden. Daarom zijn voor de migratie alle scanners en functionaliteit van Basisbeveiliging uitgezet. Maar de website bleef wel gewoon bereikbaar samen met veel van de meetgegevens. Dit komt omdat we sterk inzetten op de cache functionaliteit van nginx. Alle drukst bezochte pagina’s op onze sites worden automatisch gecached: ook als de achterliggende software of database uit staan worden de al opgeslagen gegevens getoond aan bezoekers. Dat is handig, omdat we hierdoor de hele backend kunnen uitzetten of herstarten zonder dat bezoekers er iets van merken. Dat is iets wat we al nagenoeg dagelijks doen, zoals bij uitrol van nieuwe softwareversies. In dit geval gaf het ons rust zodat we de migratie zorgvuldig konden uitvoeren zonder haast en fouten… of zo dachten we.
Die fouten kwamen eigenlijk gelijk al de hoek kijken, of liever gezegd na een uur of 2 in de eerste migratiepoging. Waarschijnlijk was het toch iets teveel voor pgloader om in een keer de hele database over te zetten. Na wat pogingen om de parameters van pgloader te tunen zijn de databasetabellen uiteindelijk per batch gemigreerd. De grootste tabel met de meetgegevens was tot het laatste bewaard. Deze aanpak verliep uiteindelijk zonder problemen en aan het einde van de dag was de migratie klaar. Het overzetten van de meetgegevens tabel heeft uiteindelijk 5 uur geduurd.
De volgende dag hebben we stukje bij beetje de applicatie weer opgestart om de load op de nieuwe database te testen en eventueel te tunen en konden alle scanners weer aangezet worden om nieuwe data te vergaren en rapporten te maken. Zodat we hierna door konden met de volgende stap in de migratie, van Ubuntu naar NixOS.

Alle servers op NixOS
Eerder dit jaar heeft Johan al een lezing gehouden op Cfgmgmtcamp over ons proces op weg naar Nix en NixOS. We gebruiken Nix al jaren om onze ontwikkel omgevingen te beheren en consistent te houden. Recent zijn we al onze systemen gaan migreren naar NixOS en deze maand is dat eindelijk afgerond en draait onze volledige infrastructuur op NixOS.
Het “serverpark” van Basisbeveiliging bestaat momenteel uit tien machines met afzonderlijke taken zoals het draaien van de website, ons weblog en het meten van het internet.

Beheer met Puppet verloor zijn glans
De met de hand in elkaar gezette LAMP stack hielp met het snel starten van Basisbeveiliging. Maar al deze onderdelen in deze stapel bewegen, waardoor je steeds moet opletten dat het niet omvalt. Een manier om dat te doen is om te omschrijven hoe je wil dat het systeem werkt, en dat dit automatisch wordt toegepast. Dat heet configuratiebeheer.
Linux (Ubuntu) is het fundament van de stapel gebleven, omdat de openheid hiervan de beste mogelijkheden biedt voor beheer en aanpasbaarheid. Voor beheer is destijds Puppet gekozen, omdat we daar de meeste kennis in hadden en het een uitgebreid ecosysteem aan modules en ondersteunde applicaties bood. Hier is uiteindelijk deze configuratie uit gerold waar we de afgelopen jaren ons platform op hebben gedraaid.
Maar Puppet bleek een minder goede oplossing voor de toekomst. De community versplinterde een paar jaar geleden toen Puppet werd overgenomen door Perforce. Ondersteuning van modules liet vaak te wensen over of leverde een dependency hell op. Ondertussen groeide de wens om de ontwikkelomgeving meer te harmoniseren met de productieomgeving, iets wat we met Docker containers probeerden op te lossen maar wat weer zijn eigen problemen gaf. Na wat korte excursies in container based oplossingen zoals CoreOS en Kubernetes kwamen Nix en NixOS op ons pad.
Alles is Nix
Nix is een package manager die o.a. op Linux en macOS draait: ofwel onze productie én ontwikkel systemen. Dit stelt ons in staat op alle systemen dezelfde software te draaien en te bouwen. Nix heeft een uitgebreide bibliotheek aan kant en klare software, genaamd nixpkgs, en het is ook nog makkelijk om je eigen recepten te bouwen die herbruikbaar zijn door anderen.
NixOS bouwt verder op deze nixpkgs en maakt het tot een Linux systeem. Omdat alles in Linux een bestand is en packages gewoon verzamelingen bestanden zijn, kan NixOS het hele besturingssysteem optrekken naar je eigen specificatie door verschillende packages te bouwen en deze samen te voegen tot één Linux systeem. Deze specificatie wordt vastgelegd in Nix configuratiebestanden, waarin declaratief de opbouw van het systeem wordt opgesteld. Dus wat je wil hebben, niet hoe dat moet gebeuren. Alle aspecten van het systeem komen daar aan bod, zoals: users die aanwezig moeten zijn, programma’s en hun instellingen, firewalls en security of de layout van de opslag.
Deze manier van werken levert een paar grote winsten. De eerste is dat updates en rollbacks van het besturingssysteem triviaal: ze zijn binnen enkele seconden uitgevoerd en niet destructief. Ook is het mogelijk om een complete systeem te bouwen en lokaal te evalueren zonder het daad werkelijk te hoeven uit te rollen. Zo wordt snel duidelijk of een bepaalde verandering in de configuratie het gewenste effect gaat opleveren. Een ander voordeel van NixOS is dat verschillende versies van applicaties probleemloos naast elkaar kunnen draaien. Hierdoor zijn we minder aangewezen op containers voor onze applicatie wat ontwikkeling weer sneller maakt. Ook is het makkelijk om andere applicaties en het operating systeem onderdeel te laten zijn van “de applicatie” zodat je niet bestaande functionaliteit zelf hoeft te herbouwen maar kunt leunen op werkt wat anderen al gedaan hebben.
Maar er zit wel één groot nadeel aan Nix: het is niet voor iedereen weggelegd vanwege de extreme leercurve. Naast het OS en de package manager moet je ook nog verstand hebben van Nix de programeertaal. Deze functionele taal is de kracht achter Nix en NixOS en maakt het wat het is.
Daarom besloten we om klein te beginnen en te kijken of we Nix voor ons project konden inzetten.

De snelste winst met Nix Flakes
Het kleinste begin was om eerst onze ontwikkelomgevingen om te zetten naar Nix Flakes. Met Flakes kan je in een repository diverse dingen (zgn. outputs) definiëren die gebouwd kunnen worden. Dit zijn bijvoorbeeld packages, ontwikkel omgevingen, virtuele machines of complete NixOS systemen. Eenmaal gebouwd kunnen deze gecached worden in een centrale opslagplaats en daarmee snel beschikbaar zijn voor uitrol, of voor collega’s om hun omgeving op te zetten.
Het eerste wat we in onze Flakes vastlegden was onze ontwikkelomgeving. Daarin zitten alle afhankelijkheden die nodig zijn voor de te ontwikkelen applicatie maar ook alle gereedschappen die nodig zijn tijdens de ontwikkeling. Voorheen gebruikte we hiervoor een combinatie van Homebrew en Docker voor maar dit was niet consistent en weinig flexibel.
Het voordeel van Nix is dat alle afhankelijkheden gelocked zijn: ze zijn vastgelegd. Dit beperkt verschillen tussen individuele ontwikkelomgevingen en productie tot een minimum. Er zijn zo geen verassingen die pas tijdens het uitrollen van de applicatie naar voren komen. Door alle ontwikkelgereedschappen, zoals linters, de gewenste Python versie, browsers en scanners vast te leggen is het makkelijk om samen aan hetzelfde probleem te werken zonder verschillen in omgevingen. Zoals jij het lokaal hebt, zo is het exact op de server: dit scheelt obscure bugs en bijbehorend tijdverlies.
Naarmate we meer vertrouwen kregen in Nix en nixpkgs zijn we gaan kijken of NixOS wat voor ons kon betekenen.
Van thuislab naar productieservers
Zoals met veel technologieën proberen we deze vaak uit in de hobbysfeer, thuis, om er ervaring mee op te doen. Een paar Raspberry Pi’s met een Kubernetes cluster in de meterkast is ons niet vreemd. Toen NixOS voor thuisgebruik wel handig bleek zijn we het voor enkele van onze productiesystemen gaan uitproberen.
Als eerste voor onze nieuw in te richting systemen met monitoring en stichting gerelateerde software zoals de helpdesk, website en cloud. Daarna werden de Plus omgeving voor deelnemers van de stichting overgezet. Dit draaide eerder nog op een container gebaseerde oplossing met Flatcar en Terraform. NixOS bleek echter een uitstekende stabiele vervanger en geleidelijk zijn deze en andere systemen zoals de eerder genoemde demo- en acceptatie-omgeving omgezet naar NixOS en ook onze server voor toegankelijkheid metingen.
Het beheer van onze NixOS systemen ging in eerste instantie decentraal. Op de systemen werden direct de configuratie bestanden opgeslagen en bewerkt. Dit was natuurlijk al snel niet houdbaar waardoor deze bestanden verhuisde naar een centrale Git repository. Vanuit hier kunnen we de configuraties centraal bijwerken en uitrollen naar de verschillende systemen. Hierbij worden gedeelde configuraties tussen systemen herbruikt d.m.v. modules. In eerste instantie is voor het uitrollen een eigen script ontwikkeld om gebruik te maken van de terugrol functie van NixOS, die het mogelijk maakt om een nieuwe configuratie te testen en die bij problemen, zoals het jezelf buitensluiten van SSH, automatisch terug te rollen. Deze functionaliteit hebben we eigenlijk nooit nodig gehad in de tijd dat we NixOS gebruiken maar was in het begin wel wenselijk om vertrouwen te krijgen in het systeem.
Uiteindelijk hebben we onze eigen scripting weggedaan en maken we nu gebruik van Colmena. Deze tool maakt het beheer van meerdere NixOS systemen aanzienlijk makkelijker door functionaliteiten als parallelle builds, deploys en ssh multiplexing. Hiermee kunnen we eenvoudig in één stap, binnen een minuut alle systemen van een change of update voorzien. Pats, boem, das gewoon goeien handel.

Voor de installatie van NixOS op onze systemen maken we gebruik van nixos-infect. Dit is een simpel script wat op een bestaand Linux systeem (bv. Debian of Ubuntu) alle oude systeem bestanden aan de kant zet in een aparte map en NixOS daarvoor in de plaats installeert. Hierna kan met reboot het nieuwe NixOS systeem worden geactiveerd. Dit gebruiken we omdat veel VPS leveranciers standaard nog geen NixOS images leveren, maar wel andere Linux en het makkelijker werkt dan met een iso image de installatie te doen.
Ditzelfde proces kunnen we ook gebruiken op onze bestaande Linux systemen omdat het eigenlijk precies hetzelfde werkt, alleen moeten we hierna wel nog wel de oude gegevens (zoals databases) terugzetten van de oude systeem map. Het rebooten van het systeem na de installatie is altijd even een spannend moment, omdat je er dan pas achter komt of alles goed gegaan want alleen dan kan je inloggen. Van tevoren kan je een boel controleren maar zal je nooit echt zeker weten of het werkt, of dat je daarna in een moeizaam recovery proces beland. Gelukkig hebben we bij geen van de migreerde systeem een probleem gehad. Iets waar we bij de nieuw te installeren systemen wel tegenaan liepen en waar we een oplossing voor hebben geupstreamed.
De moeite waard
Al met al liep onze migratie naar NixOS redelijk probleemloos. Daarbij hielp het dat we het hele proces over ongeveer 2 jaar in kleine stappen hebben doorgevoerd en veel dingen in greenfield hebben kunnen proberen. En terwijl we klaar zaten om een heel weekend kwijt te zijn aan debuggen en probleem oplossen konden de vrijdag van de laatste migratie, Basisbeveiliging.nl, vroeg afsluiten. Wel ervaren we momenteel nog wat kleine problemen momenteel rondom het uitvoeren van scantaken. Voornamelijk gedrag wat zich pas na langere tijd bij grotere load laat zien en wat lastig te onderzoeken is.
Omdat we er voor gekozen hebben de systeem configuratie opnieuw op te bouwen en daarbij wat andere constructies te introduceren t.o.v. de vorige installaties, is er wat impliciet gedrag van de systemen waar we mee moeten leren omgaan. We verwachten dit binnen afzienbare tijd opgelost te hebben en we blijven onze logging en monitoring in de gaten houden om problemen vroeg voor te zijn.

Er is nog een bijkomende voordeel van configuratie management en de gehele definitie van je systemen in code te hebben staan wat de laatste maanden steeds meer naar voren komt. Want niet alleen wij mensen vinden het handig om alle informatie in een plek te hebben, ook machines vinden zo’n naslagwerk ideaal.
Door de infrastructuur repository mee te geven als context of informatiebron voor AI agents is voor deze agents makkelijker om oplossingsgericht te werken en met goede voorstellen te komen zonder dat ze gaan fantaseren over de opzet van onze systemen of allerlei wilde commando’s moeten gaan uitvoeren op onze systemen. Een simpele vraag zoals “waarom voert de server dellinspiron8600 geen scantaken uit” is dan genoeg om de AI agent direct tot de conclusie te laten komen dat er een firewall rule was vergeten, zonder ook maar één systeem te hoeven inspecteren. (Is het toch gelukt om AI hype in het artikel te stoppen :))




