Avoid Secrets in your Azure Cognitive Service Code (Python Tutorial)
Recently I was wondering in Azure Documentation on Cognitive Services, seeking how to write a simple Python app that uses Face API.
The Azure Docs has really good samples and get started Guide here, but they all demonstrate on how to use the Cognitive service API with an API Key.
“There must be another way” I was thinking to myself, reflecting on the very most basic and critical aspects of security in code: Never Store your Keys and Secrets in code.
Sure, I could use Azure Key Vault to store the secret and then retrieve it on runtime with a few lines of code, but there is even a better way nowadays.
The following short Tutorial will show you how to use Managed Identities for Azure Cognitive Service API for Python, but actually the techniques demonstrated here can be used for any other Service in Azure which its SDK is based on Rest Authentication with no specific Managed Identity API.
I will not re-publish the steps already presented on the Docs, just the ones related to using Managed Identities on the code.
Prerequisites:
- Have a Cognitive Service on your Azure Subscription
- Python3 with VS Code (or any other IDE of your choice)
- Some basic understanding on Managed identities in Azure (not covered)
- Clone the Source Code for this article here.
Part 1: Sanity on the Code with API Keys
Let’s start by examining the first sample from the docs : detect-faces.
The sample appears at my repo on the server.py / auth.py file.
If you examine the code, you will see its almost identical to the official docs with the slight twist of using INI File:
CognitiveFaceApiUrl= https://<your_accunt>.cognitiveservices.azure.comCognitiveFaceKey = <your_key>
You will need to replace <your_account> with your cognitive service face URL and <your_key> with your API Key accordingly.
The following code snippet uses the FaceClient object with API Key and a service endpoint, just as described on the official docs:
run pip install -r requirements.txt
and then python server.py
to see that the demo works :
Since this was just a sanity Test, let’s move on to the main event.
Part 2: Authentication with Managed Identity
We will now look at the more interesting method on the auth.py file:
few changes are noticeable:
- The Key was now replaced by the FaceTokenCognitiveServicesEndpoint which is used to fetch the Token we need against our Active Directory authentication with the Managed Identity.
- we are using the DefaultAzureCredential class from Azure Azure.identity module which provides basic token authentication to azure services.
The utility tries several credentials types such as Environment Credentials, Managed Identity Credentials, etc.
more information can be found here. - We are now using the BasicTokenAuthentication and not the CognitiveServiceCredentials class.
When I first ran this sample with my VM’s (see below) It took me some time to make this code works since it was not clear from the docs what is the “scope” exactly and how the access token should be constructed.
So, I’m here to save you some time, sit and relax.
- The code first gets the token from the DefaultAzureCredenitals as explained above. This means we will need to attach the MI to the running VM/service instance and give it the right role assignment to our cognitive service API.
- Fetching the credentials usually is performed from a dedicated service endpoint that supports Managed Identity (https://cognitiveservices.azure.com in our case),but sometimes you will need to get the token from AAD service itself (“https://management.azure.com")
- The return format of the auth-token is a tuple, where our BasicTokenAuthentication class on line 16 above asks for a dictionary, so I needed to converted the Tuple to dictionary
(proper disclosure: I’m not a python developer and you find a better way to achieve that, don’t hesitate to suggest it!)
Part 3: Creating the Managed Identity and assign permissions
Now it’s time to go to azure portal (or your favorite CLI tool) and create a managed identity.
- Go to Azure Portal and create a managed Service on your subscription. select the same Region where you deployed your Cognitive Service.
- Head to your Cognitive Service and under Access Control (IAM) select Role Assignments and then Add- > Add Role Assignment.
- Select the Role “Cognitive Service User”, your subscription and the Managed Identity created before, and confirm.
- Once the role assignment has been created, we have granted the MI the permission to access the cognitive service. we selected a general role this time, you can refine this permission according to your specific needs.
Part 4: Configure your runtime
There are various way to attached the User-Assigned Managed Identity to your service, depending on your scenario.
If you are running on bare IaaS VM’s than Azure lets you configure both System assigned Identities and User Assigned identities as described here.
If you ran on Service Fabric you can assign the Managed Identities during the creation phase as explained here.
Azure Kubernetes Service (AKS) also supports Managed Identities now as first-class-citizen if your AKS cluster supports the CNI Plugin (here) or if you leverage the AAD-Pod-Identity onboarding process (here).
Attaching Managed Identities to your application running on any of those techniques is out of the scope of this article.
You will need to comment-out the Authentication with Keys and uncomment the code for Part2 that appears on server.py in order to take advantage of the MI authentication method:
Since I am running on AKS version 1.19, with aad-Pod-Identity enabled I have attached both the Dockerfile used to create the image and the pod.yaml definition which reveals some of the Environment variables needed for this solution to work :
- AZURE_TENANT_ID : your tenant Id (used to authenticate with your AD)
- AZURE_CLIENT_ID : your Managed Identity’s client Id.
after all set is done we can launch the pod by running:
kubectl apply -f pod.yaml
and verify that we see the results on the console with:
kubectl logs -f face
Conclusion
We have enabled Managed Identity on our runtime, removed the Keys (or the need to retrieve them from Key Vaults), chose a dedicated permission to our Identity according to the “least privileges” concept, and last but not least we have done that in very few lines of code !
Managed Identities are great way to not only increase security in your code, but also to simplify it and have a much clean code.
The full source code can be found here.