In deze blogserie laat ik je zien hoe je je eigen Sentinel Codeless Connector Platform (CCP) data-connector kunt maken. Als dit je startpunt is, raad ik je aan eerst de vorige delen te lezen, want deze blog is best pittig als standalone.
Een Codeless Connector Platform (CCP) data-connector bouwen: deel 1
Een Codeless Connector Platform (CCP) data-connector bouwen: deel 2
Het Codeless Connector Platform is best een complex product, dus we hebben meerdere blogs nodig om alles goed uit te leggen. Maar ik beloof je: deze blog bevat waar je echt op zat te wachten. We gaan namelijk de daadwerkelijke API-aanroep “programmeren” die Sentinel zal uitvoeren. Laten we beginnen!

Even opfrissen
De vorige keer zijn we geëindigd met het bouwen van de GUI voor de connector. Deze GUI stelde de gebruiker in staat om twee waarden in te voeren (een base URL en een API key) die we nodig hebben om de API-calls richting 1Password uit te voeren. Die GUI hebben we gebouwd met behulp van het dataConnectorDefinition-bestand. Vandaag bouwen we het dataConnectorPoller-bestand.
Het dataConnectorPoller-bestand: API’s aanroepen
Het dataConnectorPoller-bestand bestaat uit een of meerdere “pollingConfig”-objecten. Zo’n object is in feite een eenvoudige beschrijving van een API. Door de API te beschrijven met verschillende properties, weet Sentinel wat voor soort call er gedaan moet worden, hoe er geauthenticeerd moet worden, hoe het antwoord geïnterpreteerd moet worden en hoe er omgegaan moet worden met paginering.
Een pollingConfig bestaat uit vier onderdelen: Authentication, Request, Paging en Response. Alleen Authentication en Request zijn verplicht, maar voor de 1Password-connector gebruiken we ze alle vier.

Authentication
We beginnen met de authenticatie. Gelukkig is die bij 1Password vrij simpel: we hebben een Authorization-header nodig met de waarde “Bearer <token>“. De onderstaande config regelt dat:
Door het maken van een auth-blok zorg je ervoor dat de CCP probeert te authentiseren met onze API. Er zijn ook andere opties beschikbaar, zoals basic (gebruikersnaam en wachtwoord) of OAuth. Deze stel je in via de property type. Elke type vereist andere eigenschappen, dus als je hiermee wilt experimenteren, check dan zeker even de Microsoft-documentatie. In ons geval gebruiken we “APIKey”, waardoor CCP de sleutel als header meestuurt. De overige eigenschappen bepalen hoe die header eruitziet.
- Weet je nog dat we in de GUI een tekstveld hebben gemaakt met de naam “ApiKey”? Hier komt de magie: door onze ApiKey in te stellen als “{{ApiKey}}” geven we het CCP-engine aan dat deze placeholder (herkenbaar aan de accolades) moet worden ingevuld op het moment dat de gebruiker op “connect” klikt, met de API key die hij of zij dan invoert.
- De naam van de header kun je instellen via de property “ApiKeyName”. Door die op “Authorization” te zetten krijgen we dus een header als “Authorization”: “”.
- Tot slot kunnen we met “ApiKeyIdentifier” een prefix toevoegen aan het token. Door deze op “Bearer” te zetten (de spatie wordt automatisch toegevoegd) krijgt de header de vorm “Authorization: Bearer <token>“.
Request
In dit gedeelte definiëren we de eigenschappen van de API-aanroep en onderstaande codeblock laat precies zien aan de CCP wat er nodig is.
Belangrijke punten:
- apiEndpoint gebruikt de baseUrl van de gebruiker, net als de API key.
- httpMethod is in dit geval POST.
- queryWindowInMin bepaalt de tijdspanne per API-aanroep (hier: 5 minuten).
- queryTimeFormat moet overeenkomen met wat de API verwacht.
- rateLimitQps geeft aan hoeveel queries per seconde toegastaan zijn.
- retryCount bepaalt het aantal keer dat Sentinel een verzoek zal proberen.
- timeoutInSeconds geeft aan na hoeveel tijd een verzoek verloopt.
- headers bevat de content-type, in dit geval “Content-Type: application/json”.
- queryParametersTemplate specificeert de payload, inclusief start- en eindtijd (in dit geval {_QueryWindowStartTime} and {_QueryWindowEndTime}.
Samenvattend: bovenstaande codeblock zorgt ervoor dat er elke 5 minuten een POST-verzoek wordt gedaan naar <baseurl>/api/v1/signinattempts om de aanmeldingsgebeurtenissen van 1Password op te halen. De CCP voert dit verzoek uit met een limiet van 1 verzoek per seconde. Als er een fout optreedt, probeert de API het nog 3 keer opnieuw. Na 60 seconden zonder antwoord verloopt het verzoek. Via de waarde van queryParametersTemplate is ingesteld dat per verzoek maximaal 1000 records worden opgehaald.
“Maar hoe halen we de resterende records op als er meer dan 1000 zijn?” Fijn dat je het vraagt!
Paginering
Paginering is eigenlijk heel logisch. Stel je voor dat je een boek vol kennis wilt afdrukken: dat is een enorme hoeveelheid informatie! Kun je redelijkerwijs verwachten dat iemand al die kennis in één keer verwerkt? Natuurlijk niet. Dus verdeel je het in behapbare stukken: een pagina. Je hebt dan nog steeds dezelfde hoeveelheid kennis (het volledige boek), maar gebruikers kunnen die informatie op hun eigen tempo verwerken, pagina voor pagina.
Paginering, oftewel pagination, werkt precies hetzelfde: in plaats van te werken met gigantische hoeveelheden data in één keer, knippen we die op in kleinere, beter hanteerbare stukken. De meeste moderne API’s ondersteunen paginering, dus je zult dit vaak tegenkomen wanneer je systemen en applicaties koppelt.
Sentinel’s CCP ondersteunt verschillende vormen van paginering maar voor 1Password gebruiken we de variant genaamd NextPageToken. De onderstaande code bevat opnieuw de magie. En als geheugensteuntje heb ik ook het PowerShell-fragment uit deel 1 toegevoegd waarin we paginering behandelden.

-
pagingType: Het type paginering dat we willen gebruiken. Andere mogelijke opties zijn bijvoorbeeld
LinkHeaders
,NextPageUrl
ofOffset
. Net als bij het authenticatieobject bepalen de andere eigenschappen van dit object welke velden je moet instellen, afhankelijk van het gekozen type. -
nextPageParaName: Bij paginering op basis van
NextPageToken
moet elk volgend verzoek de nieuwe NextPageToken uit de vorige response bevatten. Omdat de 1Password API verwacht dat dit wordt meegegeven als"cursor": <cursor info>
, gebruiken we hiercursor
. -
nextPageTokenJsonPath: Het pad naar de token in de JSON-response.
$
staat voor de root van de API-response. Bij 1Password zit de cursor incontent.cursor
, dus gebruiken we$.cursor
. -
hasNextFlagJsonPath: Het pad naar de boolean die aangeeft of er nog meer items zijn of dat het tijdsvenster volledig is doorlopen. Voor 1Password is dit het
has_more
-veld, dus gebruiken we$.has_more
.
Met bovenstaande instellingen zou de CCP soepel door de verschillende pagina’s van de 1Password-response moeten kunnen bladeren.
“Maar hoe verwerken we de response eigenlijk?” Fijn dat je het vraagt!
Response
Dit deel is eenvoudig. De logs staan in het “items”-veld van de response, dus:
Yep, zo simpel is het. Het formaat is json en het pad naar de events is gewoon items. Makkie!
Nu moet ik wel bekennen: eerder zei ik dat het dataConnectorPoller bestand alleen uit de vier besproken onderdelen zou bestaan – dat klopt niet helemaal. Omdat we werken met versie 2 van het CCP, moeten we nog drie extra zaken toevoegen: we moeten onze poller koppelen aan het dataConnectorDefiniton bestand (uit deel 2), we moeten het dataType opgeven dat deze poller ophaalt (ook uit deel 2) en we moeten aangeven welke Data Collection Rule (DCR) deze poller gebruikt (komt in deel 4). De code hiervoor ziet er uit als volgt:
- connectorDefinitionName moet overeenkomen met de id uit het connectorDefinition-bestand.
- dataType is een van de types uit het dataTypes-blok in datzelfde bestand.
- dcrConfig bevat koppelingen naar je DCR (komt in deel 4).
Hoewel dit gedeelte van de sjabloon eenvoudiger is dan de voorgaande, is het belangrijk om het zorgvuldig te behandelen. Dit deel vormt namelijk de koppeling tussen het bestand dataConnectorDefinition.json
en het bestand dataConnectorPoller.json
, evenals de koppeling tussen het bestand dataConnectorPoller.json
en het bestand dcr.json
. Als er tijdens het verpakken fouten optreden die wijzen op ontbrekende koppelingen, is dit het gedeelte om te controleren!
Samenvattend
We hebben veel behandeld in deze blog. Je weet nu precies hoe je het CCP kunt laten doen wat jij wilt: het gaat de API-aanroep doen die je wilt, gebaseerd op je sjabloon. Gefeliciteerd!
Als je wat meer uitdaging wilt, herinner je je misschien dat we in het vorige deel drie datatypes hebben gedefinieerd: OnePasswordAuditEvents_CL
, OnePasswordSignInEvents_CL
en OnePasswordItemUsageEvents_CL
. De bovenstaande poller zorgt voor het ophalen van de SignInEvents
. Kun jij de resterende twee pollers maken?
Zoals altijd kun je de code voor deze blog hier op GitHub vinden (Spoiler alert: het bevat de resterende twee pollers, dus als je wilt oefenen, doe dat dan eerst).
We zijn er nog niet helemaal. Je hebt nu twee ongelooflijk ingewikkelde ARM-sjablonen die je nog steeds niet kunt implementeren. Gelukkig naderen we het einde van de tunnel. In de volgende aflevering zal ik kort de DCR- en tabelsjablonen behandelen, waarna we direct overgaan tot het daadwerkelijk verpakken en implementeren van je connector, zodat je deze “in het wild” kunt ervaren.
Bedankt voor het lezen van het derde deel van deze serie blogs. In de vierde en laatste blog gaan we alle losse eindjes aan elkaar verbinden. Na het lezen van alle vier de blogs beschik je gegarandeerd over alle benodigde kennis voor het deployen van je eigen CCP-connector en het laten draaien hiervan in je Sentinel-werkruimte.