Accessing Service Principal Secrets in Azure DevOps

Azure DevOps uses Service Principals to interface with Azure. This let's you view their secret from within a pipeline.

Accessing Service Principal Secrets in Azure DevOps

Azure DevOps use something called Service Principals to interface with Azure. They're pretty much comparable with users in the sense that you login to them with a username and a secret. Normally this is setup by an admin in DevOps at the start of a project, then developers simply select from a drop-down to to use them, never seeing the user/secret.

The issue, is that to truely secure these values, you have to have an absurdly locked down CI pipeline. In this post, I'll show you how to access these guarded values with a well known task.

In DevOps in the Azure Resource Group Deployment task, there is a check-box. It lets you access Service Principal (SP) details from an ARM template.

This is almost always a bad idea for a task to implement.

This lets us use the user/secret for a SP in a tempate, as shown:

Still, almost always a bad idea.

We now have two issues. We need to override the script without going via the build (because that's slow), and we need to convince ARM to output the raw values.

If we pretend that the Service Principal is only allowed to deploy to a Resource Group that we as a user do not have access to, then it'll be pretty quite tricky to output as a Resource name or a Web App variable which we would then view raw. Luckily however, there is a feature in ARM called LinkedTemplates.

What we do, is go over to gist.github.com and create a quick ARM template that uses a LinkedTemplate where the user/secret are query parameters. The target of the LinkedTemplate is then an server which extracts the query parameters and outputs the result.

I know, the spacing hurts me too.

Then you pop back to DevOps and update your task to now use a URL rather than a build artefact.

This is the raw link to the gist.

Fire off your build and wait for the payload to arrive. You'll receive a GET request for a url, with query parameters that match up to the user/secret. I've put a step in that just neatly formats it into a string.

I used the very simple Node-Red as the host for the echo server

Then, it's a simple matter of going to your local CLI and logging in.

Voilla. We have a long-lasting secret for a service prinpical which are often trusted with high-level access.

Conclusion

It's important with every part of security to ask what vectors your product needs to protect against. As we've seen here, if a user has the ability to change a DevOps CI in Prod in any way, they'll be able to access service principals user/secrets.

Given the users ability to change old releases, create new ones with custom tasks, and edit task-groups, it'll be very hard to protect against every vector whilst maintaining velocity in a Dev team. Unless you have a modern DevOps team, I would recomend on relying on trust and the developers own stake in a project to protect it internally.

Always make sure to clean up after yourself.