OCTO 19

Container - Héberger vos sites Web

Depuis un moment, l’utilisation des containers est de plus en plus fréquente, mais attention ce n’est pas parce que c’est à la mode qu’il faut les utiliser à tort et à travers.

La plateforme Azure n’est pas indifférente aux containers, et bien heureusement. Il existe plein de manières de mettre en place des containers dans Azure, nous allons donc voir comment les mettre en place et pourquoi utiliser tel ou tel service sur Azure.

Pour commencer, on va prendre le cas d’un container ou d’un ensemble de containers ayant pour but d’afficher un pur site web, car effectivement l’utilisation des containers n’est pas uniquement exclusive aux architectures microservices.

Prenons en premier exemple un site très simple basé sur un container ayant ce docker file :

FROM nginx:alpine
COPY . /usr/share/nginx/html

Bon comme on peut le voir c’est plus dur de faire compliquer. Mais l’image m’offre l’avantage de ne pas avoir à mettre moi-même en place mon NGINX.

Maintenant si je veux mettre en place ce site Web sur Azure, j’ai plein de possibilités comme celle de créer un cluster AKS ou un cluster Service Fabric, mais vous serez d’accord avec moi sur le fait que ça serait sortir le marteau pour tuer une mouche.

Sur le service de Web Apps d’Azure il est possible d’héberger un container qui se situe dans n’importe quel registry accessible depuis ma Web App.

Pour ma part, je vais créer une registry sur Azure via le Service Azure Container Registry

$registry = New-AzureRmContainerRegistry -ResourceGroupName $rgName -Name $acrName -EnableAdminUser -Sku Basic

Puis j’enregitre ces accès dans mon Docker afin de pouvoir publier mes images dessus.

$credentials = Get-AzureRmContainerRegistryCredential -Registry $registry
docker login $registry.LoginServer -u $credentials.Username -p $credentials.Password

Pour ceux qui n’ont jamais utilisé Docker, et pour les autres un rappel ne faisant jamais de mal, voici comment construire notre image et l’envoyer sur une registry distante

docker build -t sample-web-app .\WebApp\
docker tag sample-web-app ($registry.LoginServer + '/sample-web-app:v1')
docker push ($registry.LoginServer + '/sample-web-app:v1')

Maintenant que notre image est sur notre registry Azure, il est possible de la déployer n’importe où que ce soit dans Azure ou ailleurs.

Je vais donc créer une nouvelle Web App sur Azure via le portail, il est bien entendu possible de faire la même chose via un template ARM ou des scripts.

image

Maintenant que mon container est hébergé, il est automatiquement configuré pour être mis à jour à chaque fois que je pousse une nouvelle version de mon image sur ma registry, il est bien entendu possible de désactiver cette fonctionnalité si besoin.

Il est aussi possible d’utiliser des images plus complexes via l’outil docker compose afin d’avoir de multiples containers qui sont exécutés au sein de votre Web App. Voici un exemple de configuration yml que vous pouvez utiliser, c’est celle qui est disponible sur la documentation Azure :

version: '3.3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
volumes:
    db_data:

Comme vous pouvez le voir, il s’agit ici d’un site Wordpress avec sa base de données. Il n’est par ailleurs actuellement pas possible d’utiliser toutes les fonctionnalités de Docker compose au sein de vos Web Apps, du coup vous pouvez oublier ce service si vous voulez vous servir des éléments suivants :

  • build : Non autorisé
  • depends_on : ignoré
  • networks : ignoré
  • secrets : ignoré

Selon moi le support des containers pour les Web App offre une solution idéale pour vos containers légers qui doivent tourner H24, et pour lesquels vous ne voulez pas à avoir à gérer un cluster sous jacent.


OCTO 12

Générer un token Azure AD via les REST API

Dernièrement j’ai eu le besoin d’accéder à un compte de stockage via un compte applicatif, j’ai donc mis en plac un SPN avec un droit RBAC sur mon compte de stockage, comme je le montre dans cet article : http://blog.woivre.fr/blog/2018/09/connectez-vous-a-vos-comptes-de-stockage-via-azure-active-directory

Maintenant il faut que je génère mon token d’accès, chose qui est assez simple avec les différentes librairies ADAL, cependant pour mon besoin j’avais les contraintes suivantes :

  • Compte applicatif avec une authentification par certificat
  • Pas de librairies supplémentaires (adieu ADAL)
  • Powershell

Première étape, il faut générer un token JWT. Pour rappel un token JWT répond à la structure suivante : base64(header).base64(payload).base64(signature)

Commençons par la construction de notre header, pour cela il nous faut le hash de notre certificat, que l’on peut récupérer de la manière suivante :

$cert = Get-Item Cert:\CurrentUser\My\$ThumbprintValue

$hash = $cert.GetCertHash()
$hashValue = [System.Convert]::ToBase64String($hash)  -replace '\+','-' -replace '/','_' -replace '='

Il est maintenant possible de constuire notre header de la manière suivante, ainsi que notre payload :

[hashtable]$header = @{alg = 'RS256'; typ= "JWT"; x5t = $thumprintValue}
[hashtable]$payload = @{aud = "https://login.microsoftonline.com/$TenantUrl/oauth2/token"; iss = $applicationId; sub=$applicationId; jti = "22b3bb26-e046-42df-9c96-65dbd72c1c81"; exp = $exp; nbf= 1536160449}

Maintenant qu’on a toutes les informations, il faut générer notre signature, et construire notre token

$headerjson = $header | ConvertTo-Json -Compress
$payloadjson = $payload | ConvertTo-Json -Compress

$headerjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerjson)) -replace '\+','-' -replace '/','_' -replace '='
$payloadjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($payloadjson)) -replace '\+','-' -replace '/','_' -replace '='

$jwt = $headerjsonbase64 + "." + $payloadjsonbase64
$toSign = [System.Text.Encoding]::UTF8.GetBytes($jwt)

$Signature = [Convert]::ToBase64String($rsa.SignData($toSign,[Security.Cryptography.HashAlgorithmName]::SHA256,[Security.Cryptography.RSASignaturePadding]::Pkcs1)) -replace '\+','-' -replace '/','_' -replace '='

$token = "$headerjsonbase64.$payloadjsonbase64.$Signature"

A noter qu’il est possible de valider la création de votre jeton JWT sur des sites comme celui-ci : https://jwt.io/

Et voilà nous avons notre token JWT qui nous servira à avoir notre access Token qu’on va pouvoir récupérer de la manière suivante:


$url = "https://login.microsoftonline.com/$TenantUrl/oauth2/token"
$body = "resource=https%3A%2F%2F$storageAccountName.blob.core.windows.net%2F&client_id=$applicationId&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=$token&grant_type=client_credentials"
$responseToken = Invoke-WebRequest -Method POST -ContentType "application/x-www-form-urlencoded"  -Headers @{"accept"="application/json"} -Body $body $url -Verbose

$accessToken = ($responseToken.Content | ConvertFrom-Json).access_token

Après avoir générer notre token, il est possible de l’utiliser dans nos headers pour appeler les REST API de notre storage.

$headerSMA =  @{"Authorization" = "Bearer " + $accessToken; "x-ms-version" = "2017-11-09"}
Invoke-WebRequest -Headers $headerSMA -Method GET "https://$storageAccountName.blob.core.windows.net/$containerName/$blobName"  -OutFile $outFile

Et voilà comment appeler des API Azure tout en s’affranchissant d’ADAL. Même si on est d’accord créer notre Token avec ADAL c’est beaucoup plus simple. Et surtout moins long à lire.


OCTO 05

Créer vos groupes de ressources via un template ARM

La création des groupes de ressources se fait généralement par le portail Azure, ou via votre terminal préféré (CLI ou PowerShell). Il est posssible dorénavant de créer vos groupes de ressources via un template ARM.

Prenons un exemple de template ARM pour créer notre resource group :

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {},
    "variables": {
        "location": "West Europe",
        "name": "rg-test"
    },
    "resources": [
        {
            "type": "Microsoft.Resources/resourceGroups",
            "apiVersion": "2018-05-01",
            "location": "[variables('location')]",
            "name": "[variables('name')]",
            "properties": {}
        }
    ],
    "outputs": {}
}

Maintenant pour déployer ce template ARM, il est possible d’utiliser la commande suivante :

New-AzureRmResourceGroupDeployment -Name deploy-rg -TemplateFile .\azuredeploy.json -ResourceGroupName existing-rg

Le problème c’est qu’il faut déjà avoir un resource group dans notre souscription, ce qui n’est pas top pour initier une souscription. Mais depuis peu, il est possible d’utiliser la commande suivante :

New-AzureRmDeployment -Name deploy-rg -TemplateFile .\azuredeploy.json

Il est bien entendu possible de retrouver les déploiements passés via la commande

Get-AzureRmDeployment

Ou alors vous pouvez retrouver vos déploiements dans la blade Souscription dans le portail Azure.

Happy deploy !


SEPT 25

Connectez vous à vos comptes de Stockage via Azure Active Directory

On a vu dans un précédent article comment utiliser le KeyVault pour générer des SAS Keys : http://blog.woivre.fr/blog/2018/09/generer-des-cles-sas-pour-vos-storage-grace-a-keyvault

Dans la même philosophie que ce dernier, il est possible de s’affranchir totalement des clés de Storage dans vos configs ou votre code source, même si j’espère que pour ce dernier c’est déjà le cas.

Certains services sur Azure supportent des rôles RBAC sur les données, comme notamment les Storage Accounts, qui contiennent des droits Reader ou Contributor sur les Blob et les Queues.

Grâce à cela il est possible pour un compte AD spécifique de se connecter à mon Stockage Azure pour récupérer un fichier par exemple.

Les noms des rôles build-in qui existent autour de la donnée sont les suivants :

  • Storage Blob Data Contributor (Preview)
  • Storage Blob Data Reader (Preview)
  • Storage Queue Data Contributor (Preview)
  • Storage Queue Data Reader (Preview)

A noter, qu’il est possible de setup ces différentes permissions via la propriété DataAction dans la définition de vos rôles RBAC

Vu que pour le moment le SDK C# pour le Storage ne supporte pas cette nouvelle fonctionnalité il faut le faire via les API REST Azure, comme ci-dessous :

AuthenticationContext authContext = new AuthenticationContext($"https://login.microsoftonline.com/{TenantId}");
AuthenticationResult authResult = await authContext.AcquireTokenAsync($"https://{StorageAccountName}.blob.core.windows.net/", new ClientCredential(ApplicationId, SecretKey));
	
HttpClient client = new HttpClient(); 
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + authResult.AccessToken);
client.DefaultRequestHeaders.Add("x-ms-version", "2017-11-09");

var response = await client.GetStringAsync($"https://{StorageAccountName}.blob.core.windows.net/{ContainerName}/{BlobName}");	

A noter qu’ici, je génère un token grâce à ADAL et que je demande bien celui-ci pour mon compte de stockage.

Avec cet article, vous avez encore moins d’excuses de conserver vos clés de stockage dans vos configs.


SEPT 21

Générer des clés SAS pour vos storage grâce à KeyVault

En entreprise, la sécurité c’est le nerf de la guerre. Il n’est pas envisagable de concevoir une application sans aucune notion de sécurité, que cette application soit hébergée on-premise ou dans le Cloud (Azure de préférence).

Quand vous utilisez des comptes de stockage Azure pour du Blob, des Tables ou des Queues, il est possible que vous souhaitiez ne pas avoir vos clés de stockage dans votre solution ou dans la configuration disponible sur vos serveurs pour des questions de Leakage.

Il est possible de mettre en place plusieurs solutions pour ne pas avoir de clés dans vos configurations :

  • Utiliser un SPN qui aura pour rôle de se connecter à Azure pour récupérer la clé de votre stockage et si besoin générer une SAS Key
  • Mettre en place un SPN qui aura des accès via l’AD sur votre compte de stockage (cela sera traité dans un prochain article)
  • Utiliser un SPN qui aura accès à un KeyVault contenant vos clés de storage
  • Utiliser le KeyVault pour générer des SAS Key.

Cette dernière solution est en preview dans Azure, nous allons voir comment la mettre en place.

La première étape coniste à avoir les droits nécessaires sur le KeyVault pour effectuer l’opération d’ajout, et à attribuer les bons droits aux personnes ou au SPN qui l’utiliseront. A ce jour, il n’est pas possible de faire cette opération via le portail, donc voici un exemple en Powershell :

Set-AzureRmKeyVaultAccessPolicy -VaultName $keyVaultName -ObjectId $userPrincipalId -PermissionsToStorage get,list,delete,set,update,regeneratekey,getsas,listsas,deletesas,setsas,recover,backup,restore,purge

La deuxième étape consiste à associer votre KeyVault à un compte de stockage. Il est possible de faire cela en PowerShell via la commande suivante :

$storage = Get-AzureRMStorageAccount -StorageAccountName $storageAccountName -ResourceGroupName $storageAccountResourgeGroup

Add-AzureKeyVaultManagedStorageAccount -VaultName $keyvaultName -AccountName $storageAccountName -AccountResourceId $storage.Id -ActiveKeyName key2 -DisableAutoRegenerateKey

A noter que sur cette dernière étape il est possible d’activer la régénération des clés de manière automatique sur vos storages.

La dernière étape consiste à construire une SAS Key qui vous servira de template pour la génération des suivantes. Ce template sera par la suite utilisé pour récupérer une nouvelle clé. Voici comment faire cela en powershell :

$sasTokenName = "fullaccess"
$storageKey = (Get-AzureRmStorageAccountKey -ResourceGroupName $storageAccountResourgeGroup -Name $storageAccountName).Value[0]

$context = New-AzureStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageKey -Protocol Https
$start = [System.DateTime]::Now.AddMinutes(-15)
$end = [System.DateTime]::Now.AddMinutes(15)
$token = New-AzureStorageAccountSASToken -Service Blob,File,Table,Queue -ResourceType Service,Container,Object -Permission "racwdlup" -Protocol HttpsOnly -StartTime $start -ExpiryTime $end -Context $context

$validityPeriod = $validityPeriod = [System.Timespan]::FromMinutes(30)
Set-AzureKeyVaultManagedStorageSasDefinition -VaultName $keyVaultName -AccountName $storageAccountName -Name $sasTokenName -ValidityPeriod $validityPeriod -SasType 'account' -TemplateUri $token

Il est important d’utiliser un “sasTokenName” explicite et unique pour chacune de vos SAS Key, car c’est celui-là qui sera utilisé par la suite.

Maintenant pour récupérer ma clé SAS, il me suffit d’appeler le KeyVault avec le secret suivant : StorageAccountName-sasTokenName, comme on peut le voir ci-dessous en Powershell :

$sasKey = (Get-AzureKeyVaultSecret -VaultName $keyvaultName -Name ("wwodemospn-fullaccess")).SecretValueText

Avec cette méthode, fini les clés de Storage qui trainent dans les fichiers de config.