Archives de catégorie : Azure

Migrer une machine virtuelle entre régions Azure

Voilà un cas client qui m’a occupé un certain temps. Mon client déploie une application basée sur des services IaaS à l’aide d’images préalablement générées. Mes images « sources » sont localisées dans la région Azure France Central et doivent être mise à disposition dans la région Brazil South. Voilà pour le besoin de base.

Mon premier réflexe a été de rechercher si un service de réplication d’images n’existait pas. On a bien un repository pour les images pour les conteneurs (Azure Contrainer Registry), on doit donc avoir le même type de service pour le IaaS. C’est effectivement le cas, cela se nomme Shared Image Gallery. Mais, j’ai tout de suite exclu ce choix car :

  • Service actuellement en Preview
  • Pas encore disponible dans les régions Azure qui me concernent

Second réflexe, Azure Site Recovery. Cela fait quelques temps que l’on peut utiliser le service pour « cloner » une machine virtuelle existante pour assurer un DRP vers une autre région Azure. Très rapidement, j’ai dû abandonner cette option car les régions Azure source et destination ne dépendent pas du même cluster géographique.

clip_image001

Source : https://docs.microsoft.com/en-us/azure/site-recovery/azure-to-azure-support-matrix

Pire, le cas de la région Azure Brazil South est un peu particulier car au sein de son Géo, il n’a pas de région Azure associée. Bref, pas la bonne solution.

En dernier ressort, je suis tombé sur cet article : Synchronously copy all managed disks of an Azure Virtual Machine to Azure Storage Accounts in multiple Azure regions. C’est presque ce qu’il me faut car il faudra reconstruire une image après. A mes yeux, la démarche proposée avait quelques défauts :

  • On paie une VM pour faire la copie avec AZCopy alors qu’on peut faire un job de copie
  • Elle est un peu salée la VM en SKU E pour de la copie de fichiers
  • Les opérations sont réalisées en séquentielle alors qu’on se contente de suivre un job.

J’avais déjà publié un billet pour cloner des machines virtuelles dans Azure. J’ai donc juste poussé la logique d’industrialisation jusqu’au bout. Cela va se dérouler en plusieurs étapes :

  • Etape n°1 : Initialisation
  • Etape n°2 : Génération des clés SAS pour les VHD
  • Etape n°3 : Initialisation des opérations de copie
  • Etape n°4 : Suivi des opérations de copie
  • Etape n°5 : Reconstruction des Managed Disks

 

Etape n°1 : Initialisation

Avant de commencer, on va poser quelques bases. J’ai déployé une machine virtuelle en utilisant mon image dans le groupe de ressources « FranceCentral ». L’objectif est d’obtenir des Managed Disks dans le groupe de ressources « BrazilSouth » dans lequel on va préalablement avoir créé un Storage Account qui va être utilisé pour réceptionner les VHD à copier.

$SourceResourceGroup = « FranceCentral »

$SourceVMName = « MASTER »

$dataDiskNames = New-Object System.Collections.ArrayList

$dataDiskSASes = New-Object System.Collections.ArrayList

$SasKeyDuration = 36000

$DestinationResourceGroup = « BrazilSouth »

$DestinationStorageAccount = « brazilreplication »

$Accounttype = ‘Standard_LRS’

$VM = Get-AzureRmVM -ResourceGroupName $SourceResourceGroup -Name $SourceVMName -Verbose

clip_image002

 

Etape n°2 : Génération des clés SAS pour les VHD

Pour chaque disque composant notre machine virtuelle, nous devons générer une clé SAS. J’ai volontairement configuré une durée de vie assez longue, ne voulant pas tomber dans une situation ou les clés SAS auraient échouées pour cause d’une copie beaucoup trop longue.

$osDiskName = $VM.StorageProfile.OsDisk.Name

$osDiskSAS = (Grant-AzureRmDiskAccess -Access Read -DiskName $osDiskName -ResourceGroupName $SourceResourceGroup -DurationInSecond $SasKeyDuration -Verbose).AccessSAS

foreach($dataDisk in $VM.StorageProfile.DataDisks)

{

$dataDiskNames.Add($dataDisk.Name) | Out-Null

}

foreach($dataDiskName in $dataDiskNames)

{

$dataDiskSASes.Add((Grant-AzureRmDiskAccess -Access Read -DiskName $dataDiskName -ResourceGroupName $SourceResourceGroup -DurationInSecond $SasKeyDuration -Verbose).AccessSAS) | Out-Null

}

clip_image003

 

A ce stade, si on observe un des objets Managed Disks dans le portail, on peut constater qu’une clé SAS a bien été générée pour chacun d’eux.

clip_image004

 

Etape n°3 : Initialisation des opérations de copie

C’est en ce point que je me suis différencié de l’article Synchronously copy all managed disks of an Azure Virtual Machine to Azure Storage Accounts in multiple Azure regions. Mon besoin n’étant pas de répliquer mes disques dans de multiples régions, pas besoin d’autant d’instance de la machine virtuelle Ubuntu pour réaliser les opérations de copies. En plus, on n’a pas besoin d’attendre. Ça fait bien longtemps qu’AZCopy.EXE supporte la notion de job pour les opérations de copies. En PowerShell, AZCOPY.EXE, c’est Start-AzureStorageBlobCopy. A ce stade, du contenu va commencer à apparaître dans le contenu prévu à cet effet dans le Storage Account localisé dans la région cible.

$DestStorageAccountKeys = Get-AzureRmStorageAccountKey -ResourceGroupName $DestinationResourceGroup -Name $DestinationStorageAccount

$DestStorageContext = New-AzureStorageContext -StorageAccountName $DestinationStorageAccount -StorageAccountKey $DestStorageAccountKeys[0].Value

Start-AzureStorageBlobCopy -AbsoluteUri $osDiskSAS -DestContainer $SourceVMName.ToLower() -DestContext $DestStorageContext -DestBlob $($osDiskName + « .VHD »)

$DatadiskCount = 0

foreach($dataDiskSAS in $dataDiskSASes)

{

Start-AzureStorageBlobCopy -AbsoluteUri $dataDiskSAS -DestContainer $SourceVMName.ToLower() -DestContext $DestStorageContext -DestBlob $(« datadisk-$DatadiskCount » + « .VHD »)

$DatadiskCount +=1

}

clip_image005

 

Etape n°4 : Suivi des opérations de copie

La commande PowerShell Start-AzureStorageBlobCopy initié un job que l’on va suivre. Avantage, on va paralléliser toutes les opérations de copie. Pour suivre cela, on dispose de la commande PowerShell Get-AzureStorageBlobCopyState. La durée des opérations de copie dépendra principalement de la volumétrie de données à répliquer. Pour mes tests, quarante-neuf minutes étaient nécessaires pour répliquer un Managed Disk SATA de 127Go et un autre de 1To.

$StartTime = Get-Date

$StorageOperations = Get-AzureStorageBlob -Container master -Context $DestStorageContext | Get-AzureStorageBlobCopyState

While ($StorageOperations | Where-Object {$_.status -eq « Pending »})

{

$StorageOperations | select-Object Copyid, Status

Start-Sleep -Seconds 10

$StorageOperations = Get-AzureStorageBlob -Container master -Context $DestStorageContext | Get-AzureStorageBlobCopyState

}

$CopyTimeSpan = New-TimeSpan -Start $StartTime -End (Get-Date)

$Totalcopyoperations ='{0:N0}’ -f $($CopyTimeSpan.TotalMinutes)

Write-Host « Copy operation completed in $Totalcopyoperations minutes. »

clip_image006

 

Etape n°5 : Reconstruction des Managed Disks

Il ne nous reste plus qu’à reconstruire des Managed Disks en commençant par construire un objet de type Managed Disk à l’aide de la commande PowerShell New-AzureRmDiskConfig. De, là, il n’y a plus qu’à créer la ressource Azure à l’aide de la commande PowerShell New-AzureRmDisk.

$Container = Get-AzureStorageContainer -Container master -Context $DestStorageContext

$ContainerBaseURI = ($Container.CloudBlobContainer).uri.absoluteuri

$ListVHDs = Get-AzureStorageBlob -Container master -Context $DestStorageContext

ForEach ($VHD in $ListVHDs)

{

$Disksize = ($vhd.Length/1GB) +1

$Vhduri = $ContainerBaseURI + « / » + $vhd.name

$diskName = ($vhd.name).Substring(0, ($vhd.name).lastindexof(« . »))

Write-Host « Creating Managed Disk $diskName in Resource Group $DestinationResourceGroup. »

$diskConfig = New-AzureRmDiskConfig -AccountType $Accounttype -Location ((get-azurermresourcegroup -Name $DestinationResourceGroup).location) -DiskSizeGB $Disksize -SourceUri $vhdUri -CreateOption Import

New-AzureRmDisk -DiskName $diskName -Disk $diskConfig -ResourceGroupName $DestinationResourceGroup

}

clip_image007

 

Maintenant, il y a plus qu’à reconstruire l’image de référence au brésil et supprimer les fichiers VHD.

 

Conclusion

La solution n’est pas parfaite, elle peut encore être améliorée. Si vous être plus de culture Linux, allez lire ce billet : Copy custom vm images on azure. L’approche est sensiblement identique.

 

Benoît – Simple and Secure by design but Business compliant.

Stocker des clés SAS de Storage Account dans un KeyVault

Voilà un peu plus d’un an, j’avais publié ce billet : Valet Key avec Azure Function App. Depuis ce temps, je l’utilise énormément. Ce n’est que très récemment en passant un peu de temps dans le KeyVault que j’ai découvert la commande PowerShell Set-AzureKeyVaultManagedStorageSasDefinition. Ça a un peu changé mon approche initiale. Avant, mon Azure Function retournait un clés SAS prête à consommer. Maintenant, j’utilise le Key-Valet pour stocker les clés SAS mises à disposition. Le KeyVault peut stocker ce type de secret (pas visible dans le portail à ce jour). On peut donc ainsi mettre à disposition des clés SAS pré-générées à des d’applications (limitées dans le temps évidement). Pour ce billet, on va se contenter de mettre en œuvre la fonctionnalité :

  • Etape n°1 : Initialisation de notre environnement
  • Etape n°2 : Génération du token SAS
  • Etape n°3 : Expérience côté consommateur

 

Etape n°1 : Initialisation de notre environnement

Pour initialiser notre environnement, nous allons commencer par définir quelques variables. La variable $KeyVaultConsumer désigne un utilisateur référencé dans notre Azure AD pour lequel nous allons mettre en place des permissions RBAC (pour accéder au KeyVault) ainsi que des Access-Policies pour accéder au secret mis à disposition.

$KeyVaultConsumer = « keyvaultconsumer@xxxxxxxxxx.xxx »

$random = Get-Random -Minimum 0 -Maximum 999

$AzureRegion = « WestEurope »

$vaultname = « keyvaultlab$random »

$ResourceGroupname = « KeyVaultLab$random »

$storageaccountname = « keyvaultlab$random »

$sastokenname = « sas$random »

$saspermission = « racwdlup »

$Sastokendurationinminutes = 15

$StorageSKUName = ‘Standard_LRS’

$StorageKind = ‘StorageV2’

$UserPrincipal = (Get-AzureRmContext).Account.id

clip_image002

 

Avec ces variables, nous allons pouvoir initialiser notre environnement comprenant un groupe de ressources contenant à la fois un Storage Account et une instance de Key Vault. Jusque-là rien de très nouveau.

New-AzureRmResourceGroup -name $ResourceGroupname -Location $AzureRegion

$StorageAccount = New-AzureRmStorageAccount -ResourceGroupName $ResourceGroupname -Name $storageaccountname `

-Location $AzureRegion `

-Kind $StorageKind `

-SkuName $StorageSKUName

$Keyvault = New-AzureRmKeyVault -ResourceGroupName $ResourceGroupname -Name $vaultname -Location $AzureRegion -Sku Standard

clip_image004

A ce jour, le portail Azure ne permet pas encore de gérer les permissions sur le Storage. On va donc commencer par se positionner des permissions pour ce type de secret puis référencer notre Storage Account comme « ManagedStorageAccount » auprès de notre instance KeyVault avec la commande Add-AzureKeyVaultManagedStorageAccount

Set-AzureRmKeyVaultAccessPolicy -ResourceGroupName $ResourceGroupname -VaultName $vaultname `

-PermissionsToStorage get,list,delete,set,update,regeneratekey,getsas,listsas,deletesas,setsas,recover,backup,restore,purge `

-UserPrincipalName $UserPrincipal

$keyVault = Get-AzureRmKeyVault -VaultName $vaultname -ResourceGroupName $ResourceGroupname

$Keyvault.AccessPoliciesText

Add-AzureKeyVaultManagedStorageAccount -VaultName $vaultname -AccountName $storageaccountname `

-AccountResourceId $storageaccount.id `

-ActiveKeyName key1 `

-DisableAutoRegenerateKey

$StorageContext = New-AzureStorageContext -StorageAccountName $storageaccountname -Protocol https -StorageAccountKey Key1

clip_image006

Pour ceux qui ont suivi mon billet « Sécuriser ses Storage Accounts dans Azure« , c’est pour l’instant très proche. On peut donc même cumuler les deux fonctionnalités.

 

Etape n°2 : Génération du token SAS

De là, nous pouvons générer notre clé SAS. Par rapport à une approche Valet-Key, le secret n’est pas retourné au demandeur mais directement stocké dans le KeyVault. Attention, ce n’est pas un secret mais un objet de type « ms-sastoken-storage »

$start = [System.DateTime]::Now.AddMinutes(-1)

$end = [System.DateTime]::Now.AddMinutes($Sastokendurationinminutes)

$AccessToken = New-AzureStorageAccountSasToken -Service blob,file,Table,Queue `

-ResourceType Service,Container,Object `

-Permission $saspermission `

-Protocol HttpsOnly `

-StartTime $start `

-ExpiryTime $end `

-Context $StorageContext

$AccessToken

Set-AzureKeyVaultManagedStorageSasDefinition -AccountName $storageaccountname `

-VaultName $vaultname `

-Name $sastokenname `

-TemplateUri $AccessToken `

-SasType ‘account’ `

-ValidityPeriod ([System.Timespan]::FromDays(30))

$secret = Get-AzureKeyVaultSecret -VaultName $vaultname -Name « $vaultname-$sastokenname »

$secret.SecretValueText

$secret.Attributes.ContentType

clip_image008

Pour qu’un consommateur puisse accéder à ce nouveau type de secret (ms-sastoken-storage), il convient de s’assurer qu’il puisse voir l’objet KeyVault dans notre groupe de ressource puis de lui assigner une Access Policy avec uniquement les permissions nécessaires.

New-AzureRmRoleAssignment -SignInName $KeyVaultConsumer -ResourceGroupName $ResourceGroupname -RoleDefinitionName Reader

set-AzureRmKeyVaultAccessPolicy -ResourceGroupName $ResourceGroupname -VaultName $vaultname `

-PermissionsToSecrets Get, List `

-UserPrincipalName $KeyVaultConsumer

Set-AzureRmKeyVaultAccessPolicy -ResourceGroupName $ResourceGroupname -VaultName $vaultname `

-PermissionsToStorage get, list, getsas, listsas -UserPrincipalName $KeyVaultConsumer

$keyVault = Get-AzureRmKeyVault -VaultName $vaultname -ResourceGroupName $ResourceGroupname

$Keyvault.AccessPoliciesText

clip_image010

Etape n°3 : Expérience côté consommateur

Stocker un nouveau type de secret, c’est bien. Maintenant, il faut pouvoir le consommer. Pour la suite de ce billet, je pars du principe que nous sommes connectés avec son identité ($KeyVaultConsumer).

$keyvault = Get-AzurermKeyVault | Out-GridView -Title « Select KeyVault containing SAS Key. » -PassThru

Get-AzureKeyVaultSecret -VaultName $Keyvault.VaultName | Select-Object Name

$secret = Get-AzureKeyVaultSecret -VaultName $Keyvault.VaultName -Name (Get-AzureKeyVaultSecret -VaultName $Keyvault.VaultName).Name

$secret.ContentType

$secret.SecretValueText

clip_image012

 

Maintenant, on peut imaginer des process qui vont automatiquement renouveler des clés pour des consommateurs. Si en plus on combine avec la rotation automatique des clés de Storage Account vu dans mon précédent article, il n’y a plus de raison d’utiliser les clés Full.

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Sécuriser ses Storage Accounts dans Azure

Le stockage Azure est certainement le composant d’Azure le plus ancien. Nous avons actuellement un support Preview pour Azure AD en lieu et place des clés primaires / secondaires. Pourtant, aujourd’hui c’est d’elles que nous allons parler. Chaque Storage Account dispose de deux « Access Key » comme illustré ci-dessous. Notre ami le RSI/RSSI (c’est mieux de l’avoir comme ami que comme ennemi) est très sensible sur les sujets de chiffrement et de condition d’accès au stockage.

clip_image001

 

Il y a quelques bonnes pratiques autour de ces clés. La première est de ne pas les divulguer. A la place, on recommande un mécanisme de type Key-Valet comme illustré dans ce billet : Valet Key avec Azure Function App. La seconde est d’opérer une rotation des clés régulièrement. Notre ami le RSSI/ RSSI rêve d’un bouton rouge qui lui permettrait d’opérer une rotation des clés à loisir. Dans les faits, cela déroule de la manière suivante :

  • On identifie les applications consommant la clé primaire d’un Storage Account donné
  • On reconfigure les applications pour utiliser la clé secondaire
  • On opère une régénération de la clé primaire du Storage Account
  • On reconfigure les applications pour utiliser la nouvelle clé primaire

Voilà pour la théorie. Dans la pratique, c’est un peu plus compliqué que cela. Nous sommes capables de configurer nos applications / services Azure pour utiliser un secret dans un KeyVault, mais il manquait encore une rotation des clés pilotée depuis le KeyVault, c’est le sujet de ce billet.

Mise en place de l’environnement

Pour la mise en place de l’environnement, nous commencerons avec un Storage Account tout ce qu’il y a de plus classique. Sa seule réelle particularité est l’activation de la fonctionnalité Managed Service Instance (MSI). Celle-ci va générer une identité Azure AD qui permettra au Storage Account d’accéder à notre Key Vault pour accéder à sa clé de chiffrement. Au passage, on notera qu’à ce stade, les clés de chiffrement sont celles de Microsoft.

$resourcegroupName = ‘KeyVaultLab’

$StorageAccountname = ‘keyvaultlab994’

$Keyvaultname = ‘keyvaultlab994’

$location = ‘WestEurope’

$StorageSKU = ‘Standard_LRS’

$StorageKind = ‘StorageV2’

$StorageAccountKeyName = ‘key1’

$StorageAccountEncryptionKeyname = $StorageAccountname

New-AzureRmResourceGroup -Name $resourcegroupName -Location $location

$storageaccount = New-AzureRmStorageAccount -ResourceGroupName $resourcegroupName -Name $StorageAccountname -Location $location -SkuName $StorageSKU -Kind $StorageKind -AssignIdentity

$Storageaccount.Encryption

$Storageaccount.Encryption.KeySource

clip_image002

 

Pour compléter l’environnement, nous allons mettre en place une instance du service Key Vault. Celle-ci sera utilisée pour deux usages :

  • Le stockage de notre clé de chiffrement du contenu du KeyVault
  • La mise en place de la rotation automatique des clés
New-AzureRmKeyVault -ResourceGroupName $resourcegroupName –Name $Keyvaultname -Location $location -EnableSoftDelete -EnablePurgeProtection

Add-AzureKeyVaultKey -VaultName $Keyvaultname -Name $StorageAccountEncryptionKeyname -Destination ‘Software’

clip_image003

 

Au passage, on notera un type de permission qui à ce jour n’est pas encore visible dans le portail : Key Vault Managed Storage.

Pour ce Key Vault, nous avons créé un premier secret. Celui-ci sera utilisé pour le chiffrement du contenu du Storage Account avec notre clé.

$KeyvaultObject = Get-AzureRmKeyVault -VaultName $Keyvaultname

$KeyObject = Get-AzureKeyVaultKey -VaultName $Keyvaultname -KeyName $StorageAccountEncryptionKeyname

$KeyObject

clip_image004

 

Pour finir, nous allons mettre en place une « Access policy » au niveau de notre instance du service Key Vault. Cette permission va permet à l’identité Azure AD liée à notre Storage Account d’accéder au Key Vault pour récupérer la clé de chiffrement.

Set-AzureRmKeyVaultAccessPolicy -VaultName $Keyvaultname -ObjectId $Storageaccount.Identity.PrincipalId -PermissionsToKeys wrapKey, unwrapKey, get

(Get-AzureRmKeyVault -VaultName $Keyvaultname).AccessPoliciesText

clip_image005

 

Mise en place de « Customer Managed Key »

La mise en place de la fonctionnalité « Customer Managed Key » permet de ne plus utiliser les clés de chiffrement de Microsoft mais la nôtre, mise à disposition dans clé précédemment mise en œuvre. A ce jour des services comme Azure Table et Azure Queue ne permettent pas encore d’utiliser la fonctionnalité « Customer Managed Key ». Maintenant que notre Storage Account est capable d’accéder aux clés de l’instance Key Vault, nous allons utiliser la commande Set-AzureRmStorageAccount pour préciser les références de la clé de chiffrement à utiliser.

Set-AzureRmStorageAccount -ResourceGroupName $resourcegroupName -Name $StorageAccountname -KeyvaultEncryption -Keyname $KeyObject.Name -KeyVersion $KeyObject.Version -KeyVaultUri $KeyvaultObject.VaultUri

$Storageaccount = Get-AzureRmStorageAccount -ResourceGroupName $resourcegroupName -Name $StorageAccountname

$Storageaccount.Encryption

$Storageaccount.Encryption.KeySource

$Storageaccount.Encryption.KeyVaultProperties

clip_image006

 

Une fois la commande exécutée, on peut constater dans les caractéristiques de notre Storage Account que la clé de chiffrement est maintenant issue du Key Vault, donc sous notre responsabilité. Charge à nous d’en opérer la rotation. Dans le portail Azure, on peut maintenant constater que le paramètre « Encryption » référence bien l’utilisation d’un secret du Key Vault pour assurer le chiffrement des données.

clip_image007

 

Mise en place de la rotation automatique Access Key

C’est la partie la plus intéressante. La première « Access Key » sera stockée dans notre instance du service Key Vault. Techniquement, ce n’est pas l’identité de notre instance Key Vault qui réalisera l’opération mais le Resource Provider « Azure Key Vault ». J’ai passé beaucoup de temps avant de comprendre ce point particulier.

$KeyVaultServicePrincipal = Get-AzureRmADServicePrincipal | Where-Object {$_.displayname -like « Azure Key Vault »}

$KeyVaultServicePrincipal.id.Guid

clip_image008

Pour opérer la rotation des clés, le service Key Vault (au sens générique) doit pouvoir manipuler les Access Key de notre Storage Account. Cela tombe bien, il existe un rôle nommé « Storage Account Key Operator Role » qui remplit parfaitement cet usage. Nous allons assigner ce rôle sur le Storage Account pour l’identité de l’application Azure Key Vault.

Get-AzureRmRoleDefinition -Name ‘Storage Account Key Operator Service Role’

New-AzureRmRoleAssignment -ObjectId $KeyVaultServicePrincipal.id.Guid -RoleDefinitionName ‘Storage Account Key Operator Service Role’ -Scope $Storageaccount.Id

clip_image009

 

On peut constater la permission dans le portail Azure pour l’application « Azure Key Vault ».

clip_image010

 

Prochaine étape, nous allons nous attribuer de nouvelles permissions dans le Key Vault concernant le stockage. Au moment de l’écriture de ce billet, le portail ne permettait pas encore de manipuler ce nouveau type de permission. On va donc réaliser l’opération en PowerShell et ajouter une Access Policy à notre instance du service Key Vault.

Set-AzureRmKeyVaultAccessPolicy -VaultName $Keyvaultname -ResourceGroupName $resourcegroupName -UserPrincipalName (Get-AzureRmContext).account.id -PermissionsToStorage set, get, regeneratekey, list, getsas, setsas

clip_image011

 

Les plus attentifs auront noté les permissions « getsas » et « setsas ». C’est pour le prochain billet, une nouvelle approche du Key-Valet, …

Pour finir, nous allons configurer la rotation automatique de la première « Access Key » de notre Storage Account. L’opération sera réalisée tous les trente jours.

(Get-AzureRmKeyVault -VaultName $Keyvaultname).AccessPoliciesText

$RegenerationPeriod = [System.TimeSpan]::FromDays(30)

$parameters = @{

VaultName = $Keyvaultname

AccountResourceId = $StorageAccount.Id

AccountName = $StorageAccountname

ActiveKeyName = $StorageAccountKeyName

RegenerationPeriod = $RegenerationPeriod

}

Add-AzureKeyVaultManagedStorageAccount @Parameters

clip_image012

Afin de vérifier la bonne mise en place, nous allons opérer une première rotation avec les commandes PowerShell suivantes :

Get-AzureRmStorageAccountKey -ResourceGroupName $ResourceGroupName -Name $storageAccountName

Update-AzureKeyVaultManagedStorageAccountKey -VaultName $keyVaultName -AccountName $storageAccountName -KeyName $StorageAccountKeyName

Get-AzureRmStorageAccountKey -ResourceGroupName $ResourceGroupName -Name $storageAccountName

clip_image013

 

A noter que la rotation n’a été configurée que pour la première « Access Key », pas pour la seconde. Maintenant, vous connaissez la manœuvre.

Quelques notes

  • A ce jour, la fonctionnalité « Customer-Managed keys » ne concerne que les services Blog et File, pas Table et queue qui continuent à utiliser la clé de chiffrement gérée par Microsoft
  • L’utilisation de la fonctionnalité « Soft delete » sur le keyvault a pour conséquence qu’après avoir supprimé un KeyVault, son nom n’est pas réutilisable avant 90 jours, temps nous permettant de le restaurer. Il est vivement recommandé de l’activer.
  • Pour la rotation des clés, le nom de la clé est nécessairement Key1, Key2. N’essayez pas d’utiliser d’autres noms. Conséquence, on ne peut pas avoir tous les « Access Keys » dans un Key Vault unique. Il faut autant de Key Vault que de Storage Accounts

 

Conclusion

Normalement, avec ces deux opérations le RSI/RSSI de votre entreprise devrait apprécier. Il dispose maintenant d’un bouton rouge pour forcer la régénération des clés d’accès au stockage. Pour ne pas casser vos applications / ressources Azure, la prochaine étape sera de les reconfigurer pour extraire l’information du Key Vault et non continuer à coder l’information en dur dans le code ou dans une variable Application Settings dans Web App.

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Générer un certificat auto-signé avec Key-Vault

Pour ceux qui comme moi génèrent des certificats depuis longtemps, je suis passé par toutes les étapes (OpenSSL, New-SelfSignedCertificate). Avec Azure, il était logique que je regarde comment générer un certificat auto-signé. Le problème des solutions citées précédemment, c’était que le certificat était généré localement, dans le magasin personnel de la machine. Combien de fois avez-vous oublié le certificat et sa clé privée sur un serveur ou pire sur votre portable.

Avec Azure, l’usage des certificats s’est banalisé. On associe des Service Principals aux applications déclarées dans Azure AD que l’on consomme ensuite dans différents services (Azure Automation aujourd’hui par exemple). Pour cette raison, j’avais rapidement cherché un moyen de générer mes certificats auto-signés directement dans Azure. Logiquement, j’ai commencé par regarder le KeyVault. Une recherche rapide dans le module PowerShell associé me confirme que c’est bien prévu dans les scénarios du produit.

clip_image001

J’ai donc creusé un peu le sujet et voilà la version courte. On commence par préparer un objet CertificatePolicy :

$AutomationcertificateName = « LabAutomation »

$AutomationcertSubjectName = « cn= » + $AutomationcertificateName

$AutomationCertificateLifetimePolicy = 36

$Policy = New-AzureKeyVaultCertificatePolicy -SecretContentType « application/x-pkcs12 » -SubjectName $AutomationcertSubjectName -IssuerName « Self » -ValidityInMonths $AutomationCertificateLifetimePolicy -ReuseKeyOnRenewal

$Policy

clip_image002

 

Vous l’avez bien compris, on peut personnaliser avec beaucoup d’autres paramètres, mais on va faire court. Pour la suite, cela se passe avec la commande Add-AzureKeyVaultCertificate. Point de détail, la commande retourne un status, à nous de suivre jusqu’à ce que le certificat soit délivré :

$AddAzureKeyVaultCertificateStatus = Add-AzureKeyVaultCertificate -VaultName ‘mykeyvaultforcert’ -Name $AutomationcertificateName -CertificatePolicy $Policy

$AddAzureKeyVaultCertificateStatus.status

While ($AddAzureKeyVaultCertificateStatus.Status -eq « inProgress »)

{

Start-Sleep -Seconds 10

$AddAzureKeyVaultCertificateStatus = Get-AzureKeyVaultCertificateOperation -VaultName ‘mykeyvaultforcert’ -Name $AutomationcertificateName

$AddAzureKeyVaultCertificateStatus.status

}

$AddAzureKeyVaultCertificateStatus

clip_image003

 

Notre certificat auto-signé est maintenant dans le KeyVault, pour l’utiliser, ne nous reste plus qu’à l’exporter. La ça se complique un peu, il faut en passer par un peu de Dot.Net avec la classe X509Certificate2Collection. Dans le code ci-dessous, nous générons un fichier PFX contenant, le certificat, sa clé privée, le tout sécurité par un mot de passe (merci de fermer la session PowerShell après usage !)

$PfxCertPathForRunAsAccount = « C:\TEMP\CERTIFICATE.PFX »

$PfxCertPlainPasswordForRunAsAccount = « P@ssw0rd12345 »

$secretRetrieved = Get-AzureKeyVaultSecret -VaultName ‘mykeyvaultforcert’ -Name $AutomationcertificateName

$pfxBytes = [System.Convert]::FromBase64String($secretRetrieved.SecretValueText)

$certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection

$certCollection.Import($pfxBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)

$protectedCertificateBytes = $certCollection.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12, $PfxCertPlainPasswordForRunAsAccount)

[System.IO.File]::WriteAllBytes($PfxCertPathForRunAsAccount, $protectedCertificateBytes)

Get-ChildItem -Path « c:\temp\cert*.* »

clip_image004

 

Ne reste plus qu’à consommer. Avantage de cette approche, le certificat est préservé dans notre KeyVault. Dans mon contexte, Azure Automation est partie intégrante d’une solution en cours de développement, nous réinstancions donc plusieurs fois par jour nos instances.

 

BenoitS – Simple and Secure by design but Business compliant (with disruptive flag enabled)

Ma session au Powershell Saturday 2018

Ce samedi s’est déroulé l’édition française du PowerShell Saturday dans les locaux de Cellenza. Pendant, cette édition, j’ai eu l’occasion de présenter l’avancement sur Resource Group As a Service. Resource Group As a Service est un sujet que j’avais déjà présenté lors du Global Azure Bootcamp de 2018.

clip_image001

A l’époque, on était plus proche du PoC of Concept, entre temps, le développement a beaucoup avancé. Aujourd’hui, nous sommes maintenant plus proche du Minimum Viable Product. L’objectif de cette session n’était pas de présenter la solution en elle-même mais ce que son développement m’a permis d’apprendre sur Azure Function, Azure Automation et sur PowerShell lui-même. C’est donc plus une session de retour sur expérience.

Pour ceux que cela intéresse, la présentation ainsi que les exemples PowerShell présentés sont disponibles à cette URL.

 

Benoît – Simple and Secure by Design but Business compliant

Cloner des machines virtuelles dans Azure

Avec l’arrivée des Managed Disks, il a été introduit la capacité à cloner ces objets sous forme d’objets Snapshots. Après avoir pas mal joué avec j’y vois plusieurs avantages :

  • Recréer une machine virtuelle à partir d’un snapshot est bien plus rapide que de la restaurer depuis Azure Site Recovery (60Mb/S pour la vitesse).
  • Changer le nom des managed Disks histoire de s’aligner avec notre politique de nommage (genre après une bascule depuis On-Premises avec Azure Site Recovery)
  • Changer le nom de l’objet machine virtuelle (pas le nom du système d’exploitation)
  • Changer la performance des disques utilisés
  • Relocaliser les Managed Disks dans d’autres groupes de ressources (ca ne se déplace pas encore mais on peut cloner)
  • Réaliser une sauvegarde rapide et être en mesure de reconstruire rapidement (updates, opérations de maintenance critiques, mises en production le vendredi, …)
  • Changer l’Availability Zone / Availability Set / région Azure de la machine virtuelle
  • Relocaliser une machine virtuelle sur un autre Virtual network

 

Bref, il y a plein de raisons qui font que cloner une machine virtuelle peut être intéressant. Dans le détail des opérations voilà comment cela se présente :

  • Etape n°1 : Désallouer la machine virtuelle
  • Etape n°2 : Créer des snapshot des managed Disks
  • Etape n°3 : Créer des Managed Disks à partir des snapshots
  • Etape n°4 : Créer une nouvelle machine virtuelle
  • Etape n°5 : Ajouter les disques de données
  • Etape n°6 : Suppression des Snapshots

Etape n°1 : Désallouer la machine virtuelle

Même si l’opération peut être réalisé à chaud, on va quand même désallouer la machine virtuelle histoire de créer une image consistante de tous les Managed Disks. C’est un snapshot de chaque Manage Disks qu’il faudra réaliser. Rappel, désallouer ne veut pas dire shutdown dans le système d’exploitation.

 

Etape n°2 : Créer des snapshot des managed Disks

Nous allons créer autant d’objets Snapshots que de Managed Disks associés à notre machine virtuelle. Le seul disque que l’on ne pourra pas snapshoter. Commencez par identifier les noms Managed Disks associés à votre machine virtuelle. Dans l’exemple ci-dessous ma machine virtuelle comprend un disque OS et un disque de données.

clip_image001

 

Pour chaque Managed Disk, on va utiliser le bouton « Create Snapshot » comme illustré ci-dessous :

clip_image002

 

L’objet Snapshot sera créé dans la même région Azure. Par contre, il est tout à fait possible de créer l’objet dans un Resource Group différent. On peut même change la performance. As-t-on réellement besoin d’avoir notre snapshot en SSD?

clip_image003

Etape n°3 : Créer des Managed Disks à partir des snapshots

Il n’est pas possible de créer une machine virtuelle avec des Snapshots. On doit créer des Managed Disks à partir de nos snapshots.

clip_image004

 

Ici encore, ce qui est intéressant, c’est de pouvoir créer le Managed Disk dans une région Azure, une autre Availability zone. Vu que nous créons l’objet nous pouvons réappliquer notre charte de nommage (ceux qui ont migré des machines virtuelles depuis On-Premises me comprendrons, …).

clip_image005

 

Une fois l’opération terminée, nous devirons avoir autant de Managed Disks que de snapshots comme illustré ci-dessous :

clip_image006

Etape n°4 : Créer une nouvelle machine virtuelle

Avec des disques, nous pouvons créer une nouvelle machine virtuelle. Pour cela nous allons reprendre le Managed Disk contenant notre système d’exploitation. Nous allons utiliser le bouton « Create VM ».

clip_image007

 

Pour la création de la machine virtuelle, il n’y a pas grand-chose à dire.

clip_image008

 

Par contre, à cette étape, il y a un point intéressant. Nous pouvons redéfinir l’Availability zone, voire même l’Availability Set. Il y a même un bonus avec la possibilité de changer à quel Virtual Network la nouvelle carte réseau sera associée.

clip_image009

Etape n°5 : Ajouter les disques de données

N’oubliez pas de redéclarer vos disques de données et de sauvegarder la configuration.

clip_image010

Etape n°6 : Suppression des Snapshots

Conserver les snapshots n’a pas de sens sur le long terme. C’est du stockage facturé, tout comme les Managed Disks. Pensez donc à les supprimer dès que le clonage sera terminé.

clip_image011

 

 

Benoît – Simple and secure by design but Business compliant

Arretons de stocker os tokens Azure avec le module Powershell

Ca faisait quelques temps que j’avais remarqué un comportement « étrange » de certains de mes scripts Azure. Pour beaucoup d’entre eux, je commence par m’assurer avec un Get-AzuremContext que je suis bien authentifié. A ma grande surprise, je découvre que oui, pourtant, je n’ai renseigné aucun credential. Magie ? Nan. J’ai creusé un peu plus le sujet pour redécouvrir le module AzureRM.Profile. Ma recherche m’a amenée sur cette page : Automatic-Context-Autosave avec la découverte de quelques commandes inconnues :

clip_image001

 

Trois commandes ont attiré mon attention :

  • Disable-AzureRmContexteAutoSave
  • Enable-AzurermContextAutosave
  • Get-AzureRmContextAutoSaveSettings

J’ai donc commencé par Get-AzureRmContextAutoSaveSettings avec une surprise. Mes tokens sont bien conservés dans mon profil, c’est la configuration par défaut.

clip_image002

 

En ce qui me concerne, d’un point de vue sécurité, c’est moche. D’une part, je me balade avec les tokens de mes clients (MFA n’est pas encore chez tous et cela ne résout pas toujours le problème). C’est dommage qu’on fasse attention à utiliser des navigateurs en mode privé pour se connecter à Azure si le module PowerShell ignore le même ce concept. D’autre part, cela induit un risque. J’ai tendance à utiliser beaucoup de scripts chez mes différents clients. Avec cette fonctionnalité, je risque de travailler sur la mauvaise souscription sans m’en rendre compte avec un impact tout aussi industriel que le script.

Pour cette raison, j’ai pris l’habitude de désactiver cette fonctionnalité avec la commande Disable-AzureRmContextAutoSave comme illustré ci-dessous :

clip_image003

 

Maintenant, il n’y a plus de risque pour moi.

clip_image004

 

Benoît – Simple and secure by design but Business compliant

Un peu de PowerShell pour gérer les Network Security Groups

De temps en temps, j’ai des clients avec des challenges. Quand on mélange Azure et PowerShell, il y a risque que je réponse présent (même si finalement, je vais me faire mal). Je vous partage donc le challenge du moment. Un de mes clients est en phase de montée en puissance sur Azure avec une exigence, un contrôle strict des flux réseaux entrants et sortants, tout comme il le pratique On-Premises. Tout de suite, ça va faire beaucoup des règles dans les Network Security group. Autant dire tout de suite que le portail ne va pas être d’une grande aide sur le sujet.

Challenge intéressant, challenge Accepté, voici AzureImportNSG.PS1. Ce script PowerShell permet de :

  • Créer l’objet Network Security group (réalise une sauvegarde si existe déjà pour le rollback)
  • Injecter les règles en provenance d’un fichier XML
  • S’assure de la mise en place de la journalisation des flux dans votre instance Log Analytics préférée

clip_image001

Que reste-t-il à faire alors ? Juste lier le Network Security group à l’objet Subnet. Simple and Secure by design ! Et comme il faut pouvoir retravailler les règles, s’il y a un script d’import, il y a aussi un script d’export pour générer notre fichier XML.

clip_image002

Avec cela, plus d’excuse pour ne pas avoir des réseaux en béton. Les deux scripts sont disponibles sur mon repo GitHub.

Benoît – Simple and secure by design but Business compliant

Resource Group As a service – Mise en place du stockage

Après, un précédent billet pour poser les bases, il est temps de poser les fondations pour nos API. Première brique, un Resource Group. L’intégralité des ressources Azure sont stockées dans un groupe de ressources : New-AzureRMResourceGroup -ResourceGroupName ResourceGroupAsAService -Location « West Europe »

clip_image001

 

Côté stockage, j’ai identifié trois usages. Etant donné que nous allons avoir deux instances du service Azure Function, nous avons déjà deux instances de Storage Account. Techniquement, on peut très bien mutualiser mais je préfère isoler. Cela ne change rien au niveau de la facturation. La dernière instance, sera destinée à stocker les données utilisées par les API (Azure Table). La nature de ces données étant relativement sensibles, j’ai volontairement retenu une instance dédiée. Ci-dessous la commande PowerShell pour créer la première instance du Storage Account :

New-AzureRmStorageAccount -ResourceGroupName resourcegroupasaservice -AccountName resourcegroupasaservice1 -Location « West Europe » -SKUName « Standard_LRS » -Kind StorageV2 -EnableHttpsTrafficOnly $True

clip_image002

 

Puis la seconde instance : New-AzureRmStorageAccount -ResourceGroupName resourcegroupasaservice -AccountName resourcegroupasaservice2 -Location « West Europe » -SKUName « Standard_LRS » -Kind StorageV2 -EnableHttpsTrafficOnly $True

clip_image003

 

Et enfin la troisième instance : New-AzureRmStorageAccount -ResourceGroupName resourcegroupasaservice -AccountName resourcegroupasaservice3 -Location « West Europe » -SKUName « Standard_LRS » -Kind StorageV2 -EnableHttpsTrafficOnly $True

clip_image004

 

Nous allons passer un peu de temps avec cette troisième instance puisqu’elle va contenir des Azure tables qui vont être utilisées par les API. Commençons par récupérer les clés de cette instance de Storage Account : $keys = Get-AzureRmStorageAccountKey -ResourceGroupName resourcegroupasaservice -Name resourcegroupasaservice3

$keys

clip_image005

 

Nous allons utiliser la première clé pour nous créer un contexte pour créer les Azure tables suivantes :

  • AuthorizedCallers
  • AuthorizedEnvironments
  • AuthorizedIAMTemplates
  • DefaultCostCenter

 

$context = New-AzureStorageContext -StorageAccountName resourcegroupasaservice3 -StorageAccountKey $keys[0].value

« AuthorizedCallers AuthorizedEnvironments AuthorizedIAMTemplateRole AuthorizedPolicyAssignment DefaultCostCenter ».Split() | new-AzureStorageTable -Context $context

clip_image006

 

Avoir des tables c’est bien mais on a aussi besoin d’un peu de contenu. Pour cela, nous allons faire appel au module PowerShell AzureRmStorageTable disponible sur la PowerShell Gallery. Commençons par poser quelques bases :

Install-Module -Name AzureRmStorageTable

$resourcegroupname = « resourcegroupasaservice »

$Storageaccountname = « resourcegroupasaservice3 »

$location = « West Europe »

clip_image007

 

Jusqu’ici rien de bien sorcier. Mes tables sont localisées dans le groupe de ressources resourcegroupasaservice3, lui-même dépendant du Storage Account resourcegroupasaservice. Continuons avec l’alimentation de la première table Authorizedcallers. Celle-ci permet aux API de déterminer :

  • Si l’utilisateur est accrédité à utiliser le service ou non pour une souscription Azure donnée
  • La liste des valeurs acceptées pour le tag CostCenter qui sera associé au groupe de ressources à créer en fonction du demandeur
  • La liste des valeurs acceptées pour le tag Environnment qui sera associé au groupe de ressources à créer en fonction du demandeur
  • La liste des régions azure autorisées pour la création du groupe de ressources en fonction du demandeur

 

$table = Get-AzureStorageTableTable -resourceGroup $resourceGroupname -tableName « Authorizedcallers » -storageAccountName $Storageaccountname

$partitionkey = (Get-AzureRmContext).Subscription.ID

$rowkey = ‘Simplebydesign@bsautierehotmail.onmicrosoft.com’

Add-StorageTableRow -table $table -partitionKey $partitionKey -rowKey $rowkey -property @{« Authorized »= »true »; »AuthorizedCostCenters »= »ITDEPT,FINANCEDEPT »; »AuthorizedDefaultRegion »= »WestEurope »;

« AuthorizedEnvironments »= »TESTS »; »AuthorizedRegions »= »WestEurope,NorthEurope »}

clip_image008

 

L’utilisateur indiqué est déclaré autorisé à demander la création d’un groupe de ressources dans la souscription courante avec des valeurs prédéfinies pour les tags CostCenter et Environment. Voilà le mécanisme d’autorisation.

Passons à la table AuthorizedEnvironments. Lors de l’appel à l’API Request-ResourceGroup, l’appelant peut préciser quelle valeur associer au tag Environnement (dans la liste des valeurs autorisées). Si l’appelant ne précise aucun environnement en particulier, alors, l’API va consulter cette table pour déterminer la valeur par défaut.

$table = Get-AzureStorageTableTable -resourceGroup $resourceGroupname -tableName « AuthorizedEnvironments » -storageAccountName $Storageaccountname

$partitionkey = (Get-AzureRmContext).Subscription.ID

$rowKey = « TESTS »

Add-StorageTableRow -table $table -partitionKey $partitionKey -rowKey $rowkey

clip_image009

 

Maintenant, si nous appelions l’API Request-ResourceGroup sans préciser de valeur pour le tag Environnement, la valeur par défaut retenue serait « TESTS ». Pour le tag CostCenter, c’est le même principe. Si l’appelant ne précise pas une des valeurs qui lui sont autorisées pour la création d’un groupe de ressources dans une souscription Azure donnée, alors, l’API ira chercher la valeur par défaut à utiliser dans la table DefaultCostCenter

$table = Get-AzureStorageTableTable -resourceGroup $resourceGroupname -tableName « DefaultCostCenter » -storageAccountName $Storageaccountname

$partitionkey = (Get-AzureRmContext).Subscription.ID

$rowkey = ‘Simplebydesign@bsautierehotmail.onmicrosoft.com’

Add-StorageTableRow -table $table -partitionKey $partitionKey -rowKey $rowkey -property @{« CostCenter »= »ITDEPT »}

clip_image010

 

Ceci clos la partie stockage. Prochaine étape, on s’attaque à la gestion des secrets dans la solution avec du KeyVault à tout va.

 

BenoîtS – Simple and Secure by Design but Business compliant

Resource Group as A service – Introduction

Lors de ma session sur la gouvernance Azure au Global Azure Bootcamp, j’avais fait la démonstration du premier prototype de mon ensemble d’API « Resource Group as a Service ». Pour rappel, l’idée générale était de répondre à la problématique de la gouvernance Azure, en particulier au niveau des groupes de ressources. Le but de Resource Group as a service est de proposer un intermédiaire qui prend en charge la création et la configuration des Resources Groups pour le compte des demandeurs. Le développement a maintenant bien avancé (Pas encore une V1 mais au moins une Beta stable). Cette série de billets sera consacrée à cette API. Nous allons commencer par les Design Principles retenus pour ce développement.

Design Principles / contraintes imposées

  • Langage de développement : OK, c’est pas un vrai Design Principle. Normalement, le langage utilisé ne rentre pas en ligne de compte. Dans mon contexte, c’est plus ma contrainte imposée pour permettre un développement simple : PowerShell
  • Support de nos API : Azure Function. C’est mon choix. Pas la peine de mettre en œuvre une instance de Service Fabric pour porter quatre API. Pour rappel, une Azure Function a un temps d’exécution limité à 300 secondes, ce qui sera largement suffisant pour créer un groupe de Ressource. Ici aussi ce choix implique une contrainte car :

    • A ce jour le support de PowerShell en encore expérimental dans Azure Function
    • Le niveau de performance ne sera pas équivalent si on avait retenu C#
  • Authentification : Chaque appel à nos différentes API devront être authentifiés. Dans le contexte d’Azure Function, le choix le plus simple a été retenu avec la prise en charge de l’authentification via Azure AD. On reviendra sur ce choix ultérieurement car il aura quelques conséquences
  • Autorisation : L’authentification et l’autorisation sont deux choses bien distinctes. Ce n’est pas par ce qu’on va avoir accès aux API que tout nous sera autorisé. Dans mes Design Principles, j’ai retenu les points suivants :

    • Chaque utilisateur devra être enregistré comme autorisé pour pouvoir soumettre une demande de création de groupe de ressource
    • Le niveau d’autorisation sera individualisé au niveau de chaque souscription prise en charge par l’API
    • Le niveau d’autorisation prendra la notion de CostCenter au niveau de chaque souscription. Dans notre contexte, c’est un TAG positionné au niveau du groupe de ressources. Chaque utilisateur sera accrédité à un ou plusieurs codes d’imputation comptables qui sera associé au tag CostCenter
    • Le niveau d’autorisation prendra en compte la notion d’environnement au niveau de chaque souscription. Dans notre contexte, c’est un TAG positionné au niveau du groupe de ressources. Chaque utilisateur sera accrédité à demander la création d’un groupe de ressources avec une sélection de valeurs pour le TAG environnement.
    • Le niveau d’autorisation prendra en compte la notion de région pour limiter la création du groupe de ressources pour un ensemble de régions Azure donnée
  • Contexte d’exécution : Pour créer des ressources Azure, on a besoin d’une identité. Hors de question de coder des comptes / mots de passe ou même des clés d’accès au stockage. Choix a été fait d’utiliser la fonctionnalité Managed Identity Services pour nos Azure Function. Cela a quand même quelques implications :

    • Toutes les API regroupées dans une même instance du service Azure Function utiliseront donc la même identité
    • L’identité en question prendra la forme d’une application Azure AD et d’un Service Principal, lequel devra disposer des rôles nécessaires
  • Support de multiples souscriptions : Un contexte d’exécution propre à chaque souscription Azure sera nécessaire. Etant donné que ces multiples souscriptions peuvent ne pas utiliser le même tenant Azure AD comme fournisseur d’authentification, il ne sera pas possible d’utiliser l’identité des Azure Function (Managed Identity Services). Pour adresser cette problématique, il a été retenu que la solution associer un contexte d’exécution pour chaque souscription. Les secrets nécessaires pour chaque souscription seront stockés dans une instance distincte du service KeyVault. Cette séparation des secrets permettra de proposer aux gestionnaires des souscriptions de mettre à jour eux même le contexte de chaque souscription
  • Informer les consommateurs de leurs droits : Les principes retenus pour le mécanisme d’autorisation font que les consommateurs pourront disposer de privilèges bien différents selon les souscriptions. Afin de les informer des privilèges qui leurs sont associés, des API spécifiques seront proposées afin leur permettre de déterminer :

    • Les souscriptions auxquelles ils ont accès
    • Les tags Environnements qu’ils peuvent utiliser au sein d’une souscription Azure donnée
    • Les tags CostCenters qu’ils peuvent utiliser au sein d’une souscription Azure donnée
    • Les régions Azure qu’ils peuvent utiliser au sein d’une souscription Azure donnée
  • Stockage des données : Dans notre contexte, il a été retenu d’utiliser le service Azure Table pour gérer le mécanisme d’autorisation des API
  • Principe du moindre privilège : Principe essentiel pour le développement des API. A chaque fois que ce sera possible, le niveau d’accès de « moindre privilège » sera utilisé.

 

Ressource créée dans la souscription

  • Groupe de ressources : C’est quand même le but. Je n’ai pas encore prévu d’intégrer de contrôle sur le charte de nommage
  • Tags : Les groupes de ressources créé par l’API seront configurés avec un ensemble de Tags obligatoires et d’autres optionnels. Le notions d’environnements et de CostCenter sont eux obligatoires. L’utilisation de certains tags sera contrainte par les autorisations précisées dans les différentes tables Azure Table.
  • RBAC : Selon la configuration mise en place au niveau des Azure table, les groupes Azure AD seront positionnés avec des rôles builtin ou custom en fonction de la souscription Azure
  • Policies : Selon la configuration documentée dans les Azure Table, une ou plusieurs Azure policies seront positionnées en fonction de la souscription Azure.

 

Liste des API

Resource Group as A service, ce n’est pas qu’une seule API. En fait, c’est un peu plus compliqué que cela. Le tableau ci-dessous résume les API qui seront proposées via Azure Function :

Nom

Accès public

Méthode d’authentification

Managed Identity Service

Get-AuthorizedSubscription Oui Azure AD Activé
Get-AuthorisedEnvironments Oui Azure AD Activé
Get-AuthorizedCostCenters Oui Azure AD Activé
Get-AuthorizedRegions Oui Azure AD Activé
Request-ResourceGroup Oui Azure AD Activé
Get-ValetkeyForAzureTableRead Non Function Key Activé

 

Pour les quatre premières API, on comprend que c’est l’implémentation de la fonctionnalité permettant aux consommateurs de déterminer de quels privilèges ils disposent au sein des souscriptions prises en charge. Logiquement, ces APIs sont directement accessibles par les consommateurs pour peu qu’ils soient correctement authentifiés par Azure AD. Il en est de même pour l’API Request-ResourceGroup as a Service. Par contre, pour l’API Get-ValetkeyForAzureTableRead, la méthode d’authentification sera différente. Cette API sera utilisée pour respecter le principe du moindre privilège. Toutes les données relatives au mécanisme d’autorisation seront stockées dans des Azure Table au sein d’un Storage Account. La clé primaire de ce Storage Account sera bien stockée dans un Key Vault mais nous allons utiliser le Cloud Design Pattern Valet-Key pour générer un contexte d’accès limité à la permission Read sur les Azure Table, avec une durée de vie limitée.

clip_image001

 

Cette API ne sera pas consommée directement par les consommateurs mais en tant qu’API interne pour générer une clé d’accès au stockage. Il aurait été possible d’utiliser Azure AD comme méthode d’authentification (avec Managed Service Identity) mais cela introduisait un risque. Dès lors qu’un consommateur de l’API connait l’URL et se présente avec une identité Azure AD vérifiée, il aurait eu la possibilité de se générer des clés d’accès au stockage. Clairement une fausse bonne idée d’un point de vue sécurité. Pour cette raison, nous allons introduire une segmentation au niveau des API. Cette segmentation permet :

  • D’isoler une API qui donne accès à des informations sensible (il sera nécessaire de connaitre la Function Key qui sert de secret d’accès)
  • De distinguer les identités (Managed Identity Services) qui vont accéder aux instances de KeyVault et donc respecter le principe du moindre privilège

Quand on sait que toutes les API hébergées dans une même instance Azure Function partagent la même identité Managed Identity Service et la même méthode d’authentification, il apparait donc nécessaire d’introduire deux instances du service Azure Function pour héberger nos API.

  • Resourcegroupasaservicepublicapi
  • Resourcegroupasaserviceinternalapi

Voilà pour l’introduction. Dans le prochain billet, on va rentrer dans le dur du sujet.

 

BenoîtS – Simple and Secure by Design but Business compliant