Certificate-based auth with Azure Service Principals from Linux command line
In his comprehensive article, Developer’s guide to auth with Azure Resource Manager API, Dushyant Gill describes multiple authentication flows for obtaining an access token from Azure Active Directory and using it to invoke Azure Resource Manager REST APIs (ARM).
There are also multiple open-source Azure SDKs (Java, Node.js, Go, Ruby, Python, .NET) that encapsulate the necessary authentication logic into convenient helper methods.
In this post, my aim is to demonstrate, in a deliberate semi-manual fashion, how to obtain the access token via Azure Service Principal with Certificate-based Authentication from Linux (e.g. CentOS 6.6) command line using a few tools and raw REST calls.
Let’s get started.
Generate Certificate
Use openssl command to generate a self-signed certificate and protect it with a PEM pass phrase (e.g. P@ssword123):
openssl req -x509 -days 3650 -newkey rsa:2048 -keyout key.pem -out cert.pem
You should see two files created in the current directly:
- cert.pem contains the public key
- key.pem contains the private key
Certificate Thumbprint
To be able to uniquely identify the certificate, let’s get its hexadecimal thumbprint:
openssl x509 -in cert.pem -fingerprint -noout
We will actually need the thumbprint converted from its hexadecimal representation to base64. We can use sed to replace the colons and remove “SHA1 Fingerprint=” substring, xxd to convert to bytes, and base64 to encode.
Combining these steps together into the following piped command:
echo $(openssl x509 -in cert.pem -fingerprint -noout) | sed ‘s/SHA1 Fingerprint=//g’ | sed ‘s/://g’ | xxd -r -ps | base64
Certificate Key Value
cert.pem file contains the public key value of the self-signed certificate we generated. We will need to grab that value skipping the first and the last lines.
tail -n+2 cert.pem | head -n-1
Setup Azure CLI
To manage Azure via the command line from Linux, please install and configure Azure CLI by following these instructions:
- Install Azure Command Line Interface (Azure CLI) on Linux
- Connect to your Azure subscription from the Azure Command Line Interface (Azure CLI)
Once Azure CLI is installed and you have logged in using your “organizational account”, switch to the “ARM mode”:
azure config mode arm
Create Application and its Service Principal
Use Azure CLI to create an “Application” in the Azure Active Directory of your subscription by passing the public key value from cert.pem file (as you recall from above tail/head are there to skip the first and last lines):
azure ad app create — name “myapp20150918” — home-page “http://myapp20150918/" — identifier-uris “http://myapp20150918/" — key-usage “Verify” — end-date “2020–01–01” — key-value “$(tail -n+2 cert.pem | head -n-1)”
NOTE: If you get an error message saying “‘ad’ is not an azure command. See ‘azure help’”, double check that you switched to the ARM mode via “azure config mode arm”.
Create the “Service Principal” object for the Application Id that was created in the previous call.
azure ad sp create <Copy and Paste Application Id GUID Here>
Assign proper role to the “Service Principal” using its Object Id. In this example, we will assign the “Contributor” role to the service principal at the scope of the subscription so that it can access and make changes to any resource group within the subscription.
azure role assignment create — objectId <Copy and Paste Service Principal Object Id GUID Here> -o Contributor
Install node.js package jsonwebtoken
Visit http://jwt.io/ to learn more about the JSON Web Token format and see many other libraries for JSON Web Token signing/verification available in various programming languages. I have picked Node.js since it is already available on the Linux VM after installing the “Azure CLI”. In addition, we need to make sure that the library we use supports RSA-SHA256 algorithm that is expected by Azure Active Directory when using certificate authentication.
Use node package manager (npm) to install the jsonwebtoken package:
npm install jsonwebtoken
Obtain Azure Active Directory Tenant Id
We also need to obtain our Azure Active Directory Tenant Id GUID to specify it within the token payload. Since we already have Azure CLI installed, we can obtain the Tenant Id by running the following command and copy-and-pasting the value returned in the “Tenant Id” column.
azure account list
azure account show “Arsen Subscription Name”
Sign the JWT Token
Create a small node.js script that we will call signjwt.js to sign the client assertion specifying the base64 encoded certificate thumbprint in the “x5t” additional header. You will recall that we obtained this base64 value in one of the earliest steps.
The other standard “claims” of the JSON Web Token should contain the following values:
- “aud” (Audience) is set to https://login.microsoftonline.com/TENANT_ID _HERE/oauth2/token
- “iss” (Issuer) and “sub” (Subject) are all set to the Application Id GUID
- “jti” (JWT ID) is set to a unique identifier for the JWT (in this example I will set it to a random value)
- “nbf” (Not Before) is set to the UNIX timestamp denoting the start of the token validity period
- “exp” (Expiration Time) is set to the UNIX timestamp denoting the end of the token validity period
var jwt = require(‘jsonwebtoken’);
var fs = require(‘fs’);
var cert = fs.readFileSync(‘key.pem’);
var additionalHeaders = {
“x5t”:”HuMOEZfePZctStPVCVWgmQc90Pc=”
}
var myJwt = {
“aud”: “https://login.microsoftonline.com/TENANT_ID/oauth2/token",
“iss”: “APPLICATION_ID”,
“sub”: “APPLICATION_ID”,
“jti”: “” + Math.random(),
“nbf”: “” + (Math.floor(Date.now()/1000)-1000),
“exp”: “” + (Math.floor(Date.now()/1000)+7*8640000)
};
console.log(myJwt);
var token = jwt.sign(myJwt,cert,{algorithm:’RS256', header:additionalHeaders});
console.log(token);
Execute the script we created
node signjwt.js
Validate Signed JWT Token
We can use http://jwt.io/ to validate that the token is parsing correctly. The signed token consists of three parts: base64(header).base64(body).base64(signature) without the “==” at the end.
Example of what JWT looks like with a signature:
Get Access Token from Azure Active Directory
We will use Postman, Fiddler, or any of your other favorite HTTP request builder tools to manually submit a raw HTTP request to the Azure Active Directory endpoint passing in all of the required parameters and obtaining back the Bearer access token that we can use to access Azure Resource Manager APIs.
We will make an HTTPS POST to this OAuth endpoint for your Azure Active Directory tenant: https://login.microsoftonline.com/TENANT_ID/oauth2/token
The request body must contain the following parameters:
HTTPS POST URL https://login.microsoftonline.com/TENANT_ID/oauth2/token
grant_type client_credentials
client_id This should be set to your Application Id GUID
client_assertion_type This should be set to a specific value telling Azure Active Directory OAuth endpoint that we will be using certificate authentication instead of “password” urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertion This is the signed JWT token. It will be a long value. Remember, it was signed using our little signjwt.js script and the private key from the key.pem file. Azure Active Directory will validate this assertion’s signature using the public key (cert.pem) that we submitted to Azure when creating the application via Azure CLI.
resource This is the endpoint for the resource that we are asking Azure Active Directory to issue us a token for. In case of Azure Resource Manager, it should be set to the following URL since that REST API start with this value https://management.azure.com/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/{provider-namespace}/
We can also obtain the access token from the command line using curl
curl
— data “grant_type=client_credentials&client_id=APPLICATION_ID&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=SIGNED_JWT&resource=https://management.azure.com/" https://login.microsoftonline.com/TENTANT_ID/oauth2/token
Access Azure Resource Manager API
Now that we obtained the “access_token”, we can use it to access Azure Resource Manager REST API.
We will manually construct a simple HTTP request to get the list of resource groups and confirm that the access token is working as expected.
HTTPS GET URL You will need your subscription id, which you can obtain using “azure account list” Azure CLI command and looking at the Id column https://management.azure.com/subscriptions/SUBSCRIPTION_ID/resourcegroups?api-version=2015-01-01
HTTP_HEADER Authorization
Bearer ACCESS_TOKEN_ISSUED_BY_AZURE_ACTIVE_DIRECTORY
We can also perform this test from the command line using the following curl command and getting back a JSON response from the Azure Resource Manager REST API:
curl -H “Authorization: Bearer ACCESS_TOKEN_HERE” https://management.azure.com/subscriptions/SUBSCRIPTION_ID/resourcegroups?api-version=2015-01-01
Conclusion
By now we should have a pretty good understanding of how to manually authenticate via Azure Active Directory using an application Service Principal and certificates, how to obtain the OAuth access token, and how to use it to invoke Azure Resource Manager REST APIs — all from Linux command line.
Whenever possible, I recommend using the available Azure SDKs with all of the inbuilt helper methods. However, I also find it useful to manually step through a process to get a deeper understanding of what is happening behind the scenes.
I’m looking forward to your feedback or questions via Twitter https://twitter.com/ArsenVlad
Originally published at blogs.msdn.microsoft.com on September 18, 2015.