Meerdere malen heb ik gediscussieerd met DevOps teams of ze nu wel of niet Role Assignments mogen maken binnen hun ‘eigen’ omgeving. Vanuit security oogpunt kun je hier je bedenkingen bij hebben. Zelf ga ik ervanuit dat een DevOps team dit niet wil misbruiken, maar juist wil gebruiken waarvoor het bedoeld is. In dit blog ga ik verder in op het argument waarom je wél permissions zou willen geven en leg ik uit hoe je dit veilig kunt doen.
Onlangs voerde ik de discussie vanuit de positie als ontwikkelaar van een landingzone met een DevOps team dat wil on-boarden op ‘onze’ landingzone. Uiteraard was ik vrij stellig dat we niet zomaar een team Owner of User Access Administrator rechten geven, om zelf de Role Assignments te kunnen beheren. Echter door heel stellig nee te zeggen kom je niet verder. Wat helpt is om een stap terug te doen en het probleem vanuit de andere kant te bekijken, in dit geval vanuit het DevOps team wat de workload wil deployen.
Inmiddels hebben we het DevOps team de rechten gegeven om zelf de Role Assignments aan te maken, maar niet onvoorwaardelijk uiteraard…
Probleemomschrijving
Binnen een landingzone wil een DevOps team hun workload deployen. Een DevOps team mag hun resources in één of meerdere Resource Groups deployen. Op deze Resource Group(s) krijgen ze Contributor rechten om de resources te deployen. Tot zover werkt dat allemaal prima, echter…
Sommige resources gebruiken System Assigned Identities of User Assigned Identities. Je voelt hem waarschijnlijk al aankomen, die identities moeten permissions (Role Assignments) krijgen op andere resources (bijv. Storage Account). Aangezien er enkel Contributor rechten zijn uitgegeven kan het DevOps team deze Role Assignments niet zelfstandig deployen...
Als we ‘niets’ doen, dan zal het DevOps team contact op moeten nemen met het team dat de landingzone beheerd om de Role Assignments die ze willen hebben aan te laten maken. Op zich geen probleem, als je om werk verlegen zit. Het aanvragen van zo’n Role Assignment zal dan via een ticketsysteem of iets dergelijks moeten verlopen… Waarbij je gelijk de volgende problemen ziet aankomen:
- Het juiste ObjectId en ResourceId moet nauwkeurig worden overgenomen, hiermee val je wellicht terug op ‘handmatige’ acties, wat weer foutgevoelig is…
- Het ticket kan vervolgens zomaar nog een dag ergens in de queue staan…
Hiermee haal je, althans in mijn ogen, één van de voordelen van deployments in de cloud onderuit. Even snel wat testen is er niet meer bij voor een DevOps team.
Oplossing
De oplossing voor dit probleem bestaat uit meerdere componenten. Ik zal proberen stap voor stap uit te leggen hoe het een en ander op elkaar inhaakt.
Custom Role
Bij voorkeur geef je een DevOps team niet de Owner of User Access Administrator role. Met de Owner role kun je werkelijk alles doen wat je wil en als je User Access Administrator combineert met de Contributor role, dan kun je bijna net zoveel als wat je zou kunnen met de Owner role. Met de User Access Administrator Role krijg je ook de rechten om Azure Policies aan te passen, uit het vervolg van het verhaal zal duidelijk worden waarom je dat niet wil.
We maken een Custom Role welke het DevOps team de rechten geeft om Role Assignments te lezen, schrijven en verwijderen. Deze Custom Role assign ik samen met de Contributor role op de Resource Group. Zie hieronder hoe de Custom Role eruitziet. Dit moet voldoende zijn om het DevOps team Role Assignments te kunnen laten maken en verwijderen en hun Resources te deployen.
{
"id": "/subscriptions/<subscriptionId>/providers/Microsoft.Authorization/roleDefinitions/<roleDefinitionId>",
"properties": {
"roleName": "roleassignments",
"description": "Custom Role to allow roleAssignments",
"assignableScopes": [
"/subscriptions/<subscriptionId>"
],
"permissions": [
{
"actions": [
"Microsoft.Authorization/roleAssignments/read",
"Microsoft.Authorization/roleAssignments/write",
"Microsoft.Authorization/roleAssignments/delete"
],
"notActions": [],
"dataActions": [],
"notDataActions": []
}
]
}
}
Azure Policy
Azure Policy gebruik ik om voorwaarden te stellen aan de Role Assignments die de DevOps mogen maken. Zie hieronder de voorwaarden:
- Het DevOps team mag niet de Owner of User Access Administrator Roles toekennen.
- Het DevOps team mag niet de Contributor rol en onze roleassignments Custom Role toekennen.
- Het DevOps team mag enkel een overeengekomen lijst van Roles toekennen.
De eerste voorwaarde kunnen we invullen door de volgende Policy Definition aan te maken. Wat opvalt in deze policy is dat er twee Id’s zijn ingevuld. Deze Id’s zijn terug te vinden in de documentatie van Azure of via het commando: az role definition list -n Owner.
{
"properties": {
"displayName": "Deny Owner and User Access Administrator Role Definitions",
"policyType": "Custom",
"mode": "All",
"description": "This policy will Deny the Owner and User Access Administrator Role ",
"metadata": {
"category": "IAM"
},
"parameters": {},
"policyRule": {
"if": {
"allOf": [
{
"equals": "Microsoft.Authorization/roleAssignments",
"field": "type"
},
{
"in": [
"8e3af657-a8ff-443c-a75c-2fe8c4bcb635",
"18d7d88d-d35e-4fb5-a5c3-7773c20a72d9"
],
"value": "[split(field('Microsoft.Authorization/roleAssignments/roleDefinitionId'),'roleDefinitions/')[1]]"
}
]
},
"then": {
"effect": "Deny"
}
}
},
"type": "Microsoft.Authorization/policyDefinitions",
"name": "deny-owner-useraccessadmin-roleassignment"
}
De tweede voorwaarde, het niet mogen toekennen van Contributor of onze eigen Custom Role, vullen we in met de volgende policy. Wat van belang is hierbij, is dat we deze Roles zelf natuurlijk wel moeten kunnen toekennen aan het team. Het idee bij deze policy is, dat we het de Contributor en onze eigen Custom Role wel gaan toestaan. Echter staan we deze Role Assignment enkel toe als deze gekoppeld wordt aan een AAD Group die we hier definieren.
{
"properties": {
"displayName": "Ensure role is only assigned to specific AD group",
"policyType": "Custom",
"mode": "All",
"description": "This policy is used to ensure role is only assigned to specific AD group",
"metadata": {
"category": "IAM"
},
"parameters": {
"aadGroupIds": {
"type": "array",
"metadata": {
"description": "IDs of AAD group",
"displayName": "IDs of AAD group"
}
},
"roleDefId": {
"type": "string",
"metadata": {
"description": "Role Definition ID of (Custom) Role",
"displayName": "Role Definition ID of (Custom) Role"
}
}
},
"policyRule": {
"if": {
"anyOf": [
{
"allOf": [
{
"equals": "Microsoft.Authorization/roleAssignments",
"field": "type"
},
{
"contains": "[parameters('roleDefId')]",
"field": "Microsoft.Authorization/roleAssignments/roleDefinitionId"
},
{
"field": "Microsoft.Authorization/roleAssignments/principalId",
"notIn": "[parameters('aadGroupIds')]"
}
]
}
]
},
"then": {
"effect": "Deny"
}
}
},
"type": "Microsoft.Authorization/policyDefinitions",
"name": "ensure-role-assignments"
}
De derde voorwaarde, om enkel overeengekomen Roles toe te kennen maken we als volgt.
{
"properties": {
"displayName": "Allowed Role Definitions",
"policyType": "Custom",
"mode": "All",
"description": "This policy defines an allow list of role definitions that can be used in IAM",
"metadata": {
"category": "IAM"
},
"parameters": {
"roleDefinitionIds": {
"type": "array",
"metadata": {
"description": "The list of Approved Role Definition Ids",
"displayName": "The list of Approved Role Definition Ids"
}
}
},
"policyRule": {
"if": {
"allOf": [
{
"equals": "Microsoft.Authorization/roleAssignments",
"field": "type"
},
{
"notIn": "[parameters('roleDefinitionIds')]",
"value": "[split(field('Microsoft.Authorization/roleAssignments/roleDefinitionId'),'roleDefinitions/')[1]]"
}
]
},
"then": {
"effect": "Deny"
}
}
},
"type": "Microsoft.Authorization/policyDefinitions",
"name": "allowed-role-definitions"
}
Deze heeft mogelijk wat extra uitleg nodig. Wanneer je deze policy gaat assignen, dan dien je de Roles die je in de tweede voorwaarde hebt opgevoerd op te voeren, maar ook de Roles waarvan je met het DevOps team hebt afgesproken dat ze die zelf mogen aanmaken. Voeg je de Roles uit de tweede voorwaarde niet op, dan kun je die Role Assignments ook niet deployen.
De policies dienen te worden aangemaakt en vervolgens op de Resource Group te worden assigned. Hadden we het DevOps team de ‘User Access Administrator’ Role toegekend, dan hadden ze de Policies zelf kunnen aanpassen, dat willen we uiteraard niet. Let dus altijd goed op welke permissions je een team geeft. Wat van belang is, is dat de combinatie van deze policies ervoor zorgt dat alle voorwaarden worden ingevuld. Het implementeren van één van de policies heeft zeker niet het gewenste effect. Stel, dat je enkel de eerste Policy implementeert, dan worden enkel de ‘Owner’ en ‘User Access Administrator’ Role expliciet 'denied', met als gevolg dat alle andere impliciet 'allowed' zijn. Het is dus belangrijk om je policies goed te testen, zodat ze het gewenste effect hebben.
Discussie
Deze oplossing laat zien wat de kracht is van Azure Policies en hoe je dit kunt inzetten. Het is goed om DevOps teams het vertrouwen te geven, maar ook om de juiste voorwaarden eromheen te bouwen zodat je toch een veilige omgeving kunt bouwen met elkaar. Mijn mening is dat we met deze oplossing een DevOps teams kunnen ondersteunen om hun werkzaamheden binnen de landingzone zoveel mogelijk zelfstandig uit te voeren. Op deze manier voorkomen we dat het team dat de landingzone beheert de bottleneck gaat vormen. Waar ik tevens benieuwd naar ben is of anderen dit probleem ook herkennen, en hoe jullie dit opgelost hebben?