How to securely access cloud resources from Linux VMs on Azure
For detailed documentation of Azure topics covered in this article, take a look at Managed Identities documentation, Azure Linux Virtual Machines, and Azure CLI. To follow through examples, get started with Free Trial.
In this article we are discussing Azure Managed Identities! Why is it a good idea to use them? How can they help make access to Azure resources more secure and less error-prone?
TL;DR: They can significantly improve authentication process between cloud resources, reduce operational overhead and vulnerability of managing and storing authentication credentials.
Note: Managed Identities in Azure are similar to IAM roles for EC2 instances on AWS.
Imagine there is an application running on a Linux Virtual machine on Azure. From your application, you’d like to have access to other cloud resources. It could be a file stored on Azure Storage, or a secret in Key Vault, or Event Hubs data source. Traditionally we would need to stote and manage credentials or access keys in application configuration files, make sure we rotate the keys when necessary and keep them secure, which is a challenge. We can store credentials in Azure Key Vault, but applications still need to authenticate to Key Vault to retrieve them.
Managed Identities feature helps authenticate to any of the supported services without needing to store or manage any credentials or access keys in application configuration or code. You can view Azure services that support Managed Identities here. That page also contains Resource ID URLs necessary requesting access tokens.
How does it work?
There are two main steps: requesting access token, and accessing the service providing the access token.
With Managed Identities the application that runs on a VM will ask for an access token by performing a request to Azure Instance Metadata Service identity endpoint. The request will need to indicate which service the token is needed for (e.g. Azure Storage, or Key Vault).
After application receives the access token, it will need to include it in the call to the specific service to be able to access it successfully.
System-assigned and User-assigned identities
There are two options for Managed Identities: system-assigned and user-assigned.
System-assigned managed identity can be enabled directly on Azure resource (e.g. your Linux VM). After it is enabled, credentials are set up for that instance, and the lifecycle of this identity is tied to the resource it’s dedicated to, meaning that when the resource is deleted — identity is deleted as well.
User-assigned managed identity is on the contrary a standalone Azure resource that isn’t dependent on a particular instance and is managed separately. Therefore, it can be assigned to many resources at the same time. This is convenient when you have a fleet of resources that need same type of access to some Azure service or services.
Some resources can have both system and user assigned identity.
How exactly does it all work?
Let’s take a look at a concrete example. Imagine you’re running a Linux VM on Azure. On this VM you’d like to run applications that access to a particular stotage account.
Preparing resource groups
We will need two resource groups — to host storage, and a VM. In further examples we might want to create a separate resource group for user-assigned identities.
export vm_name=identityvm$RANDOM export ssh_user=lena export storage_group=identity-storage export storage_account=identitystorage$RANDOM az group create -n $vm_group -l westus az group create -n $storage_group -l westus
Creating a Linux VM
To check whether it’s possible to access Azure Storage Account through a Managed Identity we will need a VM. The code below creates an Ubuntu Linux VM. Make sure to note its IP address to connect to it in future steps.
az vm create \ --resource-group $vm_group \ --name $vm_name \ --image UbuntuLTS \ --admin-username $ssh_user \ --generate-ssh-keys # Alternatively, use this command to specify specific size of VM # az vm create --resource-group $vm_group --name $vm_name --image UbuntuLTS --admin-username $ssh_user --generate-ssh-keys --size Standard_D3_v2 # Take note of public IP address of the VM !!! az vm open-port --port 80 --resource-group $vm_group --name $vm_name
Creating a Storage Account
We will also need a storage account, a container within it, and some file we will use as a target resource.
az storage account create \ --resource-group $storage_group \ --name $storage_account \ --sku Standard_LRS# Take note of storage account Resource ID! export AZURE_STORAGE_CONNECTION_STRING=`az storage account show-connection-string --resource-group $storage_group --name $storage_account -o tsv`az storage container create --name test echo "hello" >> text.txt az storage blob upload \ --container-name test \ --file text.txt \ --name text.txt
SSH to the VM and access the file
If we now try to SSH to the Linux VM and try to access the “text.txt” file we should get an error, because we haven’t yet enabled Managed Identity for the VM.
# Use VM public IP instead of <public-ip> placeholder to SSH to the VMssh $ssh_user@<public-ip> # Within the VM, run the following commandssudo apt-get -y update # Note: resource parameter is set to "https://storage.azure.com/" (but escaped) # If you need to use Managed Identities for other services, get Resource URLs from here: https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/services-support-msi# Copy access token received from this command:curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fstorage.azure.com%2F' -H Metadata:true # Use name of the storage account instead of <STORAGE ACCOUNT># In place of <ACCESS TOKEN> insert the access token you received from the previous step curl https://<STORAGE ACCOUNT>.blob.core.windows.net/test/text.txt -H "x-ms-version: 2017-11-09" -H "Authorization: Bearer <ACCESS TOKEN>" # It should display "hello" if managed identity with the right role is enabled - content of text.txt file! # It will display the following when managed identity is disabled:# <?xml version="1.0" encoding="utf-8"?><Error><Code>AuthorizationPermissionMismatch</Code><Message>This request is not authorized to perform this operation using this permission.
Assign a System Managed Identity
Because in this case we have a single VM, there’s no need for user-assigned identity. Here, we’re assigning a system-managed identity with a role and a scope to the VM. Make sure to put the correct storage account Resource ID as a scope. Feel free to explore what other roles are supported by resources you need access to.
# Replace <resource-id-of-storage-account> with the actual valueaz vm identity assign \ --resource-group $vm_group \ --name $vm_name --role "Storage Blob Data Reader (Preview)" \ --scope <resource-id-of-storage-account>
Try to access the file again!
The output should now display “hello” this time.
Now the VM is set up to allow any of the applications running on it access Azure Storage with scope and role granted to the managed identity.
Clean up your resources
az group delete --name $vm_group az group delete --name $storage_group
Thank you for reading!
I hope this was useful, and if so — mention me at @lenadroid on Twitter — I’ll be happy to hear your thoughts! In the next parts we will learn about managed identities with Azure Kubernetes Service and Azure Functions!
Originally published at lenadroid.github.io.