Reading service principal password from Azure DevOps
Posted February 19, 2021 ‐ 3 min read
Service principal is representation of your tenant or directory on Azure. On Azure Devops, service principal credentials can be accessed for deploying any components It is possible to retrieve credentials from Azure Devops and use them on GitHub Actions.
Security
Given service principal is eligible to manage your services and deployments. Almost all the CI/CD services are coming with masking feature by default for these kinds of secret values.
By design of these CI agents, they are stateless
and each build they need sort of environment variable to work as your service principal.
This comes with a lot of flexibility where you can extend your setup without caring about previous state.
Downside of this flexibility, every CI pipeline needs access to credentials where anyone can access them as well.
Your credential can be super safe inside the Azure world, in fact, they are not as long as someone has access to your CI pipeline.
Reading credentials
For the sake of demonstration, I’ve published an example repo on GitHub.
How to
On your Azure DevOps
pipeline, you need to grant the given pipeline to access service principal
credentials by just enabling a single flag.
addSpnToEnvironment: true
This flag will add service principal credentials as environment variables on your CI pipeline. As shown here.
Next thing to do use reading values and printing them in hex
or base64
format. This will help you to cheat masking
behaviour.
inlineScript: |
echo "servicePrincipalId"
xxd -p <<< $servicePrincipalId -c 256
echo "servicePrincipalKey"
xxd -p <<< $servicePrincipalKey -c 256
echo "tenantId"
xxd -p <<< $tenantId -c 256
Just a reminder, Azure DevOps pipelines will be using ubuntu-latest
VM unless you change it.
Reading
After retrieving values as hex
strings from your Azure Devops, you need to convert them to actual values back.
It can be simply done with few lines of Node.js
implementation.
const convert = (from, to) => str => console.log(Buffer.from(str, from).toString(to));
const hexToUtf8 = convert('hex', 'utf8');
console.log('servicePrincipalId');
hexToUtf8('your servicePrincipalId goes here');
console.log('servicePrincipalKey');
hexToUtf8('your servicePrincipalKey goes here');
console.log('tenantId');
hexToUtf8('and tenantId here as well');
Using them on GitHub Actions
Adding as Secret
Let’s create another secret for GitHub Actions
. To do that, we need one more thing called subscriptionId
.
The subscriptionId
can be found on Azure Portal or simple powershell
or bash
command like
Get-AzSubscription
Name Id TenantId State
---- -- -------- -----
Name Of Subscription subscriptionId tenantId Enabled
Add new Secret
as JSON format on your project or organisation on GitHub.
{
"clientId": "value of servicePrincipalId",
"clientSecret": "value of servicePrincipalKey",
"subscriptionId": "your subscriptionId",
"tenantId": "value of tenantId"
}
Accessing from GitHub Actions
On your github actions workflow file, you can try to log in with using given credentials. Full example can be found on GitHub repo as well.
steps:
- uses: actions/checkout@v2
- uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- run: az group list
- name: Azure logout
run: az logout
Conclusion
It is possible to retrieve credentials and use them in a different CI*. Level of trust is important in the company level. In order to prevent these kinds of issues, you can rotate credentials, but then they can be retrieved again.
As I said, if any person who is granted to access your CI then s/he can do whatever they like.
The purpose of this article, I just wanted to highlight that these ways of protections are useless and technically possible to read secrets.
Same goes for GitHub Actions
as well and there is no way to keep those secrets away from people who work for company.
If you didn’t like Azure DevOps and still want to use GitHub Actions
, it is not that hard to migrate your CI to wherever you want.
*By the way, GitHub Actions and Azure DevOps are using same virtual machines under the hood.