Managing permissions in ADO can be complex but understanding granular permissions and applying them correctly is critical to an effective software supply chain security posture.
Azure DevOps is the evolution of Microsoft's suite of products, such as Team Foundation Server (TFS) and Visual Studio Team System (VSTS), that enterprises use to implement DevOps practices and manage their software supply chain.
Microsoft offers Azure DevOps as a software-as-a-service (SaaS) platform, which includes a comprehensive set of DevOps tools organizations can use for continuous software development and delivery. It also provides an offline version called Azure DevOps Server that uses the same code base as the online service.
Azure Pipelines, Repos, Boards, Test Plans, and Artifacts are some of the services available in Azure DevOps (ADO) for different phases of the software development lifecycle. Most enterprises prefer Azure DevOps for software development and delivery, as it is flexible, is cloud-agnostic, and supports all leading development frameworks.
In this blog, we will look into the basics of Azure DevOps security permissions, how to manage them, and how to fine-tune the access control process.
The CIS Software Supply Chain Security Guide emphasizes the principle of least privilege and access control for securing the software development environments (SDE) and source code management (SCM). Leaving your development ecosystem unguarded could lead to attacks and code manipulations that could go undetected for a long time.
Azure DevOps provides a robust and granular permission framework that, when properly utilized, can be used to implement effective security controls. However, the downside of Azure DevOps is that managing permissions can sometimes become challenging due to the large number of granular controls available.
Let’s start with some of the key concepts surrounding Azure DevOps permissions: security groups, permissions, and access levels.
In Azure DevOps, you can create organizations, projects, and teams to help structure the access and usage of services. With the creation of each of these entities, a security group is created automatically with default permissions or access rights. These groups are Build Administrators, Contributors, Project Administrators, Readers, Project Valid Users, Release Administrators, and the default Team group.
You can also create custom groups at a project or organizational level.
The security group, either default or custom, will have permissions assigned that authorize a member of the group to allow/deny access to Azure DevOps tasks or features, for example, create test runs, manage build and release operations, etc. Role-based assignments can be used to manage permissions. Users who are added to a security group inherit that group's permissions. Permissions can be assigned directly as well, but this is not the best practice.
You can define five types of permission assignments for users or groups: allow/inherited allow or deny/inherited deny/not set.
Each feature of Azure DevOps will have its associated permissions that impact who has access to the different stages of the software development life cycle (deployment groups, code release pipelines, branches, variable libraries, etc.). An example of the permissions under the branch security category is shown below. These are important access permissions that decide which users and groups can perform branch-related operations.
Permissions to certain web portal features are defined by access levels. These are Stakeholder, Basic, Basic + Test Plans, and Visual Studio Subscriber. Users added to specific security groups will have different access levels assigned to them:Basic: Access to most features
Azure DevOps access is managed by a combination of a user’s membership, permissions, security groups, and access levels.
The permission management process follows a hierarchy, where permissions can be inherited or overridden from the parent. Work item inquiry folders, version control folders, iterations, Git repositories, branches, artifacts—these are some of the hierarchy nodes where object-level permissions are assigned and the permissions are propagated down the hierarchy. You can also set explicit permissions at the object level to override the inherited permissions. For example, a subset of permissions at the repository level will be inherited by a branch, but you can configure branch permissions to override this.
Group members inherit permissions from the security groups by default. In the event that a user is a member of two separate groups with conflicting permissions, “deny” will take precedence over “allow” access. There are some exceptions to this rule, which can be confusing at times. For example, regardless of whether users are also part of other groups that have “deny” rights assigned to them, members of "Team Foundation Administrators" or "Project Collection Administrators" will continue to have access to certain resources. For example: If you deny permissions to a user for some of the project-level actions like renaming a project or viewing the project permissions, they can still be able to do it if they are part of Project Collection Administrators.
As we saw in the previous section, managing permissions in Azure DevOps is not very simple or straightforward. It's interesting to note that Azure DevOps has 302 different kinds of permissions, including project-level permissions, object-level permissions, task-level permissions, etc.
We saw earlier that ADO provides options to manage security at a granular level during every stage of code development and delivery. Then there are permissions at the project and organizational levels to consider. Additionally, you need to take into account permission precedence; for example, while a “not set” permission setting denies the ability to perform certain activities, a user would still be able to do those activities if he is a member of a group that has the same permission set as “allow".
It becomes difficult for an ADO administrator to track and ensure that a specific user/group is given the required permissions for doing their day-to-day work. More importantly, ensuring that permissions do not overlap and implementing the principle of least privilege becomes even more challenging.
Clearly, controlling permissions in Azure DevOps involves careful planning to prevent the assignment of excessive access levels. As was previously demonstrated, users belonging to some groups may end up with excessive permissions, even if you explicitly assign them to groups that have restricted access.
A branch policy can be configured for each branch created in an Azure DevOps repository to protect them. Using a branch policy, you can implement controls such as setting a specific number of reviewers, resolving comments, limiting the type of merge requests, etc. Additionally, you can fine-tune permissions in branch security settings. There is a very real possibility of misconfigurations creeping in here. For example, a user may not have permission to review a PR in branch security, but they are marked as an approver of PRs in the branch protection policy.
You can also create cross-repo policies that help protect multiple branches centrally; for example, build pipelines with specific steps that execute on all main branches. However, the caveat with this approach is that you can only append to a cross-repo policy; you cannot override it with a branch-specific policy, which makes the process inflexible.
There is additional complexity associated with implementing the minimum required permissions for users in Azure DevOps. If you remove a member from a group, it will impact the access of the user to multiple repositories. If you revoke the access of a group from a project or repo, all members of the group and its nested teams will be impacted. As you can see, a small change can have a domino effect, and it will be difficult to pinpoint the root cause, especially for environments with extensive DevOps implementation.
Access levels in Azure DevOps are associated with different tiers of licensing. Let’s take a look at how the access levels and licenses are connected.
This level can be assigned to unlimited users but only provides access to a limited set of features like Azure Boards and collaboration tools. Users will not be able to access any code repositories, and it is not helpful for developers. This level is mainly intended for stakeholders who want to track their work.
Users with Visual Studio Enterprise and Visual Studio Professional licenses can log in to Azure DevOps and use the features that are associated with their subscription level.
With the Basic level, you will have access to the main Azure DevOps features, meaning Azure pipelines, Azure Boards, Azure Repos, and Azure Artifacts. The first five users are free; for every subsequent user, there is a cost of $6/user/month.
This tier enables access to different options for test planning and execution, i.e., browser-based testing, UAT, rich-client testing, etc. You also get access to centralized reporting. As of press time, the license costs $52 per user per month.
As you can see, the cost of using Azure DevOps is directly correlated to the number of features and the access levels required. Any enterprise will need access to test plan features for end-to-end software delivery. In Azure DevOps, you need the “Basic + Test Plans” license for this, which will cost more. Without a structured access audit process, the project's stale users will also continue to incur monthly costs.
By reducing access levels to developers and restricting them to only the required features, Azure DevOps customers can bring down the cost considerably.
Solving the above challenges at scale requires a tool specialized in software supply chain risk management. Arnica’s behavior-based solution for the management of software supply chain risk and security helps ease the complexity of granular permission management in Azure DevOps. Arnica offers a comprehensive solution that implements end-to-end protection of your DevOps toolset and is trusted by enterprises to ensure developer security.
With Arnica, you get visibility into excessive permissions for an unlimited number of users and can fix them for free. Organizations can use Arnica for automating their cybersecurity supply chain risk management (C-SCRM) and implement the principle of least privilege for accessing Azure DevOps. It also simplifies software supply chain security management by granting self-service access.
Arnica integrates with Azure DevOps to help you ensure code security and implement best practices for software supply chain security.