DNS- en time-software wordt veel veiliger met Rust

Essentiële software voor kritieke internetinfrastructuur opnieuw geïmplementeerd

Blije programmeur zittend op z'n werkplek, gezien vanachter z'n rug.

Op dit moment wordt essentiële software voor kritieke internetinfrastructuur opnieuw geschreven in de programmeertaal Rust. Belangrijkste eigenschap van deze relatief jonge taal is dat deze ontwikkelaars ingebakken memory safety biedt zonder verlies aan performance (via zero-cost abstractions).

Een regelmatig terugkerende statistiek is dat grofweg 70 procent van alle ernstige kwetsbaarheden in systeemsoftware wordt veroorzaakt door bugs gerelateerd aan een gebrek aan memory safety. Dat zou betekenen dat misschien wel meer dan de helft van alle securityproblemen in systeem- en netwerksoftware voorkomen kan worden door van de programmeertalen C en C++ over te schakelen naar codering in Rust. Inmiddels wordt onder andere gewerkt aan (her)implementaties van software voor DNS, tijdsynchronisatie en TLS. Met name die eerste 2 zijn van direct belang voor onze dienstverlening.

Rust bij NLnet Labs

De programmeertaal Rust is sterk in opkomst in de wereld van systeem- en netwerkprogrammering. Inmiddels is Rust bij NLnet Labs, een van de belangrijkste bouwers ter wereld van software voor de internetkerninfrastructuur, zelfs de standaard voor nieuwe implementaties geworden. Zo is al hun RPKI-software (zie NLnet Labs Routing tools)– een relatief nieuwe pilaar naast hun DNS-software – in Rust geschreven.

De Domain crate (een Rust-library voor DNS) [@GitHub NLnet Labs] fungeerde daarbij in eerste instantie als proof-of-concept voor de programmering in Rust. Maar inmiddels vormt deze library de basis onder een Rust-gebaseerde DNS-suite die straks ook een validerende resolver, een autoritatieve nameserver, een DNS-proxy en een uitgebreide diagnosetool bevat. De functionaliteit van de bestaande ldns-library en -tools (geschreven in C en inmiddels alleen nog onderhouden) zal in de Domain crate worden opgenomen.

De ontwikkeling van de Domain-suite wordt voor bijna 1 miljoen euro gesponsord door het Sovereign Tech Fund, een fonds van de Duitse federale overheid voor de ondersteuning en ontwikkeling van open-source internetsoftware.

De Rust programmeertaal: memory safe

Rust is een relatief jonge programmeertaal [Rust online boek, @YouTube Rust-video's, Blog beginnen met Rust, Github Comprehensive Rust]. De taal is ontwikkeld bij Mozilla en in eerste instantie gebruikt om een nieuwe browser engine te schrijven (Mastodon post over Servo). Inmiddels is het beheer van de taal als open source ondergebracht in de Rust Foundation, in 2021 opgericht door Amazon, Huawei, Google, Microsoft en Mozilla.

Rust lijkt heel erg op C en C++(uitleg wikipedia), nog steeds de klassieke programmeertalen voor systeem- en netwerksoftware. Belangrijkste meerwaarde van Rust is dat deze taal memory safe is. Dat wil zeggen dat de toegang tot datastructuren gecontroleerd wordt, zodat de programmeur niet zomaar willekeurige geheugenadressen kan benaderen. Dit in tegenstelling tot C/C++, waar de programmeur via pointers en indexen in beginsel vrije toegang heeft tot het hele geheugen. Sterker nog: de uitwisselbaarheid van indexen en pointers (blog digitalmars) is een standaard feature in C lees je op Wikipedia.

Voor lezers bekend met C/C++ hebben we onderaan dit artikel de belangrijkste kenmerken van memory-safe Rust en het onderscheid met C op een rijtje gezet.

Een terugkomende statistiek is dat grofweg 70 procent (bron Wikipedia) van alle ernstige kwetsbaarheden in systeemsoftware wordt veroorzaakt door bugs gerelateerd aan een gebrek aan memory safety [informatie memory safety op Microsoft, Android, Apple, C++]. Het bekendste geval is de Heartbleedbug (uitleg Wikipedia Heartbleedbug), waarbij een buffer over-read (uitleg Wikipedia buffer over-read) in de veelgebruikte OpenSSL library 10 jaar geleden veel van de TLS-verbindingen onveilig maakte.

Om een indruk te geven: in 2023 werden bijna 29 duizend CVE's (uitleg Wikipedia Common Vulnerabilities and Exposures) geregistreerd, waarvan ruim de helft met de kwalificatie ernstig (een CVSS-score van 7.0 of hoger).[cve-dat-review op jerrygamblin.com]

Aanbeveling FBI, NSA en cybersecurity-centers

Het grote aandeel van kwetsbaarheden in software veroorzaakt door een gebrek aan memory safety was voor de FBI, de NSA [1 media.defence.gov - CSI software memory safety pdf] en een hele zwik cybersecurity-centers aanleiding om op te roepen tot het gebruik van memory-safe programmeertalen (The case of memory safe roadmaps op cisa.gov). Deze aanbeveling is onderdeel van CISA's 'Secure by Design'-project [1 Cisa.gov secure by design -publication, 2 Cisa.gov secure by design-best practices], een internationale campagne om de veiligheid van software te verbeteren, waarin ook het Nederlandse NCSC participeert. Volgens de auteurs kan de toepassing van memory-safe programmeertalen een hele klasse van kwetsbaarheden elimineren.

Naast de onafhankelijke talen Rust en Python – die laatste is ontwikkeld bij het CWI, ooit de moederorganisatie van SIDN – noemt de aanbeveling ook meer leverancier-specifieke talen als C#, Go, Java en Swift (uitleg via Wikipedia). De NSA voegde daar onlangs Ruby, Ada en Delphi (nog aan toe [1 readwrite.com blog Deanna Ritchie]. Van al deze talen is Rust echter de enige die C voor generieke systeem- en netwerkprogrammering redelijkerwijze kan vervangen.

Roadmap

Softwareontwikkelaars en -bedrijven wordt aangeraden een roadmap richting memory-safe programmeertalen te ontwikkelen (en die ook naar klanten en gebruikers te communiceren). Daarbij geven de auteurs van de aanbeveling ook aanwijzingen over wat een goede aanpak zou zijn. Tegelijkertijd erkennen zij dat deze transitie vanwege de enorme installed base jaren gaat duren en significante investeringen vraagt.

Belangrijkste doel is om de veiligheid van programmacode te verbeteren en tegelijkertijd de kosten te verlagen. Denk daarbij aan de kosten voor training, tools, de toepassing van best practices (voor codering, segmentering en architectuur), het gebruik van veiligheidsopties in hardware, operating systems en compilers, (geautomatiseerde) tests, (geautomatiseerde) code-analyses, audits, security updates, patch management en incident responses. De auteurs verwachten dat een deel van de benodigde investeringen op de lange termijn terugverdiend [1 arxiv.org 'Increasing, not Diminishing..'] kan worden door de productie van veiliger software met een hogere betrouwbaarheid en kwaliteit.

Witte Huis: oproep aan leveranciers

In aanvulling (whitehouse.gov Future Software should be Memory Safe) op de CISA-aanbeveling legt de Office of the National Cyber Director (ONCD, het cybersecurity-bureau van de Amerikaanse president, verantwoordelijk voor de nationale cybersecurity-strategie) de verantwoordelijkheid voor de overstap naar memory-safe technieken primair bij de leveranciers. In het rapport 'Back to the Building Blocks: A Path Toward Secure and Measurable Software' op whitehouse.gov beschrijft de ONCD een tweeledige strategie om de hoeveelheid kwetsbaarheden terug te dringen:

  • het reduceren van het (online) aanvalsoppervlak door een hele klasse van kwetsbaarheden te elimineren; sleutel is de toepassing van memory-safe programmeertalen waar mogelijk

  • de ontwikkeling van betere diagnose-tools om de veiligheid van systemen te meten; dit gaat om de ontwikkeling van security-gerelateerde metrieken en tools als onderdeel van de software-kwaliteit

Voor het eerste punt worden leveranciers van software en hardware (whitehouse.gov statement of support for software) opgeroepen over te stappen naar memory-safe talen. Het tweede punt is een klus voor onderzoekers die zich bezighouden met software-(kwaliteits-)metrieken, software-architectuur en security.

De aanbeveling van ONCD was overigens aanleiding tot een reactie van Bjarne Stroustrup, de bedenker van C++. Volgens hem bevat modern C++ voldoende veiligheidsgaranties en is er (inderdaad) behoefte aan betere ontwikkeltools en -processen. [1 infoworls.com] De technische gemeenschap lijkt echter redelijk eensgezind in haar mening dat Stroustrop inmiddels een achterhoedegevecht voert voor het behoud van zijn geesteskind. [reactie 1 , 2 op news.ycombinator: C++ creator rebuts White House warning ]

Meest geliefde taal

De inherent robuustere code die Rust oplevert, is een belangrijke drijver achter de groei van de taal. Dat zegt Erik Jonkers, evenementenorganisator bij de Stichting Rust Nederland (RustNL) en directeur Open Source bij het bedrijf Tweede Golf. Rust staat inmiddels op plek 14 van de meest gebruikte talen in de jaarlijkse Stack Overflow Developer Survey. Veel indrukwekkender is dat Rust in deze meting al bijna een decennium lang de meest populaire taal is. In de TIOBE Index staat Rust nu op de 18e plek.

Figuur 1: Rust is al bijna een decennium de meest populaire taal: 85 procent van de huidige programmeurs wil deze taal blijven gebruiken. [bron: Stack Overflow Developer Survey 2023] Bekijk deze grafiek in tabelvorm

Figuur 2: Rust staat "slechts" op plek 14 van de meest gebruikte talen. [bron: Stack Overflow Developer Survey 2023] Bekijk de grafiek in tabelvorm

Robuuste code

Dat grote verschil tussen populariteit en daadwerkelijk gebruik komt volgens Jonkers doordat Rust bij uitstek geschikt is voor systeem- en netwerk-programmering. Maar dat is natuurlijk maar een beperkte, gespecialiseerde markt ten opzichte van bijvoorbeeld web-ontwikkeling, wat tegenwoordig veelal in PHP, JavaScript en allerlei daarop gebaseerde frameworks plaatsvindt.

"Bij Tweede Golf zijn we een jaar of 7 geleden met Rust begonnen, vooral omdat het een goede taal is waar al onze programmeurs enthousiast over waren. Belangrijkste reden voor dat enthousiasme is wat 'empowerment' genoemd wordt: de kwaliteit van de code is veel hoger, en als het compileert, dan kun je ervan uitgaan dat de software ook draait en blijft draaien. Dat is gewoon een eigenschap ingebakken in de taal zelf. Daardoor hebben programmeurs veel meer vertrouwen in de software die ze afleveren. Vergelijk dat met een nieuwe applicatie in C/C++, waaraan je meestal nog weken moet testen en schaven voordat die goed loopt en stabiel is. Zo vertaalt de robuustheid van Rust zich met name in de late fase van ontwikkeling (het traject van eerste versie tot productierijp) in een hogere productiviteit en een kortere time-to-market. Ook het onderhoud en de doorontwikkeling daarna vragen minder inspanning."

Blijvertje

Belangrijke voortrekkers van Rust zijn onder andere de 'Big Tech'-bedrijven Google [ 1 Tackling cybersecurity vulernarbilities through secure by design], Amazon (AWS) en Microsoft [1 github rust for dotnet devs]. Zo maakte Google afgelopen maand bekend 1 miljoen dollar in de Rust Foundation te investeren om de interoperabiliteit tussen Rust en C++ te verbeteren [1 Google blog 'Improving internetoperability'] (zoals hierboven beschreven is de interoperabiliteit met C al goed voor elkaar). Amazon introduceerde een paar maanden geleden een Rust-interface [API en SDK op AWS.Amazon.com] voor zijn AWS-diensten. En Microsoft vervangt onderdelen van Windows door Rust-code [1 X-post van Mark Russinovich].

Rust wordt inmiddels ook opgenomen in de broncode van Linux [Rust for Linux, Nova op phoronix.com-Red Hat Nova Rust Abstractions] en FreeBSD [ 1 phoronix.com- FreeBSD Considers Rust Base, 2 wiki.freebsd.org/Rust]. Volgens Jonkers is Rust dan ook een blijvertje. "Het gebruik van de taal is inmiddels over het kantelpunt heen, wat betekent dat Rust niet meer het gevaar loopt te verdwijnen. Ook in de industrie wordt Rust steeds vaker ingezet [1 rust-lang.org]."

Rust voor kritieke software

RustNL promoot het gebruik van Rust en organiseert regelmatig meetups. Vorig jaar werd samen met NLnet Labs en Tweede Golf de eerste RustNL-conferentie georganiseerd (na een eerdere gezamenlijke meetup over de toepassing van Rust in kritieke infrastructuren via Tweedegolf.nl). Dit jaar vindt deze conferentie plaats op 7 en 8 mei in Delft [kijk de video's terug van RustNL].

Tweede Golf is gespecialiseerd in Rust-programmering en ontwikkelde onder andere teach-rs via Github, een universitaire Rust-cursus die al op meerdere plekken gebruikt is, waaronder de Slovak University of Technology (STU). De TU Delft heeft eigen materiaal ontwikkeld voor zijn Embedded Systems Lab [slides Jonathan Dönszelmann via Github.ewi.tudelft.nl en de video via YouTube 'Teaching embedded Rust']. Daarnaast deed Tweede Golf een herimplementatie van sudo (uitleg wikipedia) (uitleg via Tweedegolf.nl sudo-rs) (uitleg via Github trifectatechfoundation, en implementaties van het Network Time Protocol (NTP uitleg via wikipedia, blog ntpd-rs op tweedegolf.nl) [@GIThub pendulum-project/ntpd-rs] en het Precision Time Protocol (PTP uitleg via wikipedia; blog Statime op tweedegolf.nl [@GitHub pendulum-project/statime]). Deze ontwikkelingen worden onder andere gesponsord door Prossimo [memorysafety.org over New home NTP, over Sudo-su, over NTP], NLnet Foundation [NLnet Foundation project PTP-Rust], het Sovereign Tech Fund [Sovereign tech fund pendulum] en SIDN fonds [SIDN fonds Building a network clock running memory-safe PTP and NTP].

Het Sovereign Tech Fund financieert via Prossimo ook de ontwikkeling van Rustls [@GitHub rustls/rustls] (als "vervanger" van OpenSSL) en Hickory DNS [@GitHub hickory-dns] (voorheen Trust-DNS, als "vervanger" van BIND uitleg via Wikipedia) [memorysafety.org/initiative/dns/].

Publieke NTP-dienst

De ntpd-rs- en Statime-software, onderdeel van Project Pendulum [@GitHub], is voor ons van bijzonder belang omdat SIDN Labs al sinds 2019 een publieke NTP/NTS-dienst [ 1 SIDN.nl Zwakheden in NTP-tijdsprotocol maken andere (beveiligings-)protocollen kwetsbaar, 2 sidn.nl NTS-beveiliging voor NTP-tijdsprotocol gestandaardiseerd als RFC 8915] draait [1 sidnlabs.nl Goede dingen hebben tijd nodig, 2 sidnlabs.nl TimeNL wordt volwassen, 3 sidnlabs.nl TimeNL: de duidelijke NTP-dienst van SIDN Labs]. Het plan is om de huidige software te zijner tijd door ntpd-rs te vervangen (experimenteel op het adres ntpd-rs.sidnlabs.nl). Daarnaast is een experimentele PTP-dienst voor bepaalde doelgroepen, gebaseerd op Statime, in ontwikkeling [1 sidnlabs Een open infrastructuur voor internettijd van minder dan een milliseconde]. Deze 2 experimenten moeten uiteindelijk resulteren in een heel nieuw time-systeem, dat niet alleen voor onze eigen TimeNL-dienst ingezet kan worden, maar ook door andere, vergelijkbare diensten kan worden gebruikt [1 sidnfonds.nl Building a network clock running memory-safe PTP and NTP].

Het backbonenetwerk dat al onze kloksystemen met elkaar verbindt is nu al gebaseerd op het PTP-protocol. Daarmee worden de (Linux-gebaseerde) front-end klokken zeer nauwkeurig met de kernklokken gesynchroniseerd. "Een nieuwe open-source implementatie is goed voor de diversiteit van het PTP-software-landschap," zegt research engineer Marco Davids, "Voor Linux zijn er nu alleen LinuxPTP en PTPd, waarvan die laatste ook nog eens matig onderhouden wordt. Ook NTP kan wel een extra implementatie gebruiken – daar heb je NTP, NTPsec en chrony – zij het dat de noodzaak daar minder dringend is."

Memory-safe Rust voor C/C++-programmeurs

Eigendom van referenties

Rust pakt de 'memory safety'-problematiek aan door alle referentie (uitleg Wikipedia) aan 1 enkele variabele (eigenaar (uitleg op doc.rust-lang.org) ) en een lifetime (scop (uitleg via Wikipedia)) te koppelen (move) en alle kopieën daarvan expliciet te maken (clone; met behulp van reference counting (uitleg Wikipedia)).

Pointers (e.g. call by reference (uitleg Wikipedia)) zijn vervangen door referenties, die wel geleend (uitleg op doc.rust-lang.org) kunnen worden, maar niet meer als geheugenadres kunnen worden gebruikt.

Arrays, Slices en Vectors

Arrays (uitleg Wikipedia) hebben nu een vaste lengte (uitleg doc.rust-lang.org) en worden dus niet meer in de heap (uitleg Wikipedia) maar op de stack (uitleg Wikipedia) opgeslagen. Indexeren kan nog steeds, maar indexen verliezen hun geldigheid als het betreffende Array niet meer bestaat.

Pointers zijn hier vervangen door Slices (deel-arrays). Zowel de array-indexen als de grenzen van de Slices zijn voorzien van extra checks om te zorgen dat geen elementen buiten de grenzen worden geadresseerd (bounds checking).

De arrays in C/C++ met een variabele lengte heten nu (verwarrend genoeg) Vectors. Ze zijn geïmplementeerd als dynamische arrays (growable), waarvan de grenzen bewaakt worden door toevoegingen expliciet te maken (append).

Unions, enums, structs en tuples

Naast de (untagged) unions die we kennen van C/C++ biedt Rust ook tagged unions, die nu enums heten. Daarbij is steeds bekend welk datatype zich in de union bevindt.

Voor een enumeratie uit C/C++ (een verzameling gerelateerde constanten) gebruik je nu een enkelvoudige union (met één enkele, impliciete tag).

Voor de structs is er niet zo veel veranderd ten opzichte van C/C++. Wel zijn in Rust de tuples erbij gekomen: structs met anonieme velden, die met name handig zijn voor de snelle decompositie van return-waarden.

Smart Pointers

Adres-pointers zijn nog wel beschikbaar, maar heten nu Smart Pointers. Daar zit een kleine wrapper en wat meta-data omheen, die ongecontroleerde toegang tot het geheugen voorkomt.

De Smart Pointers vormen de basis onder alle datastructuren die geen vastgestelde lengte hebben en op de heap terecht komen: Boxes (geheugenblokken, vgl. malloc), Vectors, Strings/Slices (beide gebaseerd op Vectors) en andere vormen van Collections (e.g. Hash Maps).

Ingewikkelder datastructuren als (gelinkte) lijsten en (binaire) bomen worden (niet heel veel anders dan in C/C++) gebouwd op recursieve datastructuren.

Op vergelijkbare wijze als voor de datastructuren biedt Rust ook memory safety voor threads en bijbehorende synchronisatie- (Mutex) en communicatiemechanismen (message passing).

Cognitieve administratie

Wat Rust feitelijk doet is de last van memory safety bij de programmeur leggen: enerzijds in de vorm van beperkingen, anderzijds in de vorm van "cognitieve administratie". De programmeur moet zich er immers steeds van bewust zijn wie de eigenaar is van referenties naar datastructuren, wie mede-eigenaar moet worden (dat willen zeggen: een eigen referentie krijgt), en wie een alleen een referentie hoeft te lenen.

Tegelijkertijd moesten C-programmeurs al weten of een datastructuur op de heap (call by reference) of op de stack (call by value) terecht zou komen, of een waarde gewijzigd mag worden (const), en wanneer data op de heap opgeruimd moest worden (free). Conceptueel is dus alleen het eigendom van datastructuren nieuw.

Onveilige Rust

Wie dat wil, kan overigens nog steeds onveilige Rust (unsafe Rust) gebruiken. Belangrijkste is dat je nog steeds dereferenties kunt doen op rauwe pointers en dat je onveilige functies kunt aanroepen. Wel moet je deze code-blokken en functies expliciet declareren als 'unsafe'.

De meeste van deze mogelijkheden zijn bedoeld om koppelingen te maken met ("legacy") C-code. Voor de details is er een apart boek met de onheilspellende titel 'The Rustonomicon' beschikbaar.