loading...

Low code, ultimate security - Secure Azure with Managed Identities

urmade profile image Tobias Urban ・7 min read

Are your apps really secure?

TL;DR: Azure Managed Identities are awesome! Give your services an Identity, configure permissions between resources and let Azure handle all access management for you. How to implement this? Here you go.

Can you remember your last side project? For me, it is always a time of hype. I am a cloud enthusiast, so I always have plenty of stuff in my mind on what to do. Let's go with Kubernetes, but Serverless seems pretty cool too, and didn't they blog lately about that new feature in Web Apps that sounded fun? Maybe I just bring in all of them?
And while building a concept and coding the logic of your side project is a real joy, there comes this point where I always start wondering: "Wait, maybe that thing I built could be cool for others as well. Okay, then I definetly have to spend more time on the UI. And I have to make it more secure. I have to make all of it more secure. Why did I choose to bring in all these components?"

Securing the interaction between different resources in a product can be time-consuming. You have to connect all resources by hand, keep secrets up to date in your environment variables, and usually build dependencies in your code to some sort of security service or library (for the Azure context, let's take Key Vault for storing your secrets and certificates as an example). You start investing a lot of time in your security assets, and that's time you could spend on value-bringing features as well.

But luckily, Azure is here to help: With Managed Identities, you can assign an Azure Active Directory Identity to many of your Azure services and give them permissions to other resources, just as you would do with users. That way, you don't have to care about access management, and Azure routes and resolves all access requests internally for you.

What are these managed identities really?

If you have never worked with Azure Active Directory (called AAD in short) before, here's the summary: AAD is Microsofts Identity Management tool. That means, every user, all of his permissions and a lot of other objects in your organizations are listed there and with every sign-in to some resource. AAD knows exactly what you can or cannot do. Nearly all of Microsofts first-party as well as a lot of third-party tools use AAD for their identity management. For example, you can thank AAD for keeping your Office secure, and making sure only you see your OneDrive files.

And exactly this Azure Active Directory can be used to regulate service communication as well: Just give your service an identity (let's call it Alex) and specify all other resources that Alex can talk to.

Okay, how can Alex do that?

Bringing it to life: Assigning an identity

When we gave our service its identity, we created two things in AAD: An Application registration and a service principal. An application registration basically just tells AAD: "Hey, here is some service that wants to call some of your APIs" (This isn't the most accurate or wholesome description ever, but it serves my cause. If you want to go deeper, start here). With that application registration also comes a service principal object. This is a modified version of a basic user in AAD (like Alice and Bob), with the restriction that it only can talk to other Azure resources. This service principal is attached to our application registration, and it is linked to its assigned managed identity. If the managed identity is deleted, the linked service principal is automatically removed as well.

Assigning an identity can be done purely from the Azure web app. As we see in the next chapter, there are two different kinds of identities, but the process is very similar: Navigate to the resource you want to assign an identity to, choose the Identity tab in the resource navigation, and click on Status: On or Add (depending on which kind of identity you want to assign). The whole process is very well documented in this article.

Who am I? User-assigned vs. System-assigned identities

When it comes to managed identities, we can choose from two different creation methods.

System-assigned identities are related directly to one specific Azure resource. You can deploy it via two clicks in all Azure resources that support managed identities. If the resource is deleted, the identity is removed as well.

User-assigned identities on the other hand can be connected to multiple Azure resources. They are created like every other Azure resource and can be used to group multiple resources together to have identical rights and permissions. When of these resources are deleted, the user-assigned identity continues to exist.

Making it talk: Requesting access to other resources

At first, you have to configure which resources our newly created identity (called Alex like you might remember) has access to. If you ever assigned access rights to your co-workers on a specific Azure resource, this is a piece of cake for you. You just do the following:

  • Navigate to the resource that you want Alex to access
  • Click on "Access Control (IAM)"
  • Click on "Add", and then "Role assignment"
  • Give it whatever role is suitable for your plans, and enter Alex' service principal ID under "Select"
  • Hit save

And just like that you are all set! For screenshots and a more elaborate description of the process, go through this documentation.

Now we can go to phase two: Giving Alex an access token to actually call other services. Depending on what kind of Azure service Alex is, we have different ways to achieve this (and this article would blow up if we would cover all of them). But to clarify the concept, let's look at the flow for Virtual Machines.

Coming to life: Implementing Managed Identity calls on Virtual Machines

For those of you who are already familiar to OAuth2 login flows, this flow should be somewhat familiar (but with far less security measures included): After we set up our Service Principal from the Azure portal, we call a certain URL and get a JSON Web Token back with which we can call other services. In detail:

From your VM, call http://169.254.169.254/metadata/identity/oauth2/token. Provide these query parameters:

  • api-version: Specifies the internal API version to use for token acquiry. Must be 2018-02-01 or higher.
  • resource: The API that you want to call with the token acquired. Could be for example https://management.azure.com/ if you want to call the Azure API, or https://vault.azure.net/ for access to Key Vault.
  • (Optional) client_id: If you're using user-assigned identities, you must include this parameter with the client ID of your service principal.

Why don't we use any secrets or any other verification to get a token? Well, we are already on the Virtual Machine when we get the token. Therefore you can see this call as machine internal, same like if we would access the file system.

After you successfully requested your token, you will get back a JSON Web Token. This token can be used as an authorization to call other services (specified by resource). And now you're done! Just one single call from your server for all Azure services, instead of implementing huge and service-specific authorization SDKs!

To learn more about other resources and how to obtain an access token, check out this documentation.

Native security: Key Vault references

This is already pretty cool. But it get's even better. A common scenario for service-to-service communication in Azure is the request of secrets. You wouldn't want to store app passwords or certificates in plain text on your server, and leaving them hardcoded as an environment variable is still a huge administrative overhead (and all your App developers can see the secrets). So it is a good idea to outsource all secrets somewhere, and Azure Key Vault is exactly that place. It stores secrets and certificates encrypted by a physical TPM module (if you never heard about that, don't worry: It is just as secure as it sounds), with strong auditing and features like auto-rotation.

The only drawback: How can we access these secrets if they are not on our server? Ironically, we can use REST: Just call an URL, authorize with a secret and get your other secret back. Do you spot the loophole? We trade in all of our secrets for one master-secret, but in the end we still have to manage a secret.

For Azure App Services and through Managed Identities we have a very elegant way of fixing this. Just configure a Managed Identity for your App Service, give that Identity access to your Key Vault and use a Key Vault reference to access your secrets. What is a Key Vault reference? In the environment variables of your App Service (in Azure, they are called App settings), instead of configuring SECRET:pq289gni..., you can put in a reference. This looks like this: SECRET:@Microsoft.KeyVault({secretUri}). With the right permissions in place (provided through Managed identities) your App Service now automatically pulls the secret from Key Vault at startup, and the secret is never exposed anywhere in the whole process.

I hope you are now equipped with the knowledge to build super-robust apps on Azure. If you have any questions/feedback or some information is still missing, please provide some feedback!

Discussion

pic
Editor guide