1"use strict";(self.webpackChunkopa_website=self.webpackChunkopa_website||[]).push([[5406],{14709:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>r,contentTitle:()=>l,default:()=>p,frontMatter:()=>s,metadata:()=>o,toc:()=>c});const o=JSON.parse('{"id":"envoy/tutorial-standalone-envoy","title":"Tutorial: Standalone Envoy","description":"The tutorial shows how Envoy\u2019s External","source":"@site/docs/envoy/tutorial-standalone-envoy.md","sourceDirName":"envoy","slug":"/envoy/tutorial-standalone-envoy","permalink":"/docs/envoy/tutorial-standalone-envoy","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":2,"frontMatter":{"title":"Tutorial: Standalone Envoy","sidebar_position":2},"sidebar":"docsSidebar","previous":{"title":"Policy Primer via Examples","permalink":"/docs/envoy/primer"},"next":{"title":"Tutorial: Istio","permalink":"/docs/envoy/tutorial-istio"}}');var i=t(74848),a=t(28453);const s={title:"Tutorial: Standalone Envoy",sidebar_position:2},l=void 0,r={},c=[{value:"Overview",id:"overview",level:2},{value:"Running a local Kubernetes cluster",id:"running-a-local-kubernetes-cluster",level:2},{value:"Creating & Serving the Policy Bundle",id:"creating--serving-the-policy-bundle",level:2},{value:"Deploying an application with Envoy and OPA sidecars",id:"deploying-an-application-with-envoy-and-opa-sidecars",level:2},{value:"See the Policy in Action",id:"see-the-policy-in-action",level:2},{value:"Listing People",id:"listing-people",level:3},{value:"Creating People",id:"creating-people",level:3},{value:"Creating People: Conflict",id:"creating-people-conflict",level:3},{value:"Shutting Down",id:"shutting-down",level:2},{value:"Summary",id:"summary",level:2}];function d(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(n.p,{children:["The tutorial shows how Envoy\u2019s External\n",(0,i.jsx)(n.a,{href:"https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter.html",children:"authorization filter"}),"\ncan be used with OPA as an authorization service to enforce security policies over API requests\nreceived by Envoy. The tutorial also covers examples of authoring custom\npolicies over the HTTP request body."]}),"\n",(0,i.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,i.jsx)(n.p,{children:"In this tutorial, OPA is used as an External\nAuthorization service for the Envoy proxy. The tutorial covers:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Running a local Kubernetes cluster"}),"\n",(0,i.jsx)(n.li,{children:"Creating a simple authorization policy in Rego and serving it via the Bundle API"}),"\n",(0,i.jsx)(n.li,{children:"Deploying a sample application with Envoy and OPA sidecars"}),"\n",(0,i.jsx)(n.li,{children:"Run some sample requests to see the policy in action"}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"Note that other than the HTTP client and bundle server, all components\nare co-located in the same pod."}),"\n",(0,i.jsx)(n.h2,{id:"running-a-local-kubernetes-cluster",children:"Running a local Kubernetes cluster"}),"\n",(0,i.jsxs)(n.p,{children:["To start a local Kubernetes cluster to run the demo, use\n",(0,i.jsx)(n.a,{href:"https://kind.sigs.k8s.io/",children:"kind"}),"."]}),"\n",(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["If you haven't used ",(0,i.jsx)(n.code,{children:"kind"})," before, you can find installation instructions\nin the ",(0,i.jsx)(n.a,{href:"https://kind.sigs.k8s.io/#installation-and-usage",children:"project documentation"}),"."]})}),"\n",(0,i.jsx)(n.p,{children:"Create a cluster with the following command:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:'$ kind create cluster --name opa-envoy --image kindest/node:v1.33.1\nCreating cluster "opa-envoy" ...\n \u2713 Ensuring node image (kindest/node:v1.33.1) \ud83d\uddbc\n \u2713 Preparing nodes \ud83d\udce6\n \u2713 Writing configuration \ud83d\udcdc\n \u2713 Starting control-plane \ud83d\udd79\ufe0f\n \u2713 Installing CNI \ud83d\udd0c\n \u2713 Installing StorageClass \ud83d\udcbe\n...\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Once the cluster is created, make sure your ",(0,i.jsx)(n.code,{children:"kubectl"})," context is set to connect\nto the new cluster:"]}
1),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"$ kubectl cluster-info --context kind-opa-envoy\nKubernetes control plane is running at ...\nCoreDNS is running at ...\n...\n"})}),"\n",(0,i.jsx)(n.p,{children:"Listing the cluster nodes, should show something like this:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"$ kubectl get nodes\nNAME STATUS ROLES AGE VERSION\nopa-envoy-control-plane Ready control-plane 2m35s v1.33.1\n"})}),"\n",(0,i.jsx)(n.h2,{id:"creating--serving-the-policy-bundle",children:"Creating & Serving the Policy Bundle"}),"\n",(0,i.jsx)(n.p,{children:"This tutorial assumes you have some Rego knowledge, in summary the policy below does the following:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Checks that the JWT token is valid"}),"\n",(0,i.jsxs)(n.li,{children:["Checks that the action is allowed based on the token payload ",(0,i.jsx)(n.code,{children:"role"})," and the request path"]}),"\n",(0,i.jsxs)(n.li,{children:["Guests have read-only access to the ",(0,i.jsx)(n.code,{children:"/people"})," endpoint, admins can create users too as long as the\nname is not the same as the admin's name."]}),"\n"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-rego",children:'# policy.rego\npackage envoy.authz\n\nimport input.attributes.request.http as http_request\n\ndefault allow := false\n\nallow if {\n is_token_valid\n action_allowed\n}\n\nis_token_valid if {\n token.valid\n now := time.now_ns() / 1000000000\n token.payload.nbf <= now\n now < token.payload.exp\n}\n\naction_allowed if {\n http_request.method == "GET"\n token.payload.role == "guest"\n glob.match("/people", ["/"], http_request.path)\n}\n\naction_allowed if {\n http_request.method == "GET"\n token.payload.role == "admin"\n glob.match("/people", ["/"], http_request.path)\n}\n\naction_allowed if {\n http_request.method == "POST"\n token.payload.role == "admin"\n glob.match("/people", ["/"], http_request.path)\n lower(input.parsed_body.firstname) != base64url.decode(token.payload.sub)\n}\n\ntoken := {"valid": valid, "payload": payload} if {\n [_, encoded] := split(http_request.headers.authorization, " ")\n [valid, _, payload] := io.jwt.decode_verify(encoded, {"secret": "secret"})\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Create a file called ",(0,i.jsx)(n.code,{children:"policy.rego"})," with the above content and store it in a ConfigMap:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"kubectl create configmap authz-policy --from-file policy.rego\n"})}),"\n",(0,i.jsx)(n.p,{children:"Now that the policy is stored in a ConfigMap, spin up an HTTP server to make it\navailable as a Bundle to OPA when it's making decisions for the application:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:'# bundle-server.yaml\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: bundle-server\n labels:\n app: bundle-server\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: bundle-server\n template:\n metadata:\n labels:\n app: bundle-server\n spec:\n initContainers:\n - name: opa-builder\n image: openpolicyagent/opa:latest\n args:\n - "build"\n - "--bundle"\n - "/opt/policy/"\n - "--output"\n - "/opt/output/bundle.tar.gz"\n volumeMounts:\n - name: index\n mountPath: /opt/output/\n - name: policy\n mountPath: /opt/policy/\n containers:\n - name: bundle-server\n image: nginx:1.25\n ports:\n - containerPort: 80\n name: http\n volumeMounts:\n - name: index\n mountPath: /usr/share/nginx/html\n volumes:\n - name: index\n emptyDir: {}\n - name: policy\n configMap:\n name: authz-policy\n---\napiVersion: v1\nkind: Service\nmetadata:\n name: bundle-server\nspec:\n selector:\n app: bundle-server\n ports:\n - protocol: TCP\n port: 80\n targetPort: http\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Create a file called ",(0,i.jsx)(n.code,{children:"bundle-server.yaml"})," with the above content and apply it to the cluster:"]}
1),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"kubectl apply -f bundle-server.yaml\n"})}),"\n",(0,i.jsx)(n.p,{children:"Once the deployment is running, check that the bundle is available by running:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"kubectl port-forward service/bundle-server 8080:80\n"})}),"\n",(0,i.jsx)(n.p,{children:"Before checking that the bundle has been generated correctly and is available to download:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"$ curl -I localhost:8080/bundle.tar.gz\nHTTP/1.1 200 OK\n...\n"})}),"\n",(0,i.jsx)(n.p,{children:"You may now exit the port-forwarding session, the bundle server will only be accessed\nfrom inside the cluster from now on."}),"\n",(0,i.jsx)(n.h2,{id:"deploying-an-application-with-envoy-and-opa-sidecars",children:"Deploying an application with Envoy and OPA sidecars"}),"\n",(0,i.jsxs)(n.p,{children:["In this tutorial, the Envoy proxy sidecar is manually configured to intermediate\nHTTP traffic from clients and the application. Envoy will consult OPA to\nmake authorization decisions for each request by sending ",(0,i.jsx)(n.code,{children:"CheckRequest"})," messages over\na gRPC connection."]}),"\n",(0,i.jsx)(n.p,{children:"The following Envoy configuration achieves this. In summary, this\nconfigures Envoy to:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Listen on port ",(0,i.jsx)(n.code,{children:"8000"})," for HTTP traffic"]}),"\n",(0,i.jsx)(n.li,{children:"Consult OPA for authorization decisions at 127.0.0.1:9191 & deny failing requests"}),"\n",(0,i.jsx)(n.li,{children:"Forward requests to the application at 127.0.0.1:8080 if ok."}),"\n"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:'# envoy.yaml\nstatic_resources:\n listeners:\n - address:\n socket_address:\n address: 0.0.0.0\n port_value: 8000\n filter_chains:\n - filters:\n - name: envoy.filters.network.http_connection_manager\n typed_config:\n "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager\n codec_type: auto\n stat_prefix: ingress_http\n route_config:\n name: local_route\n virtual_hosts:\n - name: backend\n domains:\n - "*"\n routes:\n - match:\n prefix: "/"\n route:\n cluster: service\n http_filters:\n - name: envoy.ext_authz\n typed_config:\n "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz\n transport_api_version: V3\n with_request_body:\n max_request_bytes: 8192\n allow_partial_message: true\n failure_mode_allow: false\n grpc_service:\n google_grpc:\n target_uri: 127.0.0.1:9191\n stat_prefix: ext_authz\n timeout: 0.5s\n - name: envoy.filters.http.router\n typed_config:\n "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router\n clusters:\n - name: service\n connect_timeout: 0.25s\n type: strict_dns\n lb_policy: round_robin\n load_assignment:\n cluster_name: service\n endpoints:\n - lb_endpoints:\n - endpoint:\n address:\n socket_address:\n address: 127.0.0.1\n port_value: 8080\nadmin:\n access_log_path: "/dev/null"\n address:\n socket_address:\n address: 0.0.0.0\n port_value: 8001\nlayered_runtime:\n layers:\n - name: static_layer_0\n static_layer:\n envoy:\n resource_limits:\n listener:\n example_listener_name:\n connection_limit: 10000\n overload:\n global_downstream_max_connections: 50000\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Create a ",(0,i.jsx)(n.code,{children:"ConfigMap"})," containing the above configuration by running:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"kubectl create configmap proxy-config --from-file envoy.yaml\n"})}),"\n",(0,i.jsxs)(n.p,{children:["The application is configured using a ",(0,i.jsx)(n.code,{children:"Deployment"})," and ",(0,i.jsx)(n.code,{children:"Service"}),".\nThere are a few things to note:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["the pods have an ",(0,i.jsx)(n.code,{children:"initContainer"}
1)," that configures the ",(0,i.jsx)(n.code,{children:"iptables"})," rules to\nredirect traffic to the Envoy proxy."]}),"\n",(0,i.jsxs)(n.li,{children:["the ",(0,i.jsx)(n.code,{children:"demo-test-server"})," container is a simple user store using in-memory state."]}),"\n",(0,i.jsxs)(n.li,{children:["the ",(0,i.jsx)(n.code,{children:"envoy"})," container is configured to use the ",(0,i.jsx)(n.code,{children:"proxy-config"})," ",(0,i.jsx)(n.code,{children:"ConfigMap"}),"\ncreated earlier."]}),"\n",(0,i.jsxs)(n.li,{children:["The OPA container is configured to download policy bundles from\nthe in-cluster bundle server (",(0,i.jsx)(n.code,{children:"bundle-server.default.svc.cluster.local"}),")."]}),"\n"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:'# app.yaml\nkind: Deployment\napiVersion: apps/v1\nmetadata:\n name: example-app\n labels:\n app: example-app\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: example-app\n template:\n metadata:\n labels:\n app: example-app\n spec:\n initContainers:\n - name: proxy-init\n image: openpolicyagent/proxy_init:v8\n # Configure the iptables bootstrap script to redirect traffic to the\n # Envoy proxy on port 8000. Envoy will be running as 1111, and port\n # 8282 will be excluded to support OPA health checks.\n args: ["-p", "8000", "-u", "1111", "-w", "8282"]\n securityContext:\n capabilities:\n add:\n - NET_ADMIN\n runAsNonRoot: false\n runAsUser: 0\n containers:\n - name: app\n image: openpolicyagent/demo-test-server:v1\n ports:\n - containerPort: 8080\n - name: envoy\n image: envoyproxy/envoy:v1.26.3\n volumeMounts:\n - readOnly: true\n mountPath: /config\n name: proxy-config\n args:\n - "envoy"\n - "--config-path"\n - "/config/envoy.yaml"\n env:\n - name: ENVOY_UID\n value: "1111"\n - name: opa\n image: openpolicyagent/opa:latest-envoy-static\n args:\n - "run"\n - "--server"\n - "--addr=localhost:8181"\n - "--diagnostic-addr=0.0.0.0:8282"\n - "--set=services.default.url=http://bundle-server"\n - "--set=bundles.default.resource=bundle.tar.gz"\n - "--set=plugins.envoy_ext_authz_grpc.addr=:9191"\n - "--set=plugins.envoy_ext_authz_grpc.path=envoy/authz/allow"\n - "--set=decision_logs.console=true"\n - "--set=status.console=true"\n - "--ignore=.*"\n livenessProbe:\n httpGet:\n path: /health?plugins\n scheme: HTTP\n port: 8282\n initialDelaySeconds: 5\n periodSeconds: 5\n readinessProbe:\n httpGet:\n path: /health?plugins\n scheme: HTTP\n port: 8282\n initialDelaySeconds: 1\n periodSeconds: 3\n volumes:\n - name: proxy-config\n configMap:\n name: proxy-config\n---\napiVersion: v1\nkind: Service\nmetadata:\n name: example-app\nspec:\n selector:\n app: example-app\n ports:\n - protocol: TCP\n port: 80\n targetPort: 8080\n'})}),"\n",(0,i.jsx)(n.p,{children:"Deploy the application and Kubernetes Service to the cluster with:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"kubectl apply -f app.yaml\n"})}),"\n",(0,i.jsx)(n.p,{children:"Check that everything is working by listing the pod (make sure that\nall three pods are running ok)."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"$ kubectl get pods\nNAME READY STATUS RESTARTS AGE\nbundle-server-5d7bfffdb6-bgn86 1/1 Running 0 1m\nexample-app-74b4bc88-5d4wh 3/3 Running 0 1m\n"})}),"\n",(0,i.jsx)(n.h2,{id:"see-the-policy-in-action",children:"See the Policy in Action"}),"\n",(0,i.jsx)(n.p,{children:"Run a shell inside the cluster to use for testing. Use this in-cluster\nshell for the rest of the tutorial."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:"kubectl run curl --restart=Never -it --rm --image curlimages/curl:8.1.2 -- sh\n"})}),"\n",(0,i.jsx)(n.p,{children:"Set two tokens for two users, Alice and Bob with different permissions.\nAs defined by the policy:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:'export ALICE_TOKEN="eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJleHAiOiAyMjQxMDgxNTM5LCAibmJmIjogMTUxNDg1MTEzOSwgInJvbGUiOiAiZ3Vlc3QiLCAic3ViIjogIllXeHBZMlU9In0.Uk5hgUqMuUfDLvBLnlXMD0-X53aM_Hlziqg3vhOsCc8"\nexport BOB_TOKEN="eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJleHAiOiAyMjQxMDgxNTM5LCAibmJmIjogMTUxNDg1MTEzOSwgInJvbGUiOiAiYWRtaW4iLCAic3ViIjogIlltOWkifQ.5qsm7rRTvqFHAgiB6evX0a_hWnGbWquZC0HImVQPQo8"\n'})}),"\n",(0,i.jsx)(n.h3,{id:"listing-people",children:"Listing People"}),"\n",(0,i.jsx)(n.p,{children:"Send a request to list people. This should succeed for both Alice and Bob."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:'curl -i -H "Authorization: Bearer $ALICE_TOKEN" http://example-app/people\n'})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:'HTTP/1.1 200 OK\ncontent-type: application/json\ndate: Tue, 18 Jul 2023 15:22:25 GMT\ncontent-length: 96\nx-envoy-upstream-service-time: 14\nserver: envoy\n\n[{"id":"
11","firstname":"John","lastname":"Doe"},{"id":"2","firstname":"Jane","lastname":"Doe"}]\n'})}),"\n",(0,i.jsx)(n.p,{children:"And for Bob:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:'curl -i -H "Authorization: Bearer $BOB_TOKEN" http://example-app/people\n'})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"HTTP/1.1 200 OK\n...omitted...\n"})}),"\n",(0,i.jsx)(n.h3,{id:"creating-people",children:"Creating People"}),"\n",(0,i.jsx)(n.p,{children:"Send a request to create a new user. This should fail for Alice but not Bob:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:'curl -i -H "Authorization: Bearer $ALICE_TOKEN" \\\n -d \'{"firstname":"Foo", "lastname":"Bar"}\' -H "Content-Type: application/json" \\\n -X POST http://example-app/people\n'})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"HTTP/1.1 403 Forbidden\ndate: Tue, 18 Jul 2023 15:25:28 GMT\nserver: envoy\ncontent-length: 0\n"})}),"\n",(0,i.jsx)(n.p,{children:"And for Bob, the request is permitted and the user is saved with an ID"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:'curl -i -H "Authorization: Bearer $BOB_TOKEN" \\\n -d \'{"firstname":"Foo", "lastname":"Bar"}\' -H "Content-Type: application/json" \\\n -X POST http://example-app/people\n'})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:'HTTP/1.1 200 OK\ncontent-type: application/json\ndate: Tue, 18 Jul 2023 15:28:20 GMT\ncontent-length: 51\nx-envoy-upstream-service-time: 11\nserver: envoy\n\n{"id":"498081","firstname":"Foo","lastname":"Bar"}\n'})}),"\n",(0,i.jsx)(n.h3,{id:"creating-people-conflict",children:"Creating People: Conflict"}),"\n",(0,i.jsx)(n.p,{children:"The policy also blocks users from creating users with the same name. Test that\nfunctionality with this request:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:'curl -i -H "Authorization: Bearer $BOB_TOKEN" \\\n -d \'{"firstname":"Bob", "lastname":"Bar"}\' -H "Content-Type: application/json" \\\n -X POST http://example-app/people\n'})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"HTTP/1.1 403 Forbidden\ndate: Tue, 18 Jul 2023 15:31:48 GMT\nserver: envoy\ncontent-length: 0\n"})}),"\n",(0,i.jsx)(n.h2,{id:"shutting-down",children:"Shutting Down"}),"\n",(0,i.jsxs)(n.p,{children:["Exit the in-cluster shell by typing ",(0,i.jsx)(n.code,{children:"exit"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"Delete the cluster by running:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-shell",children:'$ kind delete cluster --name opa-envoy\nDeleting cluster "opa-envoy" ...\nDeleted nodes: ["opa-envoy-control-plane"]\n'})}),"\n",(0,i.jsx)(n.h2,{id:"summary",children:"Summary"}),"\n",(0,i.jsx)(n.p,{children:"This tutorial showed how to use OPA as an External authorization service to\nenforce custom policies by leveraging Envoy\u2019s External authorization filter."}),"\n",(0,i.jsxs)(n.p,{children:["This tutorial also showed a sample OPA policy that returns a ",(0,i.jsx)(n.code,{children:"boolean"})," decision\nto indicate whether a request should be allowed or not."]}),"\n",(0,i.jsxs)(n.p,{children:["Envoy's external authorization filter allows optional response headers and body\nto be sent to the downstream client or upstream. An example of a rule that\nreturns an object that not only indicates if a request is allowed or not but\nalso provides optional response headers, body and HTTP status that can be sent\nto the downstream client or upstream can be seen\n",(0,i.jsx)(n.a,{href:"https://github.com/open-policy-agent/opa-envoy-plugin#example-policy-with-object-response",children:"in the opa-envoy-plugin documentation"}),"."]})]})}function p(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},28453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>l});var o=t(96540);const i={},a=o.createContext(i);function s(e){const n=o.useContext(a);return o.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(i):e.components||i:s(e.components),o.createElement(a.Provider,{value:n},e.children)}}}]);
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.