PageSourceSearch

https://ratify.dev/assets/js/f525788e.f7c76a1e.js

js ratify.dev collected 2026-10-02 05:38:38 UTC 17,143 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkratify=globalThis.webpackChunkratify||[]).push([[3563],{28453(e,t,n){n.d(t,{R:()=>a,x:()=>o});var s=n(96540);const i={},r=s.createContext(i);function a(e){const t=s.useContext(r);return s.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),s.createElement(r.Provider,{value:t},e.children)}},40984(e,t,n){n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>a,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"quickstarts/gatekeeper-policy-authoring","title":"Gatekeeper Policy Authoring","description":"Ratify can be deployed behind admission","source":"@site/versioned_docs/version-1.3/quickstarts/gatekeeper-policy-authoring.md","sourceDirName":"quickstarts","slug":"/quickstarts/gatekeeper-policy-authoring","permalink":"/docs/1.3/quickstarts/gatekeeper-policy-authoring","draft":false,"unlisted":false,"editUrl":"https://github.com/ratify-project/ratify-web/blob/main/versioned_docs/version-1.3/quickstarts/gatekeeper-policy-authoring.md","tags":[],"version":"1.3","frontMatter":{},"sidebar":"tutorialSidebar","previous":{"title":"Developer getting started","permalink":"/docs/1.3/quickstarts/developer-getting-started"},"next":{"title":"Ratify CLI","permalink":"/docs/1.3/quickstarts/ratify-cli"}}');var i=n(74848),r=n(28453);const a={},o="Gatekeeper Policy Authoring",l={},c=[{value:"Rego References",id:"rego-references",level:2},{value:"Ratify Response in Rego",id:"ratify-response-in-rego",level:2},{value:"Sample Rego Policy",id:"sample-rego-policy",level:2}];function d(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,r.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.header,{children:(0,i.jsx)(t.h1,{id:"gatekeeper-policy-authoring",children:"Gatekeeper Policy Authoring"})}),"\n",(0,i.jsx)(t.p,{children:"Ratify can be deployed behind admission\ncontrollers such as Gatekeeper. When deployed in this manner Ratify will provide\nthe results of all verifiers back to Gatekeeper so that policies can be authored\nin rego."}),"\n",(0,i.jsx)(t.p,{children:"Rego policies utilizing external data are authored in exactly the same way as\ntraditional rego policies, but need to know the structure of the response from\nthe external data provider in order to take advantage of it."}),"\n",(0,i.jsx)(t.h2,{id:"rego-references",children:"Rego References"}),"\n",(0,i.jsx)(t.p,{children:"These are some helpful references for working with rego:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://www.openpolicyagent.org/docs/latest/policy-language/",children:"Rego Policy Language"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://www.openpolicyagent.org/docs/latest/policy-reference/",children:"Rego Policy Reference"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://www.openpolicyagent.org/docs/latest/policy-reference/#built-in-functions",children:"Rego Built-in Functions"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://open-policy-agent.github.io/gatekeeper/website/docs/externaldata",children:"Gatekeeper External Data"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://open-policy-agent.github.io/gatekeeper/website/docs/debug",children:"Gatekeeper Policy Debugging"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://play.openpolicyagent.org/",children:"Rego Playground"})}),"\n"]}),"\n",(0,i.jsx)(t.p,{children:"The rego playground is a great way to test out policies against responses from\nRatify. Use the steps from the Gatekeeper debugging reference to print the\nresponse from Ratify. This can then be used as the input for rego playground. In\nrego playground you can replace the external_data call with the input:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-rego",children:'package ratify\n# You would use the below when running the rego using external_data\nremote_data := response {\n  images := [img | img = input.review.object.spec.containers[_].image]\n  response := external_data({"provider": "ratify-provider", "keys": images})\n}\n# Use this to mimic the behavior of external_data within rego playground\nremote_data := response {\n  response := input\n}\n'})}),"\n",(0,i.jsx)(t.h2,{id:"ratify-response-in-rego",children:"Ratify Response in Rego"}),"\n",(0,i.jsx)(t.p,{children:"Let's assume we have a registry with the following image and associated\nartifacts:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{children:"localhost:5000/net-monitor:v1\n    - sbom\n    - notation signature\n    - cosign signature\n"})}),"\n",(0,i.jsx)(t.p,{children:"We will try to deploy the following pod spec:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-yaml",children:"apiVersion: v1\nkind: Pod\nmetadata:\n  name: test-pod\nspec:\n  containers:\n    - name: net-monitor\n      image: localhost:5000/net-monitor:v1\n    - name: missing-image\n      image: localhost:5000/missing-image:v1\n"})}),"\n",(0,i.jsx)(t.p,{children:"Note that one of the images listed in the pod spec does not actually exist in\nthe registry. This is to give an example of something that might generate an\nerror message in one of the response fields."}),"\n",(0,i.jsx)(t.p,{children:"Given the following rego snippet:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-rego",children:'package ratify\n# Get data from Ratify\nremote_data := response {\n  images := [img | img = input.review.object.spec.containers[_].image]\n  response := external_data({"provider": "ratify-provider", "keys": images})\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.code,{children:"remote_data"})," document contents would look something like this (NOTE: this\nis a sample response to explain the structure of the response data and the\nactual values of fields may differ):"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",children:'{\n  "errors": [\n    ["localhost:5000/missing-image:v1", "The subject did not exist"]\n  ],\n  "responses": [\n    [\n      "localhost:5000/net-monitor:v1",\n      {\n        "isSuccess": true,\n        "verifierReports": [\n          {\n            "artifactType": "application/x.example.sbom.v
10",\n            "isSuccess": true,\n            "message": "SBOM verification success. The contents is good.",\n            "name": "sbom",\n            "subject": "localhost:5000/net-monitor:v1"\n          },\n          {\n            "artifactType": "application/vnd.cncf.notary.signature",\n            "extensions": {\n              "Issuer": "CN=localhost:5000,O=Ratify,L=Seattle,ST=Washington,C=US",\n              "SN": "CN=localhost:5000,O=Ratify,L=Seattle,ST=Washington,C=US"\n            },\n            "isSuccess": true,\n            "message": "signature verification success",\n            "name": "notation",\n            "subject": "localhost:5000/net-monitor:v1"\n          },\n          {\n            "artifactType": "application/vnd.dev.cosign.artifact.sig.v1+json",\n            "isSuccess": true,\n            "message": "cosign verification success. valid signatures found",\n            "name": "cosign",\n            "subject": "localhost:5000/net-monitor:v1"\n          }\n        ]\n      }\n    ]\n  ],\n  "status_code": 200,\n  "system_error": ""\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["Most of the response structure is dictated by the Gatekeeper external data\n",(0,i.jsx)(t.a,{href:"https://open-policy-agent.github.io/gatekeeper/website/docs/externaldata#api-v1alpha1",children:"ProviderResponse"}),"\ninterface. This structure includes:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"errors"}),": An array of 0-N elements where N is typically the number of subjects\nfrom the external data request. This array contains nested arrays where each\nnested array contains the subject as the first element and an error message as\nthe second element."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"responses"}),": An array of 0-N elements where N is typically the number of\nsubjects from the external data request. This array contains nested arrays\nwhere each nested array contains the subject as the first element and a\nvalue as the second element. The type of the value is dictated by the external\ndata provider."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"status_code"}),": The HTTP status code returned from the request to the external\ndata provider."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"system_error"}),": A system level error that can either be returned by the\nexternal data provider or result from some communication issue with the\nexternal data provider."]}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["An array of each\n",(0,i.jsx)(t.code,{children:"VerifyResult"})," for a subject is included in the ",(0,i.jsx)(t.code,{children:"response"})," field. This allows the policy to be entirely\nauthored in the rego using all the results from the individual verifiers."]}),"\n",(0,i.jsxs)(t.p,{children:["Each verifier produces a ",(0,i.jsx)(t.code,{children:"VerifierResult"})," with the following potential fields:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'type VerifierResult struct {\n\tSubject       string           `json:"subject,omitempty"`\n\tIsSuccess     bool             `json:"isSuccess"`\n\tName          string           `json:"name,omitempty"`\n\tMessage       string           `json:"message,omitempty"`\n\tExtensions    interface{}      `json:"extensions,omitempty"`\n\tNestedResults []VerifierResult `json:"nestedResults,omitempty"`\n\tArtifactType  string           `json:"artifactType,omitempty"`\n}\n'})}),"\n",(0,i.jsx)(t.p,{children:"The most commonly used fields are:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"Name"}),": The name of the verifier"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"IsSuccess"}),": The result of the verifier"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"Message"}),": A message about the success or failure of the verifier"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"Extensions"}),": Additional metadata added to the result by the verifier"]}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.code,{children:"Extensions"})," field allows the verifiers to include additional metadata as\npart of the response to use for policy evaluation (for example, the notation\nverifier includes the Issuer and SubjectName from the certificate as an extension field).\nCurrently, the best way to determine the extension fields for a verifier is to\ncheck the source for that verifier."]}),"\n",(0,i.jsxs)(t.p,{children:["When Ratify is verifying against a notation signature, there might be ambiguity\nin the verifierReport from notation verifier. Notation verifier introduces\na ",(0,i.jsx)(t.a,{href:"https://github.com/notaryproject/notaryproject/blob/main/specs/trust-store-trust-policy.md#trust-policy",children:"trust policy"})," component in the rc.1 release. Users\ncould control the verification level and scopes applied to specified artifact. There\ncould be 2 scenarios needed to pay attention:"]}),"\n",(0,i.jsxs)(t.ol,{children:["\n",(0,i.jsx)(t.li,{children:"If users forgot to set up scopes\nfor some repositories/artifacts, then the verification result from notation verifier will be an error saying the policy is missing."}),"\n",(0,i.jsxs)(t.li,{children:["If users set the verification level in trust policy to any value except ",(0,i.jsx)(t.code,{children:"strict"}),",\nthen the notation verification result might be a success even though some underlying\nvalidation failed. Check the verification level provided in notation for more information: ",(0,i.jsx)(t.a,{href:"https://github.com/notaryproject/notaryproject/blob/main/specs/trust-store-trust-policy.md#signature-verification-detail
1s",children:"Signature Verification Level"})]}),"\n"]}),"\n",(0,i.jsx)(t.h2,{id:"sample-rego-policy",children:"Sample Rego Policy"}),"\n",(0,i.jsxs)(t.p,{children:["Let's look at a Ratify policy from the library in more detail. Below is the\nrego from the ",(0,i.jsx)(t.code,{children:"notationissuervalidation"})," policy:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-rego",children:'package notationissuervalidation\n\n# Get data from Ratify\nremote_data := response {\n  images := [img | img = input.review.object.spec.containers[_].image]\n  response := external_data({"provider": "ratify-provider", "keys": images})\n}\n\n# Base Gatekeeper violation\nviolation[{"msg": msg}] {\n  general_violation[{"result": msg}]\n}\n\n# Check if there are any system errors\ngeneral_violation[{"result": result}] {\n  err := remote_data.system_error\n  err != ""\n  result := sprintf("System error calling external data provider: %s", [err])\n}\n\n# Check if there are errors for any of the images\ngeneral_violation[{"result": result}] {\n  count(remote_data.errors) > 0\n  result := sprintf("Error validating one or more images: %s", remote_data.errors)\n}\n\n# Check if the success criteria is true\ngeneral_violation[{"result": result}] {\n  subject_validation := remote_data.responses[_]\n  subject_validation[1].isSuccess == false\n  result := sprintf("Subject failed verification: %s", [subject_validation[0]])\n}\n\n# Check that signature result for Issuer exists\ngeneral_violation[{"result": result}] {\n  subject_results := remote_data.responses[_]\n  subject_result := subject_results[1]\n  notation_results := [res | subject_result.verifierReports[i].name == "notation"; res := subject_result.verifierReports[i]]\n  issuer_results := [res | notation_results[i].extensions.Issuer == input.parameters.issuer; res := notation_results[i]]\n  count(issuer_results) == 0\n  result := sprintf("Subject %s has no signatures for certificate with Issuer: %s", [subject_results[0], input.parameters.issuer])\n}\n\n# Check for valid signature\ngeneral_violation[{"result": result}] {\n  subject_results := remote_data.responses[_]\n  subject_result := subject_results[1]\n  notation_results := [res | subject_result.verifierReports[i].name == "notation"; res := subject_result.verifierReports[i]]\n  notation_result := notation_results[_]\n  notation_result.isSuccess == false\n  notation_result.extensions.Issuer == input.parameters.issuer\n  result = sprintf("Subject %s failed signature validation: %s", [subject_results[0], notation_result.message])\n}\n'})}),"\n",(0,i.jsx)(t.p,{children:"There are a handful of rules defined here that serve different purposes."}),"\n",(0,i.jsxs)(t.p,{children:["The first rule creates a document called ",(0,i.jsx)(t.code,{children:"remote_data"})," by making a call to the\nRatify external data provider. This document is then utilized in other rules to\nevaluate the results."]}),"\n",(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.code,{children:"violation"})," rule is a Gatekeeper construct and is used by Gatekeeper to\ndetermine rule violations. If a message is not present after evaluating the rego\nthe admission request will pass. From this rule we call all the different types\nof ",(0,i.jsx)(t.code,{children:"general_violations"})," that we have defined."]}),"\n",(0,i.jsxs)(t.p,{children:["Finally, there are a collection of ",(0,i.jsx)(t.code,{children:"general_violation"})," rules. Since these rules\nall share the same name and signature they are iteratively building a set of\nmessages that will be bubbled up to the main ",(0,i.jsx)(t.code,{children:"violation"})," rule. Each of them is\nchecking for different criteria that would indicate a violation."]}),"\n",(0,i.jsxs)(t.p,{children:["The first ",(0,i.jsx)(t.code,{children:"general_violation"})," is satisfied when there is a ",(0,i.jsx)(t.code,{children:"system_error"})," field\nunder the ",(0,i.jsx)(t.code,{children:"remote_data"})," document and when the field is not an empty string. When\nthe body of the rule is satisfied it will generate a result defined by the\n",(0,i.jsx)(t.code,{children:"sprintf"})," statement. This result is then appended to the ",(0,i.jsx)(t.code,{children:"msg"})," in the\n",(0,i.jsx)(t.code,{children:"violation"})," rule and will be returned to the user."]}),"\n",(0,i.jsxs)(t.p,{children:["Each subsequent ",(0,i.jsx)(t.code,{children:"general_violation"})," is checking for different criteria that\nwould indicate a violation using standard rego."]}),"\n",(0,i.jsxs)(t.p,{children:["When building custom policies the top four rules can be reused as they are used\nto handle data fetching and general error cases. Custom ",(0,i.jsx)(t.code,{children:"general_violation"}),"\nrules can then be added to handle specific violations."]})]})}function h(e={}){const{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}}}]);

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.