9 min read

25 Days of Serverless (Day 22): Winter Solstice - Protect Secrets from Grim Reaper!

Justin Yoo

This article is part of #25DaysOfServerless. New challenges will be published every day from Microsoft Cloud Advocates throughout the month of December. Find out more about how Microsoft Azure enables your Serverless functions.

Have an idea or a solution? Share your thoughts on [Twitter!](https://twitter.com/intent/tweet?text=I'm joining the @azureadvocates %2325DaysOfServerless challenge!! Learn more at https://aka.ms/25daysofserverless or see solutions at https://dev.to/search?q=25DaysOfServerless! Join me!)


Challenge

Welcome to Korea in this festive season! It's Winter Solstice today. Traditionally in Korea, it's the day that many grim reapers are seeking for young kids to take their souls. But they won't be able to find out our children who eat red-bean porridge before going to sleep.

porridge

Oh no! Cheol-soo missed the porridge tonight. He's in danger to get caught by the grim reaper! We need to keep him in a safe place and lock the door until the next day; the Sun is rising. His best friend Young-hee locked the door and got the secret code to open it.

memo

Then she managed to store the secret into Azure Key Vault, but how smart the grim reaper is!! The grim reaper is trying to destroy the Key Vault so that Cheol-soo can't get out of the safe place forever! If Young-hee can't find out how to backup and restore the Key Vault, he will die in there!

She needs to back it up before the grim reaper destroys it. And she also needs to restore it even if the grim reaper demolishes the Key Vault. How can we help Young-hee backup and restore Key Vault secret?

Prerequisites

Azure Account

Do you have an Azure account yet? Let's create the one free of charge. As this free account is offered with USD 200, it would be more than enough to complete this challenge.

Azure CLI

Azure CLI as a cross-platform tool helps manage Azure resources in a console terminal. Use this link to install Azure CLI.

Sample Solution Code

The sample solution used in this post can be found at this GitHub repository.

Resource Provisioning

In order to solve this challenge, first of all, you need to provision resources on Azure. Click the link below for provisioning.

Deploy to Azure

If you prefer to using Azure CLI, run the following command.

az group create \
-n <RESOURCE_GROUP_NAME> \
-l <RESOURCE_GROUP_LOCATION>
az group deployment create \
-n 25dos-challenge-22 \
-g <RESOURCE_GROUP_NAME> \
--template-file azuredeploy.json \
--parameters @azuredeploy.parameters.json \
--verbose

Once completed, you can find out all the resources correctly provisioned on Azure Portal.

resource provisioning result

Azure Functions Managed Identity

In order to directly access Azure Key Vault from Azure Functions without performing explicit authentication/authorisation, check whether the Managed Identity feature is activated or not.

managed identity

Azure Key Vault Access Policy

In order to manage secrets directly on the portal, with my account, the Access Policy needs to be updated. Go into the Access policies blade and click the + Add access policy link.

access poliocy blade

Choose Select all for Secret permissions.

secrets

Enter your account name for Select principal.

user

Then click the Add button followed by the Save button on the screen. Now the Azure Key Vault instance allows my account for access.

Secrets to Azure Key Vault

When Young-hee locked the door to hide Cheol-soo, she received a secret code.

DoYouKnow,soN,Bts,&pSy?

We need to store it to the Azure Key Vault. Click the Secrets blade and the + Generate/Import button.

create secret

Let's put cheolsoo into the Name field and the secret code into the Value field. Click the Create button at the bottom. Now, we've stored the secret into Azure Key Vault.

secret created

Azure Function for Azure Key Vault Secret Backup

Here's the workflow to backup secrets from Azure Key Vault.

  1. Get the list of secrets
  2. Iterate the list and backup each secret individually
  3. Create an array containing all the backup result
  4. Serialise the array and upload it to Azure Blob Storage

Let's create the first workflow item – get the list of secrets. This method returns the list of secret names.

public async Task<List<string>> GetSecretsAsync()
{
// Declares a KeyVaultClient instance.
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var kv = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
// Gets the list of secrets.
var baseUri = "https://<my-keyvault-backup>.vault.azure.net/";
var secrets = await kv.GetSecretsAsync(baseUri)
.ConfigureAwait(false);
// Returns the list of secret names.
return secrets.Select(p => p.Identifier.Name).ToList();
}
view raw secrets-get.cs hosted with ❤ by GitHub

This method loops the list of secret names and backup each secret individually. At the time of this writing, as there is no bulk backup feature offered yet, we should run the loop. Once every secret is backed up, the method returns a list of backup results.

public async Task<List<BackupSecretResult>> BackupSecretsAsync(List<string> secrets)
{
// Declares a KeyVaultClient instance.
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var kv = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
// Performs the backup and add the result into the list.
var results = new List<BackupSecretResult>();
var baseUri = "https://<my-keyvault-backup>.vault.azure.net/";
foreach (var name in secrets)
{
var result = await kv.BackupSecretAsync(baseUri, name)
.ConfigureAwait(false);
results.Add(result);
}
// Returns the backup results.
return results;
}
view raw secrets-backup.cs hosted with ❤ by GitHub

This method serialises the backup results and uploads it to Azure Blob Storage. Spot on the backup filename of this format, <yyyyMMdd>.json.

public async Task<bool> UploadAsync(List<BackupSecretResult> results)
{
// Declares the BlobClient instance.
var connectionString = Environment.GetEnvironmentVariable("AzureWebJobsStorage");
var blob = CloudStorageAccount.Parse(connectionString)
.CreateCloudBlobClient();
// Gets the Blob container.
var containerName = "backups";
var container = blob.GetContainerReference(containerName);
await container.CreateIfNotExistsAsync().ConfigureAwait(false);
// Gets the Blob.
var blobName = $"{DateTimeOffset.UtcNow.ToString("yyyyMMdd")}.json";
var blob = container.GetBlockBlobReference(blobName);
// Serialises the backup result.
var serialised = JsonConvert.SerializeObject(results);
// Uploads the backup result to Blob Storage.
await blob.UploadTextAsync(serialised).ConfigureAwait(false);
// Returns true, if everything is OK.
return true;
}
view raw blob-upload.cs hosted with ❤ by GitHub

Now, we've got all the workflow features. Let's create an HTTP trigger function and put the workflow inside.

[FunctionName(nameof(BackupSecrets))]
public async Task<IActionResult> BackupSecrets(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "secrets/backup")] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
// Gets the list of secrets.
var secrets = await this.GetSecretsAsync().ConfigureAwait(false);
// Performs the backup.
var results = await this.BackupSecretsAsync(secrets).ConfigureAwait(false);
// Uploads the backup data.
var uploaded = await this.UploadAsync(results).ConfigureAwait(false);
return new OkObjectResult(results);
}
view raw trigger-backup.cs hosted with ❤ by GitHub

We're ready to run the HTTP trigger. Let's run this on our local development environment. In order to use the Managed Identity feature on the local, we need to, first of all, login to Azure via Azure CLI. Once logged in, run the debugging mode on Visual Studio Code.

debug backup

Use Postman to call the endpoint, and it will show the result.

postman backup

And this result can also be found on Azure Blob Storage.

storage explorer backup

We managed to help Young-hee backup Azure Key Vault before the grim reaper destroys it. Time is running out! Let's move on! The grim reaper is storming in!

Azure Function for Azure Key Vault Secret Restore

Here's the workflow to backup secrets to Azure Key Vault.

  1. Get the timestamp of the backup
  2. Download backup from Azure Blob Storage with the timestamp
  3. Deserialise the backup
  4. Restore the backup to a new Azure Key Vault instance

We know the timestamp format, yyyyMMdd. It is passed through the URL of the HTTP trigger. This method downloads the backup with the timestamp and deserialises it to a list, and return the list.

public async Task<List<BackupSecretResult>> DownloadAsync(string timestamp)
{
// Declares the BlobClient instance.
var connectionString = Environment.GetEnvironmentVariable("AzureWebJobsStorage");
var client = CloudStorageAccount.Parse(connectionString)
.CreateCloudBlobClient();
// Gets the Blob container.
var containerName = "backups";
var container = client.GetContainerReference(containerName);
// Gets the Blob.
var blobName = $"{timestamp}.json";
var blob = container.GetBlockBlobReference(blobName);
// Downloads the Blob content.
var downloaded = await blob.DownloadTextAsync().ConfigureAwait(false);
// Deserialises the contents.
var results = JsonConvert.DeserializeObject<List<BackupSecretResult>>(downloaded);
// Returns the result.
return results;
}
view raw blob-download.cs hosted with ❤ by GitHub

This method iterates the list of backup secrets, and each backup secret is restored individually within the loop. Similar to the backup, there is no bulk restore feature supported, at this time of the writing.

public async Task<List<string>> RestoreSecretsAsync(List<BackupSecretResult> secrets)
{
// Declares a KeyVaultClient instance.
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var kv = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
// Performs the restore and add the result into the list.
var results = new List<SecretBundle>();
var baseUri = "https://<my-keyvault-restore>.vault.azure.net/";
foreach (var secret in secrets)
{
var result = await kv.RestoreSecretAsync(baseUri, secret.Value)
.ConfigureAwait(false);
results.Add(result);
}
// Returns the list of secret names.
return results.Select(p => p.SecretIdentifier.Name).ToList();
}
view raw secrets-restore.cs hosted with ❤ by GitHub

Now, we got the whole workflow for restore. Let's create the HTTP trigger to run them all.

[FunctionName(nameof(RestoreSecrets))]
public async Task<IActionResult> RestoreSecrets(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "secrets/restore/{timestamp}")] HttpRequest req,
string timestamp,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
// Downloads the backup of the given timestamp.
var secrets = await this._blob.DownloadAsync(timestamp).ConfigureAwait(false);
// Performs the restore from the backup.
var results = await this._secret.RestoreSecretsAsync(secrets).ConfigureAwait(false);
return new OkObjectResult(results);
}
view raw trigger-restore.cs hosted with ❤ by GitHub

We have the HTTP trigger for restore. Let's run it locally. With the debug mode of Visual Studio Code, calling the endpoint through Postman shows the result.

postman restore

The new Azure Key Vault instance show the restored result.

keyvault restore

Phew! We got this! Young-hee has now been able to backup and restore Azure Key Vault. The grim reaper can no more take Cheol-soo, nor scrap the Key Vault. Soon the Sun is rising, the grim reaper must leave, and Young-hee can now go to sleep. So can you. We will see you tomorrow with another challenge!


Want to submit your solution to this challenge? Build a solution locally and then submit an issue. If your solution doesn't involve code, you can record a short video and submit it as a link in the issue description. Make sure to tell us which challenge the solution is for. We're excited to see what you build! Do you have comments or questions? Add them to the comments area below.


Watch for surprises all during December as we celebrate 25 Days of Serverless. Stay tuned here on dev.to as we feature challenges and solutions! Sign up for a free account on Azure to get ready for the challenges!