Een micro-service architectuur is tegenwoordig een hippe en moderne architectuurstijl. Het levert vaak geen eenvoudig applicatielandschap op. Hoe los je bijvoorbeeld security op? Features zoals single sign-on, transparante audit logs worden in zo’n architectuur extra belangrijk. Immers is je functionaliteit verspreid over het applicatielandschap. In dit artikel bespreken wij onze aanpak alsmede de gangbare alternatieven.
De implementatie van security bestaat uit een authenticatie en een autorisatie proces. Het authenticatieproces bepaalt of je toegang hebt. Het autorisatieproces bepaalt of je de rechten hebt. Het is heel belangrijk dat je dit als twee verschillende, complementerende processen ziet.
Kerberos
Wij hebben gekozen voor kerberos voor onze authenticatie omdat deze ten tijde van het keuze moment het beste paste binnen onze architectuur. Daarnaast heeft kerberos zich zeker bewezen als een gedegen beveiligingsprotocol, wat een belangrijke voorwaarde is voor ons project.
Een alternatief voor het beveiligen van micro services is OpenID Connect. Dit is een authenticatie laag bovenop het OAuth 2.0 protocol, welke vaak gebruikt wordt voor autorisatie.
Kerberos is een sterk en transparant mechanisme op basis van ‘secret key’ cryptografie. Het is één van de eerste authenticatie protocollen dat gebaseerd is op symmetrische sleutel cryptografie. OpenID Connect is één van de laatste protocollen en maakt gebruik van JSON Web Tokens (JWT). Het token moet beveiligd zijn met een HMAC SHA-256 signature en kan versleuteld zijn.
Client perspectief
Het voert wat ver om de precieze werking van kerberos toe te lichten in dit artikel, maar laten we het principe toelichten aan de hand van een voorbeeld. Stel, je wilt een antwoord van de service https://example.com/serviceA/hello.
Je typt deze url in de browser of voert een GET uit via curl. De service geeft een 401 error code terug. Je bent als gebruiker onbekend en hebt geen toegang. Gelukkig geeft de server een hint mee in de header: www-authenticate: negotiate. Dit betekent dat de server graag authentication parameters wil hebben via het SPNEGO mechanisme. De afkorting SPNEGO staat voor Simple and Protected GSS-API Negotiation.
Je moet de client, in dit voorbeeld de browser of curl, vertellen dat je dit SPNEGO mechanisme gebruikt. Met de juiste configuratie haalt de client dan via GSS-API het gewenste token op en stuurt dan het request nog een keer, maar dan met de header www-authenticate: [token]. De server stuurt de status 200 en de verwachte body terug. Succes!
Maar, wacht even. Hoe werkt die magie met dat GSS-API? En wat is GSS-API uberhaupt? De GSS-API staat voor Generic Security Service – API. Het is een gestandaardiseerde API die je gebruikt om te communiceren met de gekozen security methodiek. In ons geval is dit kerberos.
Principe
Om een SPNEGO token te verkrijgen via kerberos moeten er een aantal stappen van het protocol doorlopen zijn. De eerste stap is jezelf bekend maken bij de Authentication Service (AS). De AS is onderdeel van de Key Distribution Center (KDC). Dit doe je door in te loggen of via terminal het commando kinit uit te voeren. Deze actie stuurt een plain-text request uit met onder andere je ID naar de AS.
De AS bekijkt of je ID bestaat in de database. Je bestaat. Mooi. De AS maakt een Ticket-Granting-Ticket (TGT) aan en genereert nu een random key voor de Ticket Granting Service (TGS), welke ook onderdeel is van de KDC. Deze random key is de TGS session key. Deze session key wordt bij het TGT gevoegd. Je krijgt nu twee versleutelde berichten terug van de KDC: de TGT welke versleuteld is met TGS secret key en de TGS session key welke versleuteld is met de Client Secret Key.
De TGS session key is nu de gedeelde sleutel. Deze zit namelijk in de TGT waarvan de TGS de sleutel heeft en hij wordt versleuteld teruggestuurd naar de client. De client kan via zijn password de TGS weer openen en gebruiken.
Gedeelde sleutels
De volgende stap is dat je toegang wilt tot de service https://example.com. Hiervoor stuur je een verzoek aan de TGS. Dit verzoek bestaat uit twee berichten: een plain-text bericht met onder andere de ID van de service – HTTP/example.com in dit voorbeeld- en een versleuteld bericht met onder andere je eigen ID en de TGT. Het versleutelde bericht is versleuteld met de TGS session key. Dit is immers de gedeelde sleutel.
De TGS haalt uit de TGT de gedeelde TGS session key en opent daarmee het verzoek tot toegang voor de service HTTP/example.com. Er worden een aantal checks uitgevoerd en als alles OK is, dan wordt er een nieuwe random key gegenereerd. Dit is de HTTP Service Session Key. Deze key wordt in een HTTP Service Ticket gestopt en dit ticket wordt versleuteld met HTTP Service Secret Key. Dan stuurt de TGS het versleutelde HTTP Service Ticket terug en een bericht met de HTTP Service Session Key welke versleuteld is met de TGS Session Key.
Nu al deze stappen doorlopen zijn, heb je de benodigde informatie om het eerder genoemde SPNEGO mechanisme te doorlopen. Het principe is gelijk aan de bovenstaande stappen. Je stuurt echter deze keer een verzoek naar de HTTP Service. Dit verzoek bestaat uit het versleutelde HTTP Service Ticket welke kwam van de TGS, en een bericht met je ID die je hebt versleuteld met de HTTP Service Session Key.
De HTTP Service opent het Service Ticket voor de Service Session Key en opent hiermee het bericht met je ID. De HTTP service voert wat checks uit en stuurt dan een soortgelijk bericht terug naar de client. Deze kan hiermee de identiteit van de service verifieren. Alles is OK en je hebt eindelijk toegang. Yeah!
Implementatie opties
Als je dit voor de eerste keer leest, dan is dit best een ingewikkeld proces. Gelukkig hoef je dit niet allemaal zelf te programmeren. Wij gebruiken Spring Boot voor onze micro service implementatie. Spring Security ondersteunt kerberos redelijk goed en door de tutorial te volgen heb je de zaak snel aan de praat.
Echter wordt de zaak iets complexer als je micro service eigenlijk een facade is en er binnen de afhandeling van een request andere REST calls gedaan worden. Dit zijn zogenaamde fan-out calls. Er zijn dan een aantal opties om dit op te lossen.
Het domein is erg belangrijk in het kerberos security mechanisme. Als je toegang hebt tot example.com, dan wil dat niet zeggen dat je toegang hebt tot serviceA.example.com. Dat kan je gebruiken door een gateway te introduceren onder example.com die de SPNEGO afhandleing verwerkt en dan bericht doorstuurt naar service A, B, C. Je URLs zien er dan zo uit: https://example.com/[service].
Dat klinkt als een plan, maar deze optie heeft een groot nadeel. Je bouwt op deze manier een extra abractielaag in die er voor zorgt dat het authenticatieproces niet meer transparant verloopt. Hierdoor wordt debuggen en een audit trail lastiger.
Wij hebben er daarom voor gekozen de communicatie direct van client naar service te laten verlopen. Het gevolg is dat elke microservice bekend moet zijn bij de TGS en dat de service naam onderdeel wordt van het domain. Dit is behoorlijk wat extra beheer op de KDC en de DNS dat we dan ook snel hebben geautomatiseerd.
Dit betekent ook in het geval van een fan-out call dat de facade deze call uitvoert in naam van de originele requestor. Anders werkt de audit trial niet goed. Dit kan in kerberos via delegation. Het ticket is dan forwardable. Echter ondersteunt spring security geen kerberos delegation.
Wij waren dan ook genoodzaakt een patch te schrijven op spring security om dit mogelijk te maken, wat dan weer betekende dat we precies moesten snappen hoe de GSS-API implementatie van Java en Spring werkt. Uiteindelijk was dit de investering waard. Het authenticatieproces verloopt nu transparant en zonder tussenlagen.
Autorisatie
Het autorisatieproces verloopt via Attribute Based Access Control (ABAC). Dit is een variatie op Role Based Access Control, waar naast de rollen ook dynamische informatie gebruikt wordt voor de autorisatie.
Wij slaan deze informatie op in LDAP. Spring security biedt prima ondersteuning voor LDAP. De EL expressies van Spring stellen je gemakkelijk in staat om een ABAC systeem te implementeren.
Een alternatief is OAuth 2.0 wat werkt met access tokens. De combinatie Kerberos en OAuth 2 is volgens onze informatie ongebruikelijk, maar wel mogelijk. De Active Directory (AD) van Microsoft biedt wel zo’n soort optie omdat het data veld in een kerberos ticket van de AD gebruikt wordt voor het opslaan van een access token.
Terugblik
Nu wij zo terug kijken op ons implementatie traject kunnen we stellen dat het een leerzame periode was. Een gedegen security implementatie is geen sinecure. Veel concepten volgen dezelfde principes, maar verschillen in implementatie en impact op je code base. Voor ons werkt kerberos goed, maar daarmee is niet gezegd dat het overal de beste keuze is. Dat geldt natuurlijk ook voor de andere oplossingen.