From cf7e272a2e2e21201dabc5ca669f897aa5bef7f2 Mon Sep 17 00:00:00 2001 From: Ashutosh Narkar Date: Tue, 24 Sep 2019 10:59:37 -0700 Subject: [PATCH] OPA security assessment This change includes the documents related to the OPA self-assessment and OPA recommendations. Signed-off-by: Ashutosh Narkar --- assessments/projects/opa/README.md | 111 ++++ .../projects/opa/docs/document_model.png | Bin 0 -> 22460 bytes .../projects/opa/docs/request_response.png | Bin 0 -> 31072 bytes assessments/projects/opa/self-assessment.md | 564 ++++++++++++++++++ 4 files changed, 675 insertions(+) create mode 100644 assessments/projects/opa/README.md create mode 100644 assessments/projects/opa/docs/document_model.png create mode 100644 assessments/projects/opa/docs/request_response.png create mode 100644 assessments/projects/opa/self-assessment.md diff --git a/assessments/projects/opa/README.md b/assessments/projects/opa/README.md new file mode 100644 index 000000000..e60e283d9 --- /dev/null +++ b/assessments/projects/opa/README.md @@ -0,0 +1,111 @@ +# OPA Security Assessment + +Completed: September 2019 + +Security reviewers: Justin Cappos, Justin Cormack, Brandon Lum, Sarah Allen + +Project security lead: Ash Narkar + +* Source code: [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) +* Web site: [openpolicyagent.org](https://www.openpolicyagent.org/) + +## Background + +Deployed cloud native applications have policies that must be correctly enforced +for security reasons. The desired overall policy may span many different pieces +of software and configurations. Open Policy Agent (OPA) seeks to consolidate +and control policy configuration in a consistent manner across the different +components of a cloud native deployment. + +_Maturity_ + +The core technology is in production with a broad set of companies, including +Netflix, Cloudflare, Chef, and others ([ADOPTERS.md](https://github.com/open-policy-agent/opa/blob/master/ADOPTERS.md)). +Code contributions are primarily from Styra, yet there has been community participation +from a wide range of adopters ([77 contributors](https://github.com/open-policy-agent/opa/graphs/contributors), +with Chef engineer in top 4 and Google engineer co-authoring RegoV2 specification). + +## Summary + +**Design**: The goal of OPA is to centralize and abstract out policy +functionality to make it easier to manage policy holistically. +Policies are written in a custom declarative language (“Rego”) which is read by +OPA agents that are queried by the software performing checks. The OPA agent +may return rich information about the status of a policy check. + +**Analysis**: There is a clear benefit for adopters who have heterogeneous +infrastructure or high rate of change where lack of policy enforcement would +present significant business risk. The added complexity of OPA is not trivial. +OPA enables “policy as code” and Rego policy expressions require the same care +to develop as any security critical code. OPA reduces risk by isolating policy +from business logic. Concise declarative policy has the potential to support +efficient review; however, that relies upon a reviewer who fully understands the +syntax and tools for effective validation. Hence usability and security around +Rego and interaction between aspects of OPA becomes more critical. + +All questions from reviewers were addressed in [self-assessment](self-assessment.md) +with non-critical issues captured as issues and noted below. + +## Recommendations + +Rego has a non-trivial learning curve and even in demos shows there is potential +for confusion. Examining the language’s potential for confusion and mitigating +situations where these problems arise would help to improve user security. + +### Recommendations to the project team + +1. ([OPA-1699](https://github.com/open-policy-agent/opa/issues/1699)) System +design and deployment guidance to include “gotchas” laying out potential issues +in a way that is more understandable to users. + + a. Common scenarios or design challenges, such as policy loading to handled + race/error conditions, initialization, and queries that require state + + b. Deployment Checklist: these so that one can check more holistically that + they are using OPA correctly before using it in prod. + +2. ([OPA-1700](https://github.com/open-policy-agent/opa/issues/1700)) Rego usability + + a. Make it more difficult for users to make policy-related errors by improving + OPA toolchain usability (testing, playground). Related Issues: [OPA-1697](https://github.com/open-policy-agent/opa/issues/1697) + and [OPA-1698](https://github.com/open-policy-agent/opa/issues/1698) + + b. Make it easier for integrators to understand the security ramifications of + different OPA deployment models, including models where the RESTful API + is used to manipulate policies + +3. Expand security response team to include participants outside of Styra + +4. Work toward better security by improving OPA tooling and Rego language + + a. OPA decision logs may contain sensitive information by default e.g. + when used as Kubernetes Admission Controller depending on the configuration + also secrets could be part of the decision logs. While [OPA configuration](https://www.openpolicyagent.org/docs/latest/management/#masking-sensitive-data) + allows specific data to be omitted, we recommend that OPA create mechanisms + to make it harder for users to accidentally leak secrets using OPA. + Related Issues: [OPA-1781](https://github.com/open-policy-agent/opa/issues/1781) + + +### Recommendations to CNCF + +The following recommendation are where help from the CNCF would assist OPA to +increase its effectiveness in cloud native security. + +* Conduct a study of user practices with OPA policies and improve tooling, +scan policies, and/or work with users to address common patterns of insecurity + + a. Are failure cases in deployment (e.g., loading policy from an + external system over HTTP) handled in practice? + Is the current guidance about setting up an external OPA service adequate? + + b. Is the method to load / add / change OPA policies appropriately secured + in prod deployments? Does this need to be automated better for users? + + c. Is the Rego language understandable enough that users can correctly specify + policies in a way that an attacker cannot find ways to subvert them? + Should errors most commonly caught with testing now be forced earlier + in the process? + +* Collect information from CNCF End User companies that integrate OPA into +software and recommend integrations where OPA would have the largest security +benefit for the cost. diff --git a/assessments/projects/opa/docs/document_model.png b/assessments/projects/opa/docs/document_model.png new file mode 100644 index 0000000000000000000000000000000000000000..ca4ffd4ffec0916ed68ebbf256a1fb02a37cddca GIT binary patch literal 22460 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 MP!unH|IzQi00K6HwEzGB literal 0 HcmV?d00001 diff --git a/assessments/projects/opa/self-assessment.md b/assessments/projects/opa/self-assessment.md new file mode 100644 index 000000000..954ca9010 --- /dev/null +++ b/assessments/projects/opa/self-assessment.md @@ -0,0 +1,564 @@ +# OPA security self-assessment + +April 24th 2019 + +*_Author_*: Ash Narkar ([@ashutosh-narkar](https://github.com/ashutosh-narkar)) + +*_Contributors/Reviewers_*: Justin Cappos (@JustinCappos), Brandon Lum (@lumjjb), +Robert Ficcaglia (@rficcaglia), Sarah Allen (@ultrasaurus) + +This document elaborates and explores the design goals for the Open Policy Agent (OPA) +as well as its security analysis to aid in the security assessment by CNCF SIG-Security. + +## Overview + +The Open Policy Agent (OPA) is a general-purpose policy engine that enables unified, +context-aware policy enforcement across the stack. OPA empowers administrators +with greater control and flexibility so that organizations can automate policy +enforcement at any layer and author policies that take external data or +context into account. + +## Background + +Every organization has unique policies that affect the entire stack. These +policies are vital to long term success because they codify important requirements +around cost, performance, security, legal regulation, and more. + +Enforcement of policy via human review is error-prone, yet common practice. +Organizations often rely on tribal knowledge and documentation to define policies +and ensure that they are enforced correctly. Some hard code policy decisions in +software. These approaches tightly couple the policy enforcement to other +required business logic in the underlying software. This practice: + +* complicates the release process + +* makes software policy compliance difficult to audit + +* makes it difficult to modify policies + +* limits visibility of policy throughout the system + +Systems frequently lack the flexibility and expressiveness required to automate +policy enforcement. Every system may have its own complex authorization logic +and as the number of systems grow, ensuring that authorization rules are being +accurately enforced becomes a hassle for administrators. + +A better model isolates policy as a separate component of the architecture just +like databases, monitoring, logging, messaging. And this decoupling makes it +possible to get better control and visibility of policy and security throughout +the system. Standards such as XACML promote a similar philosophy and this +document will [later](#related-projects-/-vendors) provide a comparison between +OPA and other authorization systems. + +### Goals + +OPA’s goal is to provide consistent policy enforcement. OPA helps disparate +services to author and enforce security policies using a common framework which +can be applied at whatever layer in the stack as needed by the target system. +OPA aims to policy-enable other projects and services, regardless of domain. +Additionally OPA targets to achieve the following: + +* Decouple policy enforcement from decision making + + * Services offload policy decisions to OPA by executing queries and enforce + the decision returned by OPA + +* Run at the edge to make policy decisions for host-local consumers + + * OPA was designed to run at the edge, eg. as a sidecar next to every + microservice. This helps to achieve high availability and low latency for + authorization decisions + +* Zero runtime dependencies + + * OPA stores policies and data in-memory + + * Alternatively, OPA can be configured to fetch policy and data from + external sources but this is optional + +* Multiple deployment models + + * Host-level daemon, sidecar, embed as a Go library + + * Allows for easy integrations with other systems + +### Non-Goals + +* OPA does not provide a control plane for management and distribution of +policies. However OPA does provide the necessary tools and APIs to build a +policy management framework + +* Provide a framework to issue identities to workloads. OPA does not +perform authentication + +### Intended Use + +For situations where your software service supports an authorization plugin +model or you can modify it to ask for authorization decisions from an external +service, you integrate your software service with the Open Policy Agent (OPA). +Every time your software needs an authorization decision, it asks OPA. You run +OPA on the same host as your software service (as a host-local daemon or sidecar) +so OPA shares fate with your software service, providing high-availability and +high-performance of authorization decision-making, even in the presence of +network failures. + +### Use cases + +Examples of common use cases for OPA: + +* Admission control in Kubernetes, Docker + +* Microservice API authorization + +* Unit testing Terraform plans + + * OPA makes it possible to write policies that test the changes Terraform is + about to make before it makes them + +* Access control for data in Ceph, Kafka, Minio, SQL, Elasticsearch + + * OPA provides fine-grained access control over Kafka topics + +* OPA enables authoring of custom security policies to protect data stored in +Ceph, Minio etc. + +### Operation + +With OPA, policy decisions are decoupled from applications and services so that +policy logic can be modified easily and upgraded on-the-fly without requiring +expensive, time consuming development and release cycles. + +OPA provides simple APIs to offload policy decisions from applications and +services. Policy decisions are computed by OPA and returned to callers as +structured data. Callers integrate with OPA by executing policy queries that +can include arbitrary input values. For example, an API gateway might supply +incoming API requests as input and expect boolean values +(representing allow/deny decisions) as output. On the other hand, a container +orchestrator might supply workload resources as input and expect a map of +clusters and weights to drive workload placement as output. In general, a +service will query OPA for a policy decision and then OPA based on the policies +and data it has access to, will evaluate the query and provide a decision back +to the service which then enforces the decision. + +![model](./docs/request_response.png) + +## Project Design + +### Data and Policies + +The primary unit of data in OPA is a document, which is similar to a JSON value. +Documents typically correspond to single, self-contained objects and are capable +of representing both primitive types (strings, numbers, booleans, and null) as +well as structured types (objects, and arrays). Document is a term that refers +to both the policy and data that may be provided by the user, and sometimes +refers to intermediate results that may be generated during evaluation. + +#### Base Documents + +Base documents contain static, structured data stored in memory and optionally +saved to disk for resiliency. A service will publish and update base documents +in order to describe its current state, and users can do the same to include +relevant data about the state of their own deployment context. Users can publish +and update base documents using OPA’s Data API. + +#### Policies + +At the core of OPA is a high-level declarative language Rego. Rego allows +administrators to enforce policies across multiple domains such as API +authorization, admission control, workload placement, storage, and networking. +OPA’s language is purpose-built for expressing policy decisions. The language +has rich support for processing complex data structures as well as performing +search and aggregation across context required for policy decisions. +The language also provides support for encapsulation and composition so that +complex policies can be shared and re-used. Finally, the language includes a +standard library of built-in functions for performing math operations, +string manipulation, date/time parsing, and more. + +Each Rego file defines a policy module which is a collection of rules that +describe the expected state of a service. Both the service and its users can +publish and update policy modules using OPA’s Policy API. + +#### Rules and Virtual Documents + +Virtual documents embody the results of evaluating the rules included in +policy modules. Virtual documents are computed when users publish new policy +modules, update existing modules, run queries, and when any relevant base +document is published or updated. Rules allow policy authors to write questions +with yes-no answers (that is, predicates) and to generate structured values from +raw data found in base documents as well as from intermediate data found in +other virtual documents. + +#### The *data* Document + +All documents pushed into OPA or computed by rules are nested under a built-in +root document named data. Since the data document includes both base and virtual +documents, it is possible to query for both at the same time. The easiest way to +illustrate this is to query for all of data at once. Note, OPA does NOT allow +base and virtual documents to overlap. For example, if you try to load a rule +that defines a virtual document at path a/b/c (which is already defined by a +base document), OPA will return an error. Similarly, if you try to load a base +document into a path that is already defined by a virtual document, OPA will +also return an error. + +#### The *input* Document + +In some cases, policies require input values. In addition to the built-in data +document, OPA also has a built-in input document. When you query OPA, you can set +the value of the input document. For example, when using OPA for HTTP API +authorization, the following information about the incoming HTTP request can be +provided to OPA as input: + +```json +{ + "method": "GET", + "path": "/servers/s2", + "user": "alice" +} +``` + +OPA can then evaluate a rule based on the policies and data it has access to +and the given input. + +The below figure illustrates OPA’s Data Model and covers the concepts discussed +in the previous section. + +![document](./docs/document_model.png) + +### Deployment + +OPA can be embedded as a Go library, or it can be deployed alongside the user’s +service – either directly as an operating system daemon or inside a container. +In this way, transactions will have low latency and availability will be +determined through shared fate with the user’s service. + +When OPA starts for the first time, it will not contain any policies or data. +Policies and data can be added, removed, and modified at any time. For example: +by deployment automation software or by administrators as needed. + +OPA has no runtime dependencies which means OPA does not need to connect to an +external DB or it does not need to talk to any external service once deployed +to make a policy decision. + +### Features + +Along with the core policy engine, OPA provides rich tooling to build, test, +debug policies. For example, there is a test framework to unit test policies, +interactive shell to write queries, a trace functionality which shows the steps +in the policy evaluation, expression profiler, IDE Integrations etc. + +Although OPA does not provide a control plane for management and distribution +of policies, it does provide management APIs that: + +1. Periodically download bundles of policy and data from remote HTTP servers + +2. Report status updates to remote HTTP servers + +3. Report decision logs to remote HTTP servers + +OPA’s [configuration guide](https://www.openpolicyagent.org/docs/latest/configuration/) +provides detailed information about how the above features can be enabled. + +To make it easy to author and try out policies using Rego, OPA has a [Rego Playground](https://play.openpolicyagent.org/). + +## Security analysis + +Organizations have to strike a fine balance between giving employees enough +power to deliver software to customers quickly but not too much that the +business suffers from security holes, financial liabilities, +and operational mistakes. + +The challenge with achieving least-privilege authorization +(not too many permissions and not too few) is the number, complexity, +dynamicity, and heterogeneity of the software systems that organizations +are embracing. A single organization may have thousands of software components +that require authorization. Each domain, vendor, and product has its own +authorization paradigm, expressiveness, and interface for administrators to +control those authorization policies. + +OPA provides a unified approach to authorization giving organizations +context-aware visibility and control over their authorization posture in +dynamic environments. Using mechanisms such as Admission Control, OPA provides +guardrails so that organizations can impart enough power to their employees to +promote rapid innovation without compromising on security and safety. + +OPA aims to solve the problem of fine-grained authorization in diverse +deployment models and technologies. It however does not tackle authentication. +OPA assumes that a user or service making a request is authenticated and then +attempts to answer the question *“What can the user or service do ?”*. + +### Attacker Motivations + +Today, many organizations follow a microservice-oriented architecture to design +and build their software systems and use the public cloud for deployment. +A typical public cloud account can have hundreds of microservices, thousands of +APIs, and possibly millions of resources. Because of the size and complexity and +ever-changing nature of the deployed services, an attacker can get access to +unauthorized resources and sensitive data by exploiting the simplistic static +policies that may have been enforced by the administrator. + +An attacker could: + +* Get access to a sensitive api endpoint. For example, an attacker can get +access to the payment gateway for a financial institution and cause permanent +damage + +* Change the behavior of the system + + * Modify how containers are configured and deployed + + * Change host-level permissions + +#### OPA Attack Surface + +Following are the vulnerabilities in OPA that an attacker could exploit: + +* When a user is setting up OPA for the first time, it does not contain any +policies or data. An attacker can access an unauthorized service while OPA is +still loading; however, a user would typically set this up with their +own policies. + +* By default, OPA does not restrict access to any of its REST API endpoints +that are used to fetch, create and update policy and data. It’s possible that +an attacker can corrupt the policy and data loaded into OPA, thereby bypassing +OPA’s authorization checks altogether + +* If OPA is deployed in an insecure environment, its effectiveness can be +compromised by an attacker + +* OPA can be configured to fetch policies and data from remote HTTP servers +using its *bundle* feature. The files inside the bundle are tar.gz compressed. +An attacker who has access to the remote server or MitM the connection between +OPA and the remote server. This can cause a Denial of Service (DoS) by +providing a bundle file that will consume the server’s memory and therefore +crash OPA or be used to add, remove, or delete OPA policies + +* OPA trusts the authenticity of the policies and data it is provided with. +An attacker can potentially feed corrupt policies and data to OPA + +* Policies that rely on existing status of cluster resources can be raced due +to the design of the validating webhook and eventual consistency model. +Attackers can potentially bypass these policies by crafting a series of +requests. Suggestions to warn against such policies - or a warning to require +routine checking during runtime may be required + +* If policies for different components interact, the user must author the +policies in such a way that there are not problematic interactions + +The following issues raised during the assessment have been addressed: + +* OPA decision logs may contain sensitive information like usernames, passwords +provided in the input to the policy. OPA now allows the user to mask sensitive +fields in the input from showing up in the decision logs by allowing the user +to define a policy that specifies the fields to mask + +* To make it easier for users to get familiar with Rego and also to make the +policy authoring experience convenient and less error prone, we +created [The Rego Playground](https://play.openpolicyagent.org/) and more +recently we’ve updated the [OPA documentation](https://www.openpolicyagent.org/docs/latest/policy-language/) +to make the policy examples interactive ! + +### Compensating Mechanisms + +* The OPA daemon can be configured to authenticate and authorize requests. This +could prove beneficial especially in scenarios where the deployment environment +is untrusted. More details on deploying OPA securely can be found [here](https://www.openpolicyagent.org/docs/latest/security/). +Additionally OAuth2.0 client credentials can be used to authenticate +OPA’s REST APIs. See [this](https://github.com/open-policy-agent/opa/issues/1205) +issue. + +* Since OPA does not contain any policies by default when started, it’s +recommended that the application fail-close in case of any non-200 response +from OPA. + +* To prevent an attacker from loading corrupt policies into OPA, users are +recommended to sign their policies using a mechanism that best suits their +use-case. Related issue [here](https://github.com/open-policy-agent/opa/issues/1757). + +* Since large bundles of policy and data have the potential to consume all of +the host’s memory, OPA puts a restriction on the size of bundles that can +be downloaded. + +## Secure development practices + +OPA has a well documented development workflow which can be +found [here](https://github.com/open-policy-agent/opa/blob/master/docs/devel/DEVELOPMENT.md). + +Some highlights: + +* All source code is checked into GitHub + +* Every pull request kicks off a build which performs unit tests, benchmark +tests and language best practices enforcement + +* Each pull request requires an approval from a core member before merging + + * The reviewer makes sure all checks and tests are passing + + * The reviewer pays close attention to any new exported methods or + interface definitions + + * The reviewer sees that the code follows standard coding practices such as + adding comments, grouping common logic in functions + + * Based on the changes made, reviewer sees that the docs are updated + accordingly + +* Pushes to master are forbidden by convention + +* OPA asserts that it meets the passing criteria in the [Core Infrastructure Initiative (CII) badging process](https://bestpractices.coreinfrastructure.org/en/projects/1768) + +* Release process is partially automated with manual portions assisted by scripts + +* More information about the release process can be found [here](https://github.com/open-policy-agent/opa/blob/master/docs/devel/RELEASE.md) + +* SHA-512 checksum files for release artifacts are not provided + +### Communication Channels + +Both new and experienced OPA users have multiple options to interact with the OPA community: + +* [OPA Slack](https://slack.openpolicyagent.org/) +* [Bi-Weekly OPA meetings](https://docs.google.com/document/d/1v6l2gmkRKAn5UIg3V2QdeeCcXMElxsNzEzDkVlWDVg8/edit?usp=sharing) + +Announcements about OPA releases, new features and general OPA updates are +shared through [OPA Slack](https://slack.openpolicyagent.org/) and [OPA blog](https://blog.openpolicyagent.org/). + + +#### Vulnerability Response Process + +Security vulnerabilities are reported by sending an email to +open-policy-agent-security@googlegroups.com. The OPA Response Team will send a +confirmation message to acknowledge that they have received the report and then +they will send additional messages to follow up once the issue has been +investigated. + +OPA Response Team: + +* Torin Sandall (@tsandall) +* Tim Hinrichs (@timothyhinrichs) +* Patrick East (@patrick-east) +* Ash Narkar (@ashutosh-narkar) + +### Ecosystem + +OPA has integrations available with more than 20 open-source projects. +Some of them are: + +* Kubernetes +* Docker +* Terraform +* Envoy +* Istio +* Kafka +* Ceph +* SQL +* Elasticsearch + +More information on each integration can be found on the [OPA Website](https://www.openpolicyagent.org/). + +## Roadmap + +The [OPA Roadmap](https://docs.google.com/presentation/d/16QV6gvLDOV3I0_guPC3_19g6jHkEg3X9xqMYgtoCKrs/edit?usp=sharing) +highlights planned and in-progress OPA features. Current features include: + +* Extended WebAssembly support for OPA. Work related to this effort can be found [here](https://github.com/open-policy-agent/opa/issues?q=is%3Aopen+is%3Aissue+label%3Awasm) + +* Update OPA’s documentation to address security concerns around OPA deployments + +* Policy language improvements + +* [Support for OAuth 2.0 client credentials for authenticating OPA’s management APIs](https://github.com/open-policy-agent/opa/issues/1205) + +## Appendix + +### Security Audit + +A third party security audit was performed by Cure53, the full report is +available [here](https://github.com/open-policy-agent/opa/blob/master/SECURITY_AUDIT.pdf). + +The issues that were discovered as part of the audit have been [tackled and resolved](https://github.com/open-policy-agent/opa/issues?q=label%3Aaugust-audit+is%3Aclosed). + +### CII Best Practices + +Currently, OPA is at the passing criteria in the [Core Infrastructure Initiative (CII) best practices badging program](https://github.com/coreinfrastructure/best-practices-badge/blob/master/doc/criteria.md) ([security details here](https://bestpractices.coreinfrastructure.org/en/projects/1768)). OPA endeavours to reach the silver tier by following best +practices and guidelines outlined by CII. + +### Case Studies + +OPA is a general-purpose policy engine that has multiple deployment and +integration models and is used in production by more than 20 companies +such as Netflix, Intuit, Capital One. + +#### REST API Authorization Example + +This example shows two simple rules that enforce an authorization policy on an +API that serves salary data. In English, the policy says that +*employees can see their own salary and the salary of any of their reports*. + +```ruby +allow { +input.method = "GET" +input.path = ["salary", employee_id] +input.user = employee_id +} + +allow { +input.method = "GET" +input.path = ["salary", employee_id] +input.user = data.management_chain[employee_id][_] +} +``` + +The first rule allows employees to ***GET*** their own salary. The rule shows +how you can use variables in rules. In that rule, ***employee_id*** is a +variable that will be bound to the same value across the last two expressions. + +The second rule allows employees to ***GET*** the salary of their reports. +The rule shows how you can access arbitrary context (e.g., JSON data) inside +the policy. The data may be loaded into the policy engine (and cached) or it +may be external and fetched dynamically. + +#### Kubernetes Admission Control Example + +This example shows how to enforce custom policies on Kubernetes objects +using OPA. The below policy prevents Ingress objects in different namespaces +from sharing the same hostname. + +```ruby +deny[msg] { + input.request.kind.kind == "Ingress" + host := input.request.object.spec.rules[_].host + ingress := data.kubernetes.ingresses[other_ns][other_ingress] + other_ns != input.request.namespace + ingress.spec.rules[_].host == host + msg := sprintf("invalid ingress host %q (conflicts with %v/%v)", [host, other_ns, other_ingress]) +} +``` + +This example shows how OPA can be used to enforce admission control decisions in +Kubernetes clusters on-the-fly without modifying or recompiling any Kubernetes +components. Also the *deny* rule returns a set illustrating Rego’s ability to +generate non-boolean policy decisions. + +More information on how OPA can be integrated with Kubernetes as an +Admission Controller can be found [here](https://www.openpolicyagent.org/docs/latest/kubernetes-introduction/). + +### Related Projects / Vendors + +* OPA’s policy language Rego, allows policy decisions that are more than +boolean values. In the Kubernetes Admission Control example above, OPA returns +a non-boolean decision, which helps to detect the violation and can be used +later for auditing etc. Another example would be a service querying OPA to +return the fields a user is allowed to see based on the user’s profile, +department or some other characteristic + +* OPA can be deployed next to the user’s service either as a host-level daemon +or sidecar, or, if building services in Go, OPA can be embedded as a library + +| Project | Open Source | Decentralized | Non-boolean Decisions | +| --- | --- | --- | --- | +| OPA | Apache 2 | Yes | Yes | +| Firebase Rules | No | Yes | No | +| HashiCorp Sentinel | No | Yes | No | +| OpenStack Congress | Apache 2 | No | Yes | +| Ladon (XACML) | Apache 2 | Yes | No |