Security / Technology / Contributed

Policy-as-Code or Policy-as-Data? Why Choose?

4 Apr 2022 10:00am, by
Omri Gazitt
Omri is the co-founder/CEO of Aserto, an authorization startup, and his third entrepreneurial venture. He's spent the majority of his 30-year career working on developer and infrastructure technology, most recently as the CPO of Puppet. Previously he was the VP and GM of HP's Cloud Native Platform (with business, product, and engineering oversight for OpenStack, Cloud Foundry, CloudSystem products and services), and a General Manager at Microsoft with responsibilities for Azure, SQL Server, Application Server, and the .NET Framework.

Authorization is an age-old problem, and we’ve seen many patterns over the years that aim to provide some structure to it, including access control lists (ACL), role-based access control (RBAC), and attribute-based access control (ABAC).

Every authorization solution requires a combination of code and data: the code controls access to a protected resource based on the user context, and the data describes which users are allowed to perform what operations on which resources.

Recently, we’ve seen two important trends that are both becoming important to modern authorization systems:

  • Lifting policy out of the application, expressing it in a domain-specific language, and storing/versioning it as code.
  • Expressing relationships between subjects (users), predicates (actions or roles), and objects (resources) in a more standard way.

Policy as Code

The rise of Web 2.0 in the 2000s brought with it a set of interoperability standards for identity and access on the web. XACML included a textual representation of an attribute-based policy, as well as a protocol for interacting with a policy decision point.

In the context of the cloud-native movement, the Open Policy Agent (OPA) project has become the de-facto standard for expressing policy as code. OPA policies are written in a Datalog-inspired language called Rego, and can be stored and versioned just like configuration or infrastructure code, as pioneered by Puppet, Chef, Ansible, and Terraform.

Policy-as-code provides a powerful abstraction for lifting authorization logic out of an application and centralizing it in a different source code repository, allowing for separation of duties between application developers, who only need to worry about enforcing the policy by passing it the correct inputs, and security engineers, that can evolve the policy without direct involvement from developers.

Expressing policy as code makes it inherently easier to reason about – an engineer that is familiar with the language syntax can easily determine how a policy works, and can test a policy with different inputs to determine what it will do.

Providing a standard mechanism for building policies into immutable images and signing them is an important aspect of ensuring a secure software supply chain for policy artifacts. The Open Policy Registry provides this capability for OPA policies.

Finally, having complete decision logs that include the policy image, user context, and resource context that were used to make each decision helps auditors reconstruct and replay these decisions, making it easier to attest to why each decision was made.

For these reasons, policy-as-code is now becoming an essential aspect of modern authorization architectures.

Policy as Data

Ultimately, code operates on data. Whether that code is embedded in an application or lifted out and stored/versioned separately, you need to bring data to the policy, in the form of a user context, resource context, or both.

Access-control lists (ACLs) are the oldest form of access control, having been around since the ’60s. Systems inspired by the ACL model have a fixed policy: relations between resources and users are limited to a small number of “roles”, such as viewer, editor, owner. In these systems, the policy logic is hardcoded in the authorization engine. The problem shifts from creating and evolving an authorization policy to defining a standard data format for feeding data tuples into the engine, typically in a form such as “alice is the owner of document:roadmap”.

Google’s Zanzibar paper, posted in 2019, describes the authorization system underpinning Google Docs, has created a resurgence around the ACL model for authorization, and inspired recent innovation in this area. New DSLs are being defined to codify resource schemas, and a standard notation is emerging for encoding { subject, predicate, object } triples.

In this context, authorization means answering a question such as “is Alice allowed to view the roadmap document?” by walking the graph of relationships between users and resources. This is a much more data-centric approach to authorization.

Which Is Better?

As always, the answer depends on the use case. In order to avoid a complexity explosion, authorization systems typically apply a set of constraints.

In the Google Docs example, the system is optimized around a fixed set of “roles” (viewer, commenter, editor) applied to a hierarchy of folders and documents. The hard problems to solve involve delivering a globally consistent, highly scalable system for managing and enforcing those permissions; flexible policy isn’t part of the requirement set.

Other systems require more flexibility in mapping between user attributes and the permissions that those users may have, either as global roles or over groups of resources. For example, many systems define resource “scopes”, such as organizations, projects, or teams, and assign users a set of permissions on resources that belong to those scopes, Those systems often have many domain-specific resources, each supporting a different set of operations, and may define dozens or hundreds of discrete permissions, often grouped into a smaller set of roles. These systems require a policy-centric design.

Policy as Both Code and Data

To answer the original question, authorization is best when it blends both policy-as-code and policy-as-data. When the authorization policy needs to be flexible and evolve with application requirements, a policy-as-code approach is a critical facilitator for this evolution. At the same time, every authorization system needs to have a strategy for defining the data that’s needed for making authorization decisions, and a scalable way to bring that data to the authorization context.

Try Aserto

At Aserto, we’ve built an authorization API for developers who want to explore the best of both worlds. It brings together a policy-as-code workflow with prescriptive ways to bring user and resource context to the authorization engine. Sign up for a free account and check it out!

Feature Image by Tobias Brunner from Pixabay.