https://www.openpolicyagent.org/ * Docs * Download * Ecosystem * Security * Support * Community * Play * Blog * Twitter Twitter * Slack Slack * GitHub GitHub Policy-based control for cloud native environments Flexible, fine-grained control for administrators across the stack [banner-ima] [banner-ima] Stop using a different policy language, policy model, and policy API for every product and service you use. Use OPA for a unified toolset and framework for policy across the cloud native stack. Whether for one service or for all your services, use OPA to decouple policy from the service's code so you can release, analyze, and review policies (which security and compliance teams love) without sacrificing availability or performance. Created By [styra-logo] OPA is now maintained by Styra and a large community of contributors [gray-top] Declarative Policy Context-aware, Expressive, Fast, Portable * [policyIcon] Kubernetes * [envoy-squa] Envoy * [policyIcon] Application Ensure all images come from a trusted registry Stop ingresses from using the same host name [input] { "apiVersion": "admission.k8s.io/v1beta1", "kind": "AdmissionReview", "request": { "kind": { "group": "", "kind": "Pod", "version": "v1" }, "object": { "metadata": { "name": "myapp" }, "spec": { "containers": [ { "image": "nginx", "name": "nginx-frontend" }, { "image": "mysql", "name": "mysql-backend" } ] } } } } [module] package kubernetes.admission import future.keywords deny contains msg if { input.request.kind.kind == "Pod" some container in input.request.object.spec.containers image := container.image not startswith(image, "hooli.com/") msg := sprintf("image '%s' comes from untrusted registry", [image]) } [output] { "deny": [ "image 'mysql' comes from untrusted registry", "image 'nginx' comes from untrusted registry" ] } Open In Playground [playground] [input] { "apiVersion": "admission.k8s.io/v1beta1", "kind": "AdmissionReview", "request": { "kind": { "group": "extensions", "kind": "Ingress", "version": "v1beta1" }, "object": { "metadata": { "name": "prod", "namespace": "bar" }, "spec": { "rules": [ { "host": "initech.com", "http": { "paths": [ { "backend": { "serviceName": "banking", "servicePort": 443 }, "path": "/finance" } ] } } ] } } } } [module] package kubernetes.admission import future.keywords # Reject any ingress with the same host as an existing ingress deny contains msg if { input.request.kind.kind == "Ingress" # iterate over all hosts in input Ingress some rule in input.request.object.spec.rules newhost := rule.host # iterate over all existing ingresses and all hosts for each ingress some namespace, name some oldrule in data.kubernetes.ingresses[namespace][name].spec.rules oldhost := oldrule.host # reject if the oldhost and the newhost are the same newhost == oldhost msg := sprintf("ingress host conflicts with ingress %s/%s", [namespace, name]) } [output] { "deny": [] } Open In Playground [playground] Allow everyone to run GET on /pets Allow frontend service to run GET on /pets/owners [input] { "attributes": { "destination": { "address": { "Address": { "SocketAddress": { "PortSpecifier": { "PortValue": 8000 }, "address": "172.17.0.8" } } } }, "request": { "http": { "headers": { ":authority": "example-app", ":method": "GET", ":path": "/pets", "user-agent": "Wget", "x-forwarded-proto": "http", "x-request-id": "4d75f8d5-983f-4ec5-92bb-d27e620b4921" }, "host": "example-app", "id": "8910035251488566126", "method": "GET", "path": "/pets", "protocol": "HTTP/1.1" } }, "source": { "address": { "Address": { "SocketAddress": { "PortSpecifier": { "PortValue": 54292 }, "address": "172.17.0.6" } } } } }, "parsed_path": [ "pets" ] } [module] package envoy.authz import future.keywords # allow all GET requests to /pets default allow := false allow if { input.attributes.request.http.method == "GET" input.attributes.request.http.path == "/pets" } [output] { "allow": true } Open In Playground [playground] [input] { "attributes": { "destination": { "address": { "Address": { "SocketAddress": { "PortSpecifier": { "PortValue": 8000 }, "address": "172.17.0.8" } } } }, "request": { "http": { "headers": { ":authority": "example-app", ":method": "GET", ":path": "/pets/owners", "user-agent": "Wget", "x-forwarded-client-cert": "By=spiffe://domain.test/db-server;Hash=331f2f4eb2a1729fa4e0f46c8c663786098151ae571fed7b66680e970f6a0f94;URI=spiffe://domain.test/frontend", "x-forwarded-proto": "http", "x-request-id": "4d75f8d5-983f-4ec5-92bb-d27e620b4921" }, "host": "example-app", "id": "8910035251488566126", "method": "GET", "path": "/pets/owners", "protocol": "HTTP/1.1" } }, "source": { "address": { "Address": { "SocketAddress": { "PortSpecifier": { "PortValue": 54292 }, "address": "172.17.0.6" } } } } }, "parsed_path": [ "pets", "owners" ] } [module] package envoy.authz import future.keywords import input.attributes.request.http as http_request # allow only the frontend service to GET /pets/owners default allow := false allow if { http_request.path == "/pets/owners" http_request.method == "GET" source_spiffe_id == "spiffe://domain.test/frontend" } source_spiffe_id := client_id if { [_, _, uri_type_san] := split(http_request.headers["x-forwarded-client-cert"], ";") [_, client_id] := split(uri_type_san, "=") } [output] { "allow": true, "source_spiffe_id": "spiffe://domain.test/frontend" } Open In Playground [playground] Only the pet's owner can update that pet's information Only employees, authenticated with a JWT, can see already adopted pets [input] { "method": "PUT", "owner": "bob@hooli.com", "path": [ "pets", "pet113-987" ], "user": "alice@hooli.com" } [module] package application.authz import future.keywords # Only owner can update the pet's information # Ownership information is provided as part of OPA's input default allow := false allow if { input.method == "PUT" some petid input.path = ["pets", petid] input.user == input.owner } [output] { "allow": false } Open In Playground [playground] [input] { "pet_list": [ { "breed": "St. Bernard", "name": "Cujo", "up_for_adoption": false }, { "breed": "Collie", "name": "Lassie", "up_for_adoption": true } ], "token": "eyJ1IjoiSFMyNTYiLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWxpY2UiLCJlbXBsb3llZSI6dHJ1ZSwibmFtZSI6IkFsaWNlIFNtaXRoIn0.vMBYEW8VK9XM7yPkKTu1C3Gy1tOq1A0d4-xYMkkRpEc" } [module] package application.authz import future.keywords # Everyone can see pets up for adoption allowed_pets contains pet if { some pet in input.pet_list pet.up_for_adoption } # Employees can see all pets. allowed_pets contains pet if { [_, payload, _] := io.jwt.decode(input.token) payload.employee some pet in input.pet_list } [output] { "allowed_pets": [ { "breed": "Collie", "name": "Lassie", "up_for_adoption": true }, { "breed": "St. Bernard", "name": "Cujo", "up_for_adoption": false } ] } Open In Playground [playground] Declarative Policy Context-aware, Expressive, Fast, Portable [policyIcon] Kubernetes [downArrow] [policyIcon] Envoy [downArrow] [policyIcon] Application [downArrow] [policyIcon] Kubernetes Ensure all images come from a trusted registry Stop ingresses from using the same host name [input] { "apiVersion": "admission.k8s.io/v1beta1", "kind": "AdmissionReview", "request": { "kind": { "group": "", "kind": "Pod", "version": "v1" }, "object": { "metadata": { "name": "myapp" }, "spec": { "containers": [ { "image": "nginx", "name": "nginx-frontend" }, { "image": "mysql", "name": "mysql-backend" } ] } } } } [module] package kubernetes.admission import future.keywords deny contains msg if { input.request.kind.kind == "Pod" some container in input.request.object.spec.containers image := container.image not startswith(image, "hooli.com/") msg := sprintf("image '%v' comes from untrusted registry", [image]) } [output] { "deny": [ "image 'mysql' comes from untrusted registry", "image 'nginx' comes from untrusted registry" ] } Open In Playground [playground] [input] { "apiVersion": "admission.k8s.io/v1beta1", "kind": "AdmissionReview", "request": { "kind": { "group": "extensions", "kind": "Ingress", "version": "v1beta1" }, "object": { "metadata": { "name": "prod", "namespace": "bar" }, "spec": { "rules": [ { "host": "initech.com", "http": { "paths": [ { "backend": { "serviceName": "banking", "servicePort": 443 }, "path": "/finance" } ] } } ] } } } } [module] package kubernetes.admission import future.keywords # Reject any ingress with the same host as an existing ingress deny contains msg if { input.request.kind.kind == "Ingress" # iterate over all hosts in input Ingress some rule in input.request.object.spec.rules newhost := rule.host # iterate over all existing ingresses and all hosts for each ingress some namespace, name some oldrule in data.kubernetes.ingresses[namespace][name].spec.rules oldhost := oldrule.host # reject if the oldhost and the newhost are the same newhost == oldhost msg := sprintf("ingress host conflicts with ingress %s/%s", [namespace, name]) } [output] { "deny": [] } Open In Playground [playground] [policyIcon] Envoy Allow everyone to run GET on /pets Allow frontend service to run GET on /pets/owners [input] { "attributes": { "destination": { "address": { "Address": { "SocketAddress": { "PortSpecifier": { "PortValue": 8000 }, "address": "172.17.0.8" } } } }, "request": { "http": { "headers": { ":authority": "example-app", ":method": "GET", ":path": "/pets", "user-agent": "Wget", "x-forwarded-proto": "http", "x-request-id": "4d75f8d5-983f-4ec5-92bb-d27e620b4921" }, "host": "example-app", "id": "8910035251488566126", "method": "GET", "path": "/pets", "protocol": "HTTP/1.1" } }, "source": { "address": { "Address": { "SocketAddress": { "PortSpecifier": { "PortValue": 54292 }, "address": "172.17.0.6" } } } } }, "parsed_path": [ "pets" ] } [module] package envoy.authz import future.keywords # allow all GET requests to /pets default allow := false allow if { input.attributes.request.http.method == "GET" input.attributes.request.http.path == "/pets" } [output] { "allow": true } Open In Playground [playground] [input] { "attributes": { "destination": { "address": { "Address": { "SocketAddress": { "PortSpecifier": { "PortValue": 8000 }, "address": "172.17.0.8" } } } }, "request": { "http": { "headers": { ":authority": "example-app", ":method": "GET", ":path": "/pets/owners", "user-agent": "Wget", "x-forwarded-client-cert": "By=spiffe://domain.test/db-server;Hash=331f2f4eb2a1729fa4e0f46c8c663786098151ae571fed7b66680e970f6a0f94;URI=spiffe://domain.test/frontend", "x-forwarded-proto": "http", "x-request-id": "4d75f8d5-983f-4ec5-92bb-d27e620b4921" }, "host": "example-app", "id": "8910035251488566126", "method": "GET", "path": "/pets/owners", "protocol": "HTTP/1.1" } }, "source": { "address": { "Address": { "SocketAddress": { "PortSpecifier": { "PortValue": 54292 }, "address": "172.17.0.6" } } } } }, "parsed_path": [ "pets", "owners" ] } [module] package envoy.authz import future.keywords import input.attributes.request.http as http_request # allow only the frontend service to GET /pets/owners default allow := false allow if { http_request.path == "/pets/owners" http_request.method == "GET" source_spiffe_id == "spiffe://domain.test/frontend" } source_spiffe_id = client_id if { [_, _, uri_type_san] := split(http_request.headers["x-forwarded-client-cert"], ";") [_, client_id] := split(uri_type_san, "=") } [output] { "allow": true, "source_spiffe_id": "spiffe://domain.test/frontend" } Open In Playground [playground] [policyIcon] Application Only the pet's owner can update that pet's information Only employees, authenticated with a JWT, can see already adopted pets [input] { "method": "PUT", "owner": "bob@hooli.com", "path": [ "pets", "pet113-987" ], "user": "alice@hooli.com" } [module] package application.authz import future.keywords # Only owner can update the pet's information # Ownership information is provided as part of OPA's input default allow := false allow if { input.method == "PUT" some petid input.path = ["pets", petid] input.user == input.owner } [output] { "allow": false } Open In Playground [playground] [input] { "pet_list": [ { "breed": "St. Bernard", "name": "Cujo", "up_for_adoption": false }, { "breed": "Collie", "name": "Lassie", "up_for_adoption": true } ], "token": "eyJ1IjoiSFMyNTYiLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWxpY2UiLCJlbXBsb3llZSI6dHJ1ZSwibmFtZSI6IkFsaWNlIFNtaXRoIn0.vMBYEW8VK9XM7yPkKTu1C3Gy1tOq1A0d4-xYMkkRpEc" } [module] package application.authz import future.keywords # Everyone can see pets up for adoption allowed_pets contains pet if { some pet in input.pet_list pet.up_for_adoption } # Employees can see all pets. allowed_pets contains pet if { [_, payload, _] := io.jwt.decode(input.token) payload.employee some pet in input.pet_list } [output] { "allowed_pets": [ { "breed": "Collie", "name": "Lassie", "up_for_adoption": true }, { "breed": "St. Bernard", "name": "Cujo", "up_for_adoption": false } ] } Open In Playground [playground] Declarative. Express policy in a high-level, declarative language that promotes safe, performant, fine-grained controls. Use a language purpose-built for policy in a world where JSON is pervasive. Iterate, traverse hierarchies, and apply 150+ built-ins like string manipulation and JWT decoding to declare the policies you want enforced. Context-aware. Leverage external information to write the policies you really care about. Stop inventing roles that represent complex relationships that years down the road no one will understand. Instead, write logic that adapts to the world around it and attach that logic to the systems that need it. Learn More Kubernetes Envoy Terraform Kafka SQL Linux [curve] Architectural Flexibility Balance integration, availability, consistency Daemon Service [Deployment] Policy (Rego) Data (JSON) Deploy OPA as a separate process on the same host as your service. Integrate OPA by changing your service's code, importing an OPA-enabled library, or using a network proxy integrated with OPA. Library Service [logo] [Deployment] Policy (Rego) Data (JSON) Embed OPA policies into your service. Integrate OPA as a Go library that evaluates policy, or integrate a WebAssembly runtime and use OPA to compile policy to WebAssembly instructions. Learn More Daemon Library [curve-up] Tools for Policy Authoring IDEs, Sharing, Profiling, Testing, Coverage [tools-play] Rego Playground for Policy Sharing [tools-vsco] VS Code for Policy Authoring [tools-cli] CLI and REPL for complete control OPA embraces policy-as-code, complete with tools that help people use and understand the policies they put in place. Integrated development environments, testing, profiling, coverage, automated performance tuning, and hot reloading aren't just things you need for programming--you need them for policy too, and OPA delivers. Learn More VS Code Rego Playground General Tooling OPA GitHub Open Policy Agent is a Cloud Native Computing Foundation Graduated project. [cloud-nati] (c) 2024 Open Policy Agent contributors. Licensed under the Apache License, Version 2.0. See the contributing documentation for information about contributing. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page.