Met de grootste eenvoud haal je container images van https://hub.docker.com of andere publieke sites. De kwaliteit is op verschillende fronten regelmatig twijfelachtig en je moet je eigenlijk afvragen of je de risico’s wil lopen. Je bewust worden van de gevaren is een goede stap op weg naar het veiligstellen van de zogenaamde “delivery pipeline”. Naast het gebruik van een afgeschermd account op de Docker hub, is er meer mogelijk.
Beveiliging van container images
De beveiliging van container images begint bij het image zelf. Om dat te begrijpen moet het duidelijk zijn wat zo’n image is en hoe de software er op draait. hoewel het onderwerp veel meer bevat dan in dit korte blog beschreven kan worden, hoop ik toch een goede introductie te kunnen geven.
Kort gezegd is een container image een mappenstructuur met daarin alles wat nodig is om het container proces te starten. Denk aan configfiles, libraries en soms zelfs een shell. Het belangrijkste hierbij is dat als het image minder software bevat, er minder software vatbaar is voor exploits. De images zijn stateless. in deze context betekent dit dat aanpassingen aan het image verdwijnen als het container proces opnieuw gestart wordt. Iets veranderen aan het image doe je niet. In plaats daarvan maak je een nieuwe versie. Zo’n image kan je lokaal zelf maken en als “shippable unit” uploaden naar een repository zodat alle kubernetes nodes er gebruik van kunnen maken.
Images
De manier waarop je image gebouwd is, beinvloedt de beveiliging. Zelfs als je in Kubernetes instelt dat een container alleen unprivileged mag draaien, kan het zijn dat het gebruikte image toch stiekem te veel rechten geeft. Standaard draait een container proces namelijk als root (binnen de container). Dat klinkt veilig. Het is immers root in de container en niet daar buiten? Zo simpel is het helaas niet.
Het verlagen van de risico’s begint met een goed container image. De vuistregel hierbij is dat een compacter image minder software bevat en dus minder snel uitgebuit kan worden. Vervolgens wil je dat het proces in de container niet als root draait maar als een normale gebruiker. In de Dockerfile waarin je het image definieert moet je hier dus rekening mee houden. voeg bijvoorbeeld het volgende aan je definities toe:
RUN groupadd -g 5000 mortaluser && \ useradd -r -u 5000 -g mortaluser mortaluser USER mortaluser
een normale gebruiker mag alleen processen starten op zogenaamde unprivileged poorten. Een webapplicatie kan je dus niet zomaar op poort 80 laten luisteren! Pas de poort waarop de applicatie moet luisteren dus aan naar een vrije poort boven de 1024. Uiteraard kan je met behulp van Kubernetes services en ingress controllers de poort van buitenaf wel gewoon 80 laten zijn. Uiteindelijk moet in de configfile van de software die op het image staat de username waaronder het proces gestart moet worden nog aangepast worden.
“root capabilities”
Mooi! Is er nu geen root user meer nodig in de container? Dat antwoord is helaas nog altijd nee. Een normale gebruiker in een conatainer heeft altijd nog bepaalde zogenaamde “root capabilities”. Dit zijn privileges die het onderscheid maken tussen een normale “mortal” gebruiker en de oppermachtige root gebruiker. Hoewel de rechten van de user binnen een container die dit image gebruikt met de bovenstaande acties al een stuk beperkter zijn, zijn er nog altijd bepaalde capabilities nodig. Zo is het op het moment van schrijven nog niet mogelijk om bijvoorbeeld netwerkgerelateerde dingen te configureren zonder die specifieke root capabilities te hebben. Open eens een shell op een container en kijk (als het commando beschikbaar is ) wat het verschil is in de output van het commando “capsh –print” bij de volgende scenarios:
- een privileged container met een image dat werkt met de root gebruiker
- een unprivileged container met een image dat werkt met de root gebruiker
- een unprivileged container met een image dat werkt met een normale gebruiker
Een leuk detail is dat gebruiker root in een zogenaamde unprivileged container veel kan maar toch niet alles. Probeer bijvoorbeeld maar eens de hostname in de container aan te passen…
Capabilities zijn niet in te stellen in het image. We zijn daarvoor afhankelijk van de Kubernetes gebruikers die het in hun pod definites verwerken zoals in het onderstaande voorbeeld:
securityContext: capabilities: add: ["NET_ADMIN", "SYS_TIME"]
Voor een overzicht van beschikbare capabilities kun je kijken op bijvoorbeeld: http://man7.org/linux/man-pages/man7/capabilities.7.html
Eigen image repository
Een afgescheiden account op de Docker hub heeft zo zijn voordelen. je kan het “as a service” gebruiken en hoeft er verder geen eigen infrastructuur voor op te zetten en te onderhouden. Wel vergroot je de beveiliging als je een eigen image repository gaat gebruiken. VMware heeft Harbor gemaakt en beschikbaar gesteld aan de gemeenschap via hun Github pagina: https://github.com/goharbor/harbor In Harbor kunnen verschillende projecten en gebruikers met specifieke rechten aangemaakt worden. Naast deze voordelen is het mogelijk om Harbor te integreren met externe componenten die de beveiliging ten goede komen:
Notary
Zoals de naam “Notary” al doet vermoeden is het een notaris. Deze notaris wordt ingezet om images digitaal te ondertekenen. Op de Kubernetes worker nodes kan je docker instellen dat alleen “signed” images van de private repository gebruikt mogen worden. Dit geeft je veel controle over welke images gebruikt mogen worden om containers te starten op het cluster.
Clair
Clair is een scanner die images in Harbor scant op basis van zogenaamde “Common Vulnerabilities and Exposures” ofwel CVE’s. Hiermee wordt het direct inzichtelijk welke bekende kwetsbaarheden er te vinden zijn in de images. Daarnaast biedt een classificatie van de kwetsbaarheden je een snel inzicht in de noodzaak van het updaten.
Enkele screenshots
Tot slot
Tja, ook bij het gebruik van images voor Kubernetes is beveiliging niet zo maar even geregeld. Ook is het duidelijk dat er goede afspraken moeten worden over verantwoordelijkheden. De gene die het Kubernetesplatform beheert is lang niet altijd de zelfde persoon of instantie die de docker images bouwt of ze in een deploy gebruikt. Zoals met meer onderwerpen is het ook hier in alle opzichten is het belangrijk dat alle neuzen dezelfde kant op staan. Daarnaast zal per casus de vraag centraal moeten staan wat de juiste afweging is qua beveiliging. Te weinig is niet goed en te veel kan een remmende werking hebben.
Auteur: Geurt Hakfoort – Cloud Specialist Denit