Gabriel L. Manor
Posted on May 13, 2024
Creating and managing an effective authorization system is no easy task. Especially when it comes to doing so effectively across distributed cloud-native applications. Working with people who have been building authorization for a long time, we designed our authorization service, Permit.io, to be cloud native. Throughout this process, we found that there are three crucial key elements to building cloud-native authorization: Proper CI/DC, thorough testing, and precise modeling and implementation of your fine-grained authorization processes.
This blog will cover these three crucial components for building cloud-native authorization, discuss how they allow you to build reliable and scalable fine-grained authorization, and show you how we implemented them in Permit.
What is Cloud Native Authorization?
In simple terms, Cloud Native Authorization refers to building and managing authorization systems that are fully integrated and operational within a cloud environment, leveraging cloud capabilities to enhance flexibility, scalability, and resilience. The key to achieving effective cloud-native authorization lies in focusing on three primary aspects:
Continuous Integration and Deployment (CI/CD), rigorous testing , and accurate modeling of your authorization processes.
- CI/CD: Continuous integration and deployment ensure that your authorization system can adapt quickly to software changes and user requirements. This practice helps automate the update and deployment processes, which minimizes the risk of errors and maximizes operational efficiency.
- Testing: Thorough testing is crucial to verify that the authorization system works as intended under various scenarios. It involves checking the system's security aspects and functional capabilities to prevent breaches and ensure that policies are enforced correctly.
- Modeling Your Implementation: Precise modeling involves outlining and structuring how authorization processes interact within your application. This step is critical because it helps create a clear framework that defines how policies are applied and managed across different application components.
Let’s dive into each of these and see how you can utilize them in your own fine-grained authorization system.
Policy Lifecycle Management Through CI/CD
Enhancing policy management with Continuous Integration and Continuous Deployment (CI/CD) processes allows us to maintain a consistent and secure authorization layer. Managing and merging policies into various environments while ensuring consistency and compliance, syncing them to Git and deployments, and utilizing the power of policy as code allows us to handle authorization swiftly and effectively.
Automated Policy Updates and Deployments
Automating updates and deployments through CI/CD ensures that changes are applied consistently and without human error across all environments. This automation speeds up the deployment process and enhances security by reducing the risk of misconfigurations. At Permit.io, we primarily manage the policy life cycle as part of a CI process by using projects and environments together with the Environment API. By working with Permit environments for CI, CD is automatically achieved using OPAL, as every environment is automatically deployed to PDPs mapped to them (via their API secret key).
Version Control and Consistency with GitOps
Maintaining version control and ensuring consistency across environments is essential when managing complex systems. Using a Git-like model for environments, where each environment corresponds to a branch, helps teams manage development, staging, and production configurations separately yet consistently. This approach allows for editing, reviewing, and merging policies as code within a Git repository, ensuring transparency and collaboration. At Permit.io, we achieve this by allowing you to automatically map branches when you connect your Permit Project to a repository, facilitating efficient lifecycle management and leveraging Git's robust version control system.
Effective Authorization Testing Strategies
Before any policy is deployed to a production environment, it must be thoroughly tested in a controlled setting. Effective testing strategies confirm that the system behaves as expected under various conditions and help catch potential issues before they affect the production environment. Here's how testing should be structured:
Unit Testing
Unit tests are essential for verifying the individual components of your authorization policies. These tests should be designed to run independently from the rest of the system, allowing for quick checks on the logic and functionality of each policy. At Permit.io, unit tests can be executed using policy as code, where the policy code is loaded directly onto the policy engine (such as via the OPA test command line), ensuring that each component functions correctly in isolation.
Application and Integration Testing
Application and integration tests evaluate how well different parts of your application work together under real-world scenarios. This involves deploying a Policy Decision Point (PDP) that is synced to the development environment and running tests that simulate actual application queries against the PDP. This method ensures that policies are not only theoretically correct, but are also effectively enforced within the application context. In Permit.io, we incorporate these tests into our development cycles, allowing for thorough validation of policy implementations in a controlled yet realistic setting.
Automation of Testing Processes
Automation plays a vital role in maintaining efficiency and consistency in testing. Automated tests can be configured to run as part of the CI/CD pipeline, ensuring that every change is vetted before it goes live. We suggest you consider the following triggers and hooks, which you can mix and match according to your needs:
- The CI system deploys PDPs specifically for testing policy changes.
- Developers can trigger tests automatically through the CI system.
- Tests must pass before environments are merged; if tests fail, the CI system will not proceed with merging changes.
- The CI system uses APIs like Create/Copy-env to establish environments purely for testing purposes, guaranteeing that each test environment mirrors the target deployment scenario accurately.
Modeling and Implementation
Modeling and implementing a cloud-native fine-grained authorization system requires a structured approach. We suggest separating our implementation into a list of several planes, each focused on a different iteration of the SDLC and architecture levels. These planes—Application, Configuration, Data, and Enforcement—serve different functions, from handling user interactions to managing data security and enforcing policies. Let’s dive in -
The Application Plane
The Application Plane serves as the foundational layer of the authorization layer, designed to align closely with common product architectures, integrating authorization directly into the software development life cycle (SDLC). Here’s how we modeled the key components of this plane in Permit.io:
- Workspaces/Organizations : These act as the main namespace or administrative domain within the authorization system, analogous to a development organization in the SDLC. Generally, each account should have one workspace to maintain a clear organizational structure.
- Projects : Projects within a workspace mirror products and can be thought of as equivalent to code repositories. Each project holds its unique configuration and policies driven by specific product requirements.
- Environments : Similar to branches in a code repository, environments within a project manage the deployment and testing of policies in different stages, such as development or production.
- Members : These are the users involved in managing and operating the authorization system, including developers, security engineers, and product managers, who handle permissions and policy configurations.
Flow and User Interaction
In the SDLC timeline, the Application Plane is relatively static but critical as it establishes the policy management and deployment structure. Interaction with the Application Plane is facilitated through various methods:
- User Interfaces (UIs): UIs allow non-technical stakeholders, such as product managers, to easily manage and navigate the structure of projects and environments. While costly to create, this type of accessibility is vital for fostering an inclusive environment where project configurations can be adjusted without deep technical knowledge.
- GitOps Workflows : GitOps provides a declarative way to manage infrastructure and configurations using Git. It allows for transparent version-controlled environments that align with DevOps practices, making it easier for teams to track changes and roll back if necessary.
- APIs : APIs offer developers the flexibility to manage projects and environments programmatically, integrate with existing CI/CD pipelines, and automate repetitive tasks. This is particularly useful for dynamically creating environments that align with feature branches or specific testing needs.
While developing these capabilities in-house can be resource-intensive and complex, our approach at Permit was to provide these capabilities out of the box, ensuring that developers can choose the most suitable method for their needs without the overhead of building and maintaining these systems.
The Configuration Plane
The Configuration Plane is central to defining and managing the rules and policies that govern access within an application. This plane is essentially about translating the complex requirements of permissions and roles into a structured, stateless configuration that integrates seamlessly with the application's codebase. Let’s go over this plane’s components:
-
Resources:
These are the various types of application assets requiring permission management. Each resource type specifies possible actions that users might undertake.- Resource Relationships: Descriptions of potential connections between resources to facilitate permission derivation.
-
Roles:
User roles within the application, both at the system and resource levels.- Role Derivations: Mechanisms to extend permissions from one role to another by using relationships.
Attributes: These are specifications that define the characteristics of resources and users, which can be used to set conditions for access permissions.
Condition sets - the ability to build conditions to match users or resources in a set. These conditions use the attribute configuration to configure
match
conditions.Policy: These are the actual rules that determine whether access is allowed or denied based on roles, resources, and condition sets.
Custom Policies: Advanced policy declarations using specific policy languages (like Rego or Cedar). Used mostly in very advanced use cases.
Data Fetchers: These synchronize data from the application to the authorization system, crucial for maintaining current and applicable permission settings.
URL Mapping: This feature links API endpoints directly to resource definitions, streamlining the integration of authorization layers with application APIs.
Flow and User Interaction
Managing the Configuration Plane can be approached through multiple methods, each offering distinct advantages and catering to different organizational needs:
- UI : Ideal for non-developer stakeholders such as product managers who need to configure permissions easily and visually. This method enhances accessibility and reduces complexity for users without deep technical knowledge.
- Infrastructure as Code : Organizations committed to a code-centric SDLC can manage permissions via tools like the Permit Terraform provider, integrating permission management directly into their development processes.
- Raw Policy as Code : For scenarios where customization needs exceed what is feasible through the UI, policies can be directly authored and managed in policy code files within version control systems.
As with the application plane, at Permit.io, we tried to enable developers to adopt the method that best fits their workflow. We support a hybrid approach, recommending the use of the UI for standard role-based and attribute-based access control configurations while accommodating more complex or stringent rules through custom policy code.
The Data Plane
As the configuration plane defines the application's authorization layer from the product requirement perspective, the data plane handles the dynamic elements of application data that influence policy decisions. It bridges the static configurations set in the Configuration Plane and the real-time enforcement of those policies in the Enforcement Plane. Let’s go over its components:
- Users : The core element of the Data Plane, representing individuals who interact with the application. User data is often synchronized from identity providers that manage authentication.
- Attributes : Attributes associated with users can be pivotal in determining the outcome of policy decisions and influencing access control based on dynamic user data.
- Tenants : Representing groups of users, tenants facilitate segmented policy enforcement, similar to organizational units in traditional IAM systems or accounts in cloud-native architectures.
- Role Assignments : These define the relationships between users and their roles within the application and can extend to relationships between users and specific resource instances.
- Resource Instances : These are specific instances of resources defined in the Configuration Plane, typically identified by unique identifiers.
- Relationship Tuples : These tuples create a graph of relationships between individual instances, aiding in the derivation of permissions between users and resources.
Flow and User Interaction
The management and synchronization of data within the Data Plane are continuous processes, tightly integrated with the application's lifecycle. Data can be handled in various ways:
- API/SDK : The most straightforward method for syncing data is through the integration of SDKs or direct API calls from within the application code. This approach ensures that data remains consistent with the application's state and development cycle.
- UI : Data can also be managed via a user interface, which is especially useful for non-developer stakeholders who need to interact with the data for configuration or testing purposes. However, direct manipulations in the UI should be used judiciously in production environments to maintain data integrity.
- Policy Engine APIs : For advanced data synchronization scenarios, data fetchers or direct policy engine API calls can be used. These are particularly useful in large-scale environments where data needs to be consistently and automatically updated.
Permit.io supports API/SDK integrations and provides UI capabilities that allow for easy data manipulation and testing. For environments where data synchronization needs to be automated and consistent, we utilize its OPAL to link data directly with the policy engines, maintaining a single source of truth and enhancing the reliability of policy enforcement.
The Enforcement Plane
The Enforcement Plane is where all the preparatory work done in the other planes comes into action. It is the most dynamic aspect of a cloud-native authorization system, responsible for the real-time application of configured policies. This plane ensures that the authorization decisions, such as allowing or denying user actions, are enforced consistently and accurately across the application. Let’s go over how its components are modeled in Permit:
-
Check Function(
permit.check()
):
This is the core component of the Enforcement Plane in Permit. It evaluates whether a particular action by a user on a resource should be allowed or denied based on the policies. The function considers several elements to make this decision:- User : Includes the user ID and, optionally, user attributes.
- Action : Specifies the operation the user is attempting.
- Resource : Identifies the type or ID of the resource involved and, optionally, resource attributes.
- Context : Any additional data that might be relevant for evaluating the policy decision.
Data Filtering : This function assists in filtering data according to the policy evaluation. It is particularly useful in applications where data visibility depends on user permissions, although it typically requires custom policy code to implement.
Local PDP APIs : These APIs fetch necessary data from the local policy decision point to aid the application in making informed authorization decisions.
Flow and Integration
The enforcement logic is embedded within the application code in the software development life cycle. It acts as the gatekeeper, controlling what users can or cannot do within the application based on the policies defined in earlier planes.
At Permit.io, enforcement is integrated using SDKs or by direct calls to the policy decision point. This integration allows developers to directly incorporate permission checks and data filtering mechanisms into the application flow.
Conclusion
Crafting an effective cloud-native authorization system is a complex yet crucial task. Through our experience dealing with cloud-native authorization in Permit.io and based on the way we modeled our own system, we have dissected the processes that contribute to building a strong authorization framework that is scalable, flexible, and secure.
The integration of continuous integration and deployment (CI/CD), meticulous testing, and precise modeling are a must when it comes to developing an authorization system that not only meets the current demands but is also prepared for future challenges. Each component—from the Application Plane through to the Enforcement Plane—plays a vital role in ensuring that the system operates seamlessly and adheres to strict security standards.
Whether you are a developer, a security engineer, or a product manager, understanding and implementing these facets will empower you to build and maintain a strong system.
Posted on May 13, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.