Presently sponsored by: ScriptRunner - Get your free PowerShell Security e-Book!
I have previously written about various Container-topics on this site. Recently, I also published a post about "Best Practices for security in Azure Container Registry."
In this post, I want to bring awareness to how we can make use of one of the tips from that post, namely the Repository-scoped permissions.
We can now create more fine-grained permission for our ACR.
- Time-limited access to help block any access after a specific point in time.
- Granular permission control helps restrict or allow specific actions on the registry. Actions are usually things like Read (pull), Write (push), Delete.
- Help your organization delegate permissions more easily, where consumers of images only have Read access to the repository they need. They can, for example, only pull the images, but not update or change anything.
The concept of repository-scoped permissions is that you have a
Token and a
Scope Map. These two are associated with one another, so you can define how access should be restricted or allowed, and where.
I have given this some thought as to when and where we would utilize it. I didn't want to publish just another article about how we can achieve success. I also want to give some examples of scenarios that I think are interesting for me, and that perhaps could be beneficial for someone else.
External developers get point-access only.
If, like me, you have worked with external developers and contractors over the years, you know that sometimes you want to grant them access to various portions of your repositories and codebases, but perhaps not all of it.
Previously with ACR, we could pretty much grant them "All" or "Nothing" - which is why we had to set up and maintain plentiful of different ACR's and separate our concerns this way.
At some point, we had different ACR's containing the same images. Still, some of the registries had more repositories that should be inaccessible to someone from outside the organization. Since there was no way to accomplish this with a single registry, we had to use multiple and replicate the images as we saw fit. Not ideal - but it worked. You can imagine that there were issues with maintaining consistency instead. Yikes.
With the introduction of this more granular permission and access scope, we can reduce the number of redundant Azure Container Registries we have, and instead focus on configuring them correctly with the right level and scope of access.
Key points I see:
- Fewer resources need operational maintenance.
- Less replicated content/images to keep consistent.
- Less deployment and release pipe issues. Yes, there are issues.
- Focus-mode for the developers, being able to see and access only what they should work on, nothing else.
I'm sure I've missed some other benefits in this scenario - if so, please comment below, and I'll add them here!
Read-only repositories for your customers.
Some products, codebases, and projects are pushing open-source images. Others have proprietary code only for paying customers.
A scenario I keep coming back to is where you have a customer pool relying on your code, and they need to be able to upgrade to the latest version at some point as it becomes available.
With the repository-scoped permissions, we can distribute access to our paying customers to be able to pull down the latest version of our code, with the need for them to set up the deployment routines on their end.
This scenario enables us to easily grant them point-in-time access to upgrade to a specific version, or grant them a non-expiring read-only (pull) access, to always be able to stay up to date with our latest published release.
Key points I see:
- Less distributed upgrade resources required.
- Fewer processes to maintain. I like this one.
- Continuous shipping made easier.
Multiple workloads in your container registry.
Instead of splitting the Azure Container Registries across different workloads, usually when we have images with dependencies on other images or a set-of-whole that cannot correctly operate unless all pieces of the machinery are there. For example, with microservices in practice - while a piece of code can work by it lonesome, it doesn't mean that the solution delivers value unless the pieces fit together.
I've often seen systems split up across many different ACR's, but for the system to work, it has requirements on "ACR1, ACR4, and ACR5 access". I also understand why the configuration was this way since there wasn't any excellent capability to configure access control granularly.
While teams developing on these registries might be separated, the functioning system wouldn't be a valuable solution unless they all played together. For this scenario, I believe that the introduction of more granular access control is fantastic, and I can already see how we can reduce complexity and maintenance, and keep a much cleaner setup across teams.
Key points I see:
- Collaborate more efficiently with less complexity.
- Have more of your shippable code and releases closer together.
- Enables for more accessible cross-team docker image security analysis.
Configuring Token and Scope Maps for ACR
I painted a picture of what I see as beneficial scenarios for this type of configuration. Now I want to try it out and ensure that it can deliver value to us.
Tag along and find out if the promise holds true.
The Scope Map
On the contrary to the token, which we'll talk about further down in this post, the scope map doesn't define the access to specific actions. Instead, the scope map associates the permissions from the token with one or more repositories.
There is a set of built-in Scope Maps by default, which you can either see from the Azure Portal or using your favorite Azure command-line utility.
In the Azure Portal, go to your ACR -> Scope Maps:
These built-in scopes are really helpful, and I can already see the benefit of granular permissions. However, should you want to design your own Scope Maps, you can simply click "+ Add" in the top, and that will kick off a new dialog like this:
In my demonstration above, we are assigning "content/read" permissions to the "tobi-demo/nginx" repository.
From now on, if any request that is authenticated with a token associated with this scope-map, they can only Read content (pull images) from the repository named "tobi-demo/nginx".
However, none of these Scope Maps works unless a Token is mapped to them. Let us do that next.
In general terms, the token can generate a password and is then used to access the repository. Similar to tokens of any API or services you might use on other service offerings. This is where a user authenticates themselves to the registry and says (pseudo), "Hey, I'm Tobias Zimmergren."
Benefits of the token:
- Kill the access of a token is easy and can be done at your own discretion.
- Time-bombing the token is equally easy and helps maintain a stringent security posture.
- The token allows for specific actions like Read/Write/Delete content or metadata in a repository.
To create a new token, head over to your "ACR -> Tokens."
From there, you can create new tokens, and you can assign it to an existing or new Scope Map.
When you have created a new token, you will see that the Azure portal says "Not generated" for your Password1 and Password2 expirations. This is because we now have to take another action, which is to generate our password(s) for this token.
Click your new token, and then click the Generate button. Unfortunately, the button currently looks more like an "Edit" button, but in fact, this is the button to generate new passwords.
You will now get the option to configure the optional expiration of the passwords. Ideally, you will use an expiration time that isn't too far into the future. This is helpful if we want to rotate our keys and passwords regularly to avoid any slack in the security posture.
A scenario where it can also be beneficial, when not used as a key rotation mechanism, is to set the expiration of contractors or other time-based work from externals or other departments.
In the Azure Portal, it's easy enough to do this. When you click the Generate button, you'll see this dialog, and then you can take action to set expiration:
Now you can share the token password with whoever needs it. Please do so in a secure manner, for example, using Azure Key Vault or a password manager, rather than pasting it in the chat or e-mail - something we, unfortunately, see too often.
The portal also grants you the privilege of copy-pasting the access details, so you can get started right away:
docker login -u tobi-nginx-reader -p <REDACTED> acrdemos.azurecr.io
Greatness. We have configured a new token and defined for how long it should live. Along the way, we also associated it with the Scope Map we set up, and now we have a way to gain access with
pull permissions to the one repository named
Trying the new Token and Scope Maps in action
We set things up. Now we want to verify that it works.
Since the Token password generator was kind enough to not only generate the password for us but also generate a nice copy-friendly string to toss into our command line, I'll grab it and try that. Here's my command line:
docker login -u tobi-nginx-reader -p vZWsc==0C8ssGy7V3S2JlAoU7NS=w2eq acrdemos.azurecr.io
Note: The credentials above are fake. In case you think that you'd want to try them out.
The generated command gives you a fully functional way to authenticate to your ACR using the -u and -p arguments, for username and passwords.
Unfortunately, this isn't recommended, and you should use --password-stdin as an argument instead, so the password doesn't show up in the UI or in the history of your command-line tool.
This is how I do it instead:
docker login acrdemos.azurecr.io -u tobi-nginx-reader --password-stdin
Whatever way you choose to do it, the result should be that I can read from the desired repo, and hence be able to fetch the image:
C:\Dropbox\Tools\cmder_mini> docker pull acrdemos.azurecr.io/tobi-demo/nginx Using default tag: latest latest: Pulling from tobi-demo/nginx Digest: sha256:6b3b6c113f98e901a8b1473dee4c268cf37e93d72bc0a01e57c65b4ab99e58ee Status: Image is up to date for acrdemos.azurecr.io/tobi-demo/nginx:latest
This worked like a charm - we now have scoped permissions directly to a single repository, with limited access (pull only), and it works.
Still in preview, though!
Today in April 2020, this is still in preview. There are some preview limitations. The docs website mentions these current limitations:
- These features currently only work with Premium SKU.
- These features currently do not work with Managed Identity or Service Principals.
- Scope Maps doesn't work for registries that have anonymous pull access enabled.
When I am writing this, in April 2020, the requirement for using the Token and scope Maps is that you are using the Premium SKU.
This means you can not make use of this if you're using the Basic or Standard tiers.
To bang the drum of Azure here, I do keep most of my ACR services on the Premium tier, which also grants me a bit more security capabilities from Azure Security Center. If repository-scoped permissions aren't enough of a reason for you to upgrade, perhaps securing your assets is.
There we have it. A feature set that I have personally been waiting on for a long time, finally have reached a bit more maturity. We can now more granularly control the permission and access to our repositories.
Here are some additional links and reading that will be helpful:
- Create a token with repository-scoped permissions (Microsoft Docs)
- Best Practices for Security in your Azure Container Registry (Zimmergren)