logo
Published on

Securing Access to Azure PaaS Services Using Managed Identities

security
Authors

One of the key aspects of cloud security is ensuring that only authorized entities have access to your resources. In this blog post, we will explore how to secure access to Azure Platform as a Service (PaaS) services using Managed Identities. We will use a case study of a serverless function with a blob trigger that uses Azure AI Vision to tag uploaded blob images and save the results in Cosmos DB. If you just want to jump into the code, check out the full working sample on GitHub.

What are Managed Identities?

Managed Identities are a feature of Azure Active Directory(aka Microsoft Entra ID) that provides a keyless and seamless way to authenticate on Azure cloud. A managed identity can be used to authenticate to any service that supports Azure AD authentication, without needing to store any credentials in your code.

Fig 1.1. Azure Managed Identity Authentication Overview

Fig 1.1. Azure Managed Identity Authentication Overview

In simple terms, managed identities offer an abstraction over Service Principals (aka service accounts). Internally there exists a Service Principal with a CLIENT_ID, CLIENT_SECRET, etc. which Azure manages on your behalf.

There are two types of managed identities available in Azure:

  1. System-assigned Managed Identity - This type of managed identity is bound to the resource it is assigned to. When the resource is deleted, the system-assigned managed identity is also deleted.
  2. User-assigned Managed Identity - This type of managed identity is a reusable managed identity that exists independently of the resource lifecycle. It can be assigned to one or more resources and can be deleted separately from the resources it is assigned to.

Managed Identities and PAAS

As businesses increasingly migrate to the cloud, leveraging Platform as a Service (PaaS) offerings for their flexibility and scalability, the importance of robust security mechanisms cannot be overstated. Azure PaaS simplifies application deployment and management but introduces unique security challenges, particularly in credential and access management.

Many Platform-as-a-Service (PaaS) offerings rely on client authentication via keys or connection strings which typically grant full access to anyone who possesses them. Although there are well-established best practices for handling these keys, as long as humans are involved there is still room for error such as mistakenly committing secret files to Git. The good news is that platforms like GitHub offer automated scanning tools to identify these mistakes early. In addition, Azure provides a multi-layered approach to mitigate this risk:

1. Managed Identities (RECOMMENDED)

  • Reduced Attack Surface: Eliminate the need for developer-managed keys, significantly shrinking the attack surface.
  • Simplified Management: Streamline access control by leveraging integrated Azure identities, removing the burden of manual key management.
  • Enhanced Security: Benefit from role-based access control (RBAC) built into Azure, ensuring granular control of permissions.

2. Azure Key Vault (for Customer Managed Keys or Third-Party Credentials)

  • Secure Storage: Safely store sensitive information like third-party API keys, connection strings, and certificates.
  • Controlled Access: Implement granular RBAC within Key Vault to manage access to stored secrets.
  • Managed Identity Integration: Combine the security of Key Vault with the convenience of managed identities for secure access without developer-managed keys.

3. Private Endpoints (Defence In Depth)

  • Limited Exposure: Restrict network access to PaaS services to your Azure Virtual Network using private endpoints.
  • Reduced Risk: Mitigate the impact of leaked credentials by limiting their usability outside your trusted network.

Key points

  • Choose managed identities for PaaS authentication whenever possible.
  • Use Key Vault for securely storing customer-managed or third-party credentials and leverage managed identities for read access inside your apps.
  • Consider enabling Private Endpoints for extra network protection when appropriate.

Case Study: AI Product Image Tagger

Let’s consider a serverless function that is triggered whenever a new image is uploaded to a blob storage. The function uses Azure AI Vision to analyze the image and tag it based on its content. The results are then saved in a Cosmos DB database.

Step 1: Setting up the Function

First, we create a serverless function with a blob trigger. The function is set up to be triggered whenever a new blob (in this case, an image) is uploaded to a specified container in a blob storage account.

@app.function_name(name="BlobTrigger1")
@app.blob_trigger(arg_name="myblob",
                  path="myblobcontainer/{name}",
                  connection="AzureWebJobsStorage")
def main(myblob: func.InputStream):
    logging.info(f"Python blob trigger function processed blob \n"
                 f"Name: {myblob.name}\n"
                 f"Name from binding: {myblob.name}\n"
                 f"Blob Size: {myblob.length} bytes")
    start = time.perf_counter()
    blob_content: bytes = myblob.read()

    # Authenticate with user managed identity to acess PAAS services
    credentials = DefaultAzureCredential(managed_identity_client_id=IDENTITY_CLIENT_ID) 
    
		# With the credentials obtained using the managed identity, we can now authenticated 
    # without using connection strings e.g. Here is how you would connect to CosmosDB
	  cosmos_client = CosmosClient(url=DB_ENDPOINT,
                                 credential=credentials)

	  # Similarly you can also connect to the Key Vault client with your identity
    keyvault_secrets_client = SecretClient(vault_url=endpoint, credential=credentials)
    
    # .... REST OF THE CODE [refer to the attached GitHub link]

Step 2: Integrating Azure AI Vision

Next, we integrate Azure AI Vision into the function. We use the Computer Vision API to analyze the image and retrieve tags for it.


def _get_tags_from_vision_api(image: bytes, api_key: str, endpoint: str) -> list:
    
    # first, we retrieve the API key from key vault secrets
    credentials = DefaultAzureCredential(managed_identity_client_id=IDENTITY_CLIENT_ID) 
    keyvault_secrets_client = SecretClient(vault_url=endpoint, credential=credentials)
    api_key = client.get_secret(secretName).value

    # Initialise Azure AI Vision client
    ai_vision_client = ImageAnalysisClient(endpoint=endpoint, credential=AzureKeyCredential(api_key))

		# Call the Azure AI Vision API to get the image tags
    image_analysis_res: ImageAnalysisResult = client._analyze_from_image_data(image_content=image, visual_features=[VisualFeatures.TAGS],  language="en")
    model = image_analysis_res.model_version
    has_tags = image_analysis_res.tags is not None and len(image_analysis_res.tags.list) > 0

    image_tags = [tag.name for tag in image_analysis_res.tags.list] if has_tags else []
    return image_tags

Note: You can find the IDENTITY_CLIENT_ID on the Azure Portal Overview screen for the Managed Identity resource

Step 3: Saving Results to Cosmos DB

Finally, we save the results of the image analysis to a Cosmos DB database using the client initialised in Step 1 above.

    db = cosmos_client.get_database_client("myDB")
    container = db.get_container_client("mydbcontainer")
    container_client.upsert_item(body=record)

Step 6: Test the example

  • Try uploading one of the product images to the blob container and monitor the Cosmos DB container for the tagging results.
  • If you configured everything correctly, you should see a record containing the uploaded image metadata as well as the tags obtained from Azure AI Vision service.
  • Note that this assumes you have used the provided Bicep template to provision the resources as well as assign the necessary roles to make this work. Good luck!

Benefits of using Managed Identities

As you can see, using managed Identities eliminates the need for developers to manage credentials manually, while offering several security benefits such as :

  • Elimination of Hardcoded Credentials: By using managed identities, the need to store credentials in code or configuration files is removed, significantly reducing the risk of credential leaks.
  • Automated Credential Rotation: Azure automatically handles the lifecycle of the identities, including credential rotation, reducing the risk of compromised credentials.
  • Granular Access Control: Managed identities can be used with Azure Role-Based Access Control (RBAC) to provide fine-grained access control to Azure resources, ensuring that services have only the permissions they need to operate.
    • This means that even if someone gains access to your environment, they are still limited in the actions they can take, assuming you have assigned roles following the principle of least privilege.
  • Secure Collaboration - Eliminating key authentication means you can easily grant and revoke access to external collaborators using AAD(aka Microsoft Entra ID) and thus eliminate the risk of keys leaked by offboarded team members.

Next steps

  • Note that in the presented demo we are still authenticating using the connection string for the Function App runtime.
  • You can improve on this, by setting Azure Keyvault as the storage medium for the connection string.
  • A better method, however, is to take advantage of managed identities to allow the Function App runtime to authenticate to Azure without a connection string. Once you have this setup, it might be a good idea to Turn Off Key Authentication on your Storage Account.
  • You can also do the same on your Cosmos DB account to only allow access using AAD or Microsoft Entra ID.

Closing thoughts

Managed Identities offer a secure and scalable way to authenticate to Azure services without the need to manage credentials. This eliminates security threats or management overhead due to misconfiguration. When deciding which identity type is best suited for your use case, here are some things to consider:

  • System-Assigned Managed Identities may be inconvenient if you work in a team where you lack permission to assign roles.
    • In such cases, it might be better to use a User-assigned identity with ALL the roles you need.
    • However, because a User-Assigned identity can be shared by multiple resources, this could potentially introduce excess permissions for some of your services, which is generally not a good security practice.
    • In this case, you could consider deploying a User-Assigned only for dev environments and then System-Assigned managed identities in production, so each service adheres to the least-privilege principle.
    • Another viable option is to assign multiple accounts where needed - Yes, this is possible, you can assign more than one user-assigned identity on a single resource.
  • One other thing to note is that it may be possible to delete the user-assigned identity despite existing assignments to resources. Since this does not give warnings, you might want to develop custom solutions to get notified when a destructive action occurs on your User-Assigned identity. One fitting solution for this could be setting a resource lock and/or an alert that monitors the activity logs of your user-assigned identity.

Are you using managed identities in your team? Please share your experience in the comments below. Also, feel free to point out any issues with this article.

Related Links