PageSourceSearch

https://www.openpolicyagent.org/assets/js/8e2cd225.c9e61b79.js

js openpolicyagent.org collected 2026-09-24 08:32:09 UTC 25,936 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkopa_website=self.webpackChunkopa_website||[]).push([[39486],{20998:e=>{e.exports=JSON.parse('{"permalink":"/blog/rego-design-principle-1-syntax-should-reflect-real-world-policies-e1a801ab8bfb","source":"@site/blog/2020-03-04-rego-design-principle-1-syntax-should-reflect-real-world-policies-e1a801ab8bfb.md","title":"Rego design principle #1: Syntax should reflect real-world policies","description":"Banner image for Rego design principle number one blog post","date":"2020-03-04T00:00:00.000Z","tags":[],"hasTruncateMarker":false,"authors":[{"name":"Tim Hinrichs","page":{"permalink":"/blog/authors/timhinrichs"},"imageURL":"/img/blog/authors/timhinrichs.png","key":"timhinrichs"}],"frontMatter":{"title":"Rego design principle #1: Syntax should reflect real-world policies","authors":["timhinrichs"],"date":"2020-03-04T00:00:00.000Z","slug":"rego-design-principle-1-syntax-should-reflect-real-world-policies-e1a801ab8bfb"},"unlisted":false,"prevItem":{"title":"Rego Design Principle #2: Embrace hierarchical data","permalink":"/blog/rego-design-principle-2-embrace-hierarchical-data-8a4913bdfea1"},"nextItem":{"title":"KubeCon US 2019 Recap","permalink":"/blog/kubecon-us-2019-recap-3e60c70d633a"}}')},28453:(e,n,i)=>{i.d(n,{R:()=>s,x:()=>l});var a=i(96540);const o={},t=a.createContext(o);function s(e){const n=a.useContext(t);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:s(e.components),a.createElement(t.Provider,{value:n},e.children)}},32300:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>r,contentTitle:()=>l,default:()=>p,frontMatter:()=>s,metadata:()=>a,toc:()=>c});var a=i(20998),o=i(74848),t=i(28453);const s={title:"Rego design principle #1: Syntax should reflect real-world policies",authors:["timhinrichs"],date:new Date("2020-03-04T00:00:00.000Z"),slug:"rego-design-principle-1-syntax-should-reflect-real-world-policies-e1a801ab8bfb"},l=void 0,r={authorsImageUrls:[void 0]},c=[{value:"Refresher on OPA",id:"refresher-on-opa",level:2},{value:"Natural encoding of real-world policies",id:"natural-encoding-of-real-world-policies",level:2},{value:"If statements in Rego",id:"if-statements-in-rego",level:3},{value:"Rich policy decisions",id:"rich-policy-decisions",level:2},{value:"Rich Policy Decisions in Rego",id:"rich-policy-decisions-in-rego",level:3},{value:"Collaboration",id:"collaboration",level:2},{value:"Collaboration in Rego",id:"collaboration-in-rego",level:3},{value:"Summary",id:"summary",level:2}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",h2:"h2",h3:"h3",img:"img",li:"li",p:"p",pre:"pre",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.p,{children:(0,o.jsx)(n.img,{alt:"Banner image for Rego design principle number one blog post",src:i(64163).A+"",width:"445",height:"423"})}),"\n",(0,o.jsx)(n.p,{children:"Sometimes people ask why Rego, OPA's policy language, looks or behaves the way it does. Part of the answer is that Rego came about after having built two other general-purpose policy languages, and lessons learned from that process shaped this one. This multi-part post lays out the results of that journey \u2014 the key design principles for Rego, why they're important, and how they influenced the language."}),"\n",(0,o.jsx)(n.p,{children:"Related posts in the series:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"/blog/rego-design-principle-2-embrace-hierarchical-data-8a4913bdfea1",children:"Design principle #2: Embrace Hierarchical Data"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"/blog/rego-design-principle-3-optimize-performance-automatically-2d29ad3ce96d",children:"Design principle #3: Optimize Performance Automatically"})}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:'The first design principle holds that Rego\'s syntax "should NOT be designed as a general-purpose programming language that reads from disk, writes to network sockets, supports multi-threading, defines custom datastructures, etc."'}),"\n",(0,o.jsx)(n.h2,{id:"refresher-on-opa",children:"Refresher on OPA"}),"\n",(0,o.jsx)(n.p,{children:"OPA is a general purpose policy engine that separates policy decisions from the softw
1are services that enforce them. It bases decisions on input data, a Rego policy, and optionally external data reflecting real-world state (e.g., on-call schedules or resource ownership)."}),"\n",(0,o.jsx)(n.p,{children:"These functional requirements ensure that OPA has enough flexibility and generality to make context-aware decisions across a broad range of use-cases: admission control, API authorization, risk-analysis, data-filtering. OPA runs as a lightweight agent or library on the same server as the software service, thereby achieving both the availability and performance needed for policy decision-making in modern, cloud-native computing environments. It has been integrated with over 20 popular software systems and is at the time of writing an incubating project within the Cloud Native Computing Foundation."}),"\n",(0,o.jsx)(n.h2,{id:"natural-encoding-of-real-world-policies",children:"Natural encoding of real-world policies"}),"\n",(0,o.jsx)(n.p,{children:"The goal is that Rego should map closely onto plain-language rules and regulations. A useful litmus test: reading Rego aloud should sound close to the source documentation."}),"\n",(0,o.jsx)(n.p,{children:"This readability test is especially important because of the broad range of stakeholders who are responsible for policy: developers, operations, security, and compliance. The less of a translation there is from the PDFs and wikis the easier it is to believe that your Rego policies are correct, that operationally you're on solid ground, that your auditors will be convinced the policies do what they should, and that your security vulnerabilities are properly mitigated."}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:"A Rego policy is a collection of if statements"}),"\n"]}),"\n",(0,o.jsx)(n.h3,{id:"if-statements-in-rego",children:"If statements in Rego"}),"\n",(0,o.jsx)(n.p,{children:"Nearly every Rego statement functions as an if-statement, but with different proportions than typical programming languages:"}),"\n",(0,o.jsxs)(n.p,{children:['"Programming languages typically have small ',(0,o.jsx)(n.code,{children:"if"})," conditions and relatively large ",(0,o.jsx)(n.code,{children:"then"})," blocks... in Rego the ",(0,o.jsx)(n.code,{children:"if"})," condition is a potentially large block of expressions, and the ",(0,o.jsx)(n.code,{children:"then"}),' part is a single expression."']}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:'# Rego example.\n# An API call is allowed if the method is a GET\nallow {\n    input.method == "GET"\n}\n'})}),"\n",(0,o.jsx)(n.p,{children:"Multiple statements inside a rule are ANDed; ORs are expressed via multiple rules:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:'# Rego example.\n# An API call is allowed if the method is a GET\nallow {\n    input.method == "GET"\n}\n\n# An API call is allowed if the method is POST and\n#   the user is an admin\nallow {\n    # Only admins can create new objects\n    input.method == "POST"\n    input.user_is_admin == true\n}\n'})}),"\n",(0,o.jsx)(n.p,{children:"An equivalent JavaScript version, given for contrast:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-javascript",children:'// Not a Rego example.  A JavaScript example.\nfunction allow() {\n  return allow1() || allow2();\n}\n\nfunction allow1() {\n  return input.method == "GET";\n}\n\nfunction allow2() {\n  return input.method == "POST"\n    && input.is_admin == true;\n}\n'})}),"\n",(0,o.jsx)(n.p,{children:'Rego "has no need for explicit ANDs and ORs" within a rule, and includes "an explicit NOT operator." A self-documenting example:'}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:"# Rego example.\nallow {\n    operation_is_create\n    user_is_admin\n}\n"})}),"\n",(0,o.jsx)(n.h2,{id:"rich-policy-decisions",children:"Rich policy decisions"}),"\n",(0,o.jsxs)(n.p,{children:["Real-world policies sometimes make a decision as simple as ",(0,o.jsx)(n.code,{children:"allow"})," or ",(0,o.jsx)(n.code,{children:"deny"})," but often they go far beyond that. What about ",(0,o.jsx)(n.code,{children:"warn"})," or ",(0,o.jsx)(n.code,{children:"error"}),"? Or what if the decision is a rate-limit (number), a permitted hostname (
1a string), or the clusters to deploy an application (an array). Since OPA is a general-purpose policy engine (not an authorization engine) it needs to handle a rich collection of policy decisions."]}),"\n",(0,o.jsx)(n.p,{children:"Because inputs are typically JSON, decisions in Rego can likewise be any JSON type\u2014not just booleans."}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:"A Rego decision is a JSON document"}),"\n"]}),"\n",(0,o.jsx)(n.h3,{id:"rich-policy-decisions-in-rego",children:"Rich Policy Decisions in Rego"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"allow"})," and ",(0,o.jsx)(n.code,{children:"deny"})," are plain variables, not keywords, defaulting to ",(0,o.jsx)(n.code,{children:"true"}),":"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:"# Rego example.\nallow = true {\n    operation_is_create\n    user_is_admin\n}\n"})}),"\n",(0,o.jsx)(n.p,{children:"Non-boolean decisions work the same way:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:'risk = 100 {\n    input.method == "DELETE"\n}\n'})}),"\n",(0,o.jsx)(n.p,{children:"Partial sets can build up collections, such as error messages:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:'deny[msg] {\n    input.method == "DELETE"\n    not user_is_resource_owner\n    msg := "only the owner of a resource may delete it"\n}\n'})}),"\n",(0,o.jsx)(n.h2,{id:"collaboration",children:"Collaboration"}),"\n",(0,o.jsx)(n.p,{children:"Real world policies are decided upon by multiple individuals and even teams. The security team might put global requirements in place across all application development teams and each application development team might put policy in place for their app. The k8s cluster administrator puts global policies in place but also delegates policy responsibilities to namespace-level admins. Policy is by its nature a collaborative endeavor, and Rego should recognize and support that."}),"\n",(0,o.jsx)(n.p,{children:'At some level collaboration is supported simply because Rego is a text-based policy language (aka "policy-as-code"). Teams can check Rego policies into source control, and use peer-review to manage changes to it. We knew, however, that there are all too many examples where teams want to work more independently than that, e.g. putting global policies in place for an entire cluster and empowering team leads to manage policy for their portion of the cluster. That means that different teams should be able to write their policies independently from each other, and then combine those policies after the fact.'}),"\n",(0,o.jsx)(n.p,{children:"If different teams write different policies independently, they will inevitably end up with conflicts from time to time (e.g. one allows the decision and the other denys it). And so there must be a way to resolve those conflicts, based on a variety of different factors. Resolving conflicts is not always easy. It may depend on the teams involved, the kind of decision being made, the resource and its attributes, the time of day, and many other factors. Ergo the conflict resolution mechanism must be tantamount to a policy itself. Sometimes languages are designed to avoid the problem of conflict resolution by designing the language to not express conflicts at all. But this approach inevitably leads to problems because when two teams disagree in the real world and there is no way for them to express that disagreement in the policy language, they simply can't write the policy that they truly mean \u2014 the language is ambiguous in terms of the author's intent."}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:"Rego policies are composable; conflict resolution is a policy itself."}),"\n"]}),"\n",(0,o.jsx)(n.h3,{id:"collaboration-in-rego",children:"Collaboration in Rego"}),"\n",(0,o.jsx)(n.p,{children:"Each policy lives in a package:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:"package microservice.authorization\n"})}),"\n",(0,o.jsx)(n.p,{children:"Separate teams can define their own packages:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:"package developer\nallow { \u2026 }\n\u2026\n"})}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:"package security\nallow { \u2026 }\n\u2026\n"})}),"\n",(0,o.jsxs)(n.p,{children:["A combining policy can reference other packages via the ",(0,o.jsx)(n.code,{children:"data"})," keyword:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:"package main\nallow {\n    data.developer.allow\n    data.security.allow\n}\n"})}),"\n",(0,o.jsx)(n.p,{children:"A more nuanced conflict-resolution example, where security's decision takes precedence when it has an opinion:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-rego",children:"package main\n# allow if the security team allows (and does not deny)\nallow {\n    data.security.allow\n    not data.security.deny\n}\n# allow if the security team has no opinion and\n#   the developer team allows (and does not deny)\nallow {\n    not data.security.allow\n    not data.security.deny\n    data.developer.allow\n    not data.developer.deny\n}\n"})}),"\n",(0,o.jsx)(n.h2,{id:"summary",children:"Summary"}),"\n",(0,o.jsx)(n.p,{children:"The post recaps three requirements: policies are mostly if-statements and should read naturally; decisions can be any JSON value rather than just booleans; and composition/conflict-resolution use the same rule mechanics as ordinary policy logic."}),"\n",(0,o.jsx)(n.p,{children:"Further reading:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"/blog/rego-design-principle-2-embrace-hierarchical-data-8a4913bdfea1",children:"Design principle #2: Embrace Hierarchical Data"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"/blog/rego-design-principle-3-optimize-performance-automatically-2d29ad3ce96d",children:"Design principle #3: Optimize Performance Automatically"})}),"\n"]})]})}function p(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},64163:(e,n,i)=>{i.d(n,{A:()=>a});const a="data:image/webp;base64,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"}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.