mirror of https://github.com/istio/istio.io.git
252 lines
39 KiB
HTML
252 lines
39 KiB
HTML
<!doctype html><html lang=en itemscope itemtype=https://schema.org/WebPage><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=theme-color content="#466BB0"><meta name=title content="Can Your Platform Do Policy? Accelerate Teams With Platform L7 Policy Functionality"><meta name=description content="Is policy your core competency? Likely not, but you need to do right. Do it once with Istio and OPA and get back team focus on what matters most."><meta name=author content="Antonio Berben (Solo.io), Charlie Egan (Styra)"><meta name=keywords content="microservices,services,mesh,istio,opa,policy,platform,authorization"><meta property="og:title" content="Can Your Platform Do Policy? Accelerate Teams With Platform L7 Policy Functionality"><meta property="og:type" content="website"><meta property="og:description" content="Is policy your core competency? Likely not, but you need to do right. Do it once with Istio and OPA and get back team focus on what matters most."><meta property="og:url" content="/v1.24/blog/2024/l7-policy-with-opa/"><meta property="og:image" content="https://raw.githubusercontent.com/istio/istio.io/master/static/img/istio-social.png"><meta property="og:image:alt" content="The Istio sailboat logo"><meta property="og:image:width" content="4096"><meta property="og:image:height" content="2048"><meta property="og:site_name" content="Istio"><meta name=twitter:card content="summary_large_image"><meta name=twitter:site content="@IstioMesh"><title>Istioldie 1.24 / Can Your Platform Do Policy? Accelerate Teams With Platform L7 Policy Functionality</title>
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-5XBWY4YJ1E"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("js",new Date),gtag("config","G-5XBWY4YJ1E")</script><link rel=alternate type=application/rss+xml title="Istio Blog" href=/v1.24/blog/feed.xml><link rel=alternate type=application/rss+xml title="Istio News" href=/v1.24/news/feed.xml><link rel=alternate type=application/rss+xml title="Istio Blog and News" href=/v1.24/feed.xml><link rel="shortcut icon" href=/v1.24/favicons/favicon.ico><link rel=apple-touch-icon href=/v1.24/favicons/apple-touch-icon-180x180.png sizes=180x180><link rel=icon type=image/png href=/v1.24/favicons/favicon-16x16.png sizes=16x16><link rel=icon type=image/png href=/v1.24/favicons/favicon-32x32.png sizes=32x32><link rel=icon type=image/png href=/v1.24/favicons/android-36x36.png sizes=36x36><link rel=icon type=image/png href=/v1.24/favicons/android-48x48.png sizes=48x48><link rel=icon type=image/png href=/v1.24/favicons/android-72x72.png sizes=72x72><link rel=icon type=image/png href=/v1.24/favicons/android-96x96.png sizes=96xW96><link rel=icon type=image/png href=/v1.24/favicons/android-144x144.png sizes=144x144><link rel=icon type=image/png href=/v1.24/favicons/android-192x192.png sizes=192x192><link rel=icon type=image/svg+xml href=/v1.24/favicons/favicon.svg><link rel=icon type=image/png href=/v1.24/favicons/favicon.png><link rel=mask-icon href=/v1.24/favicons/safari-pinned-tab.svg color=#466BB0><link rel=manifest href=/v1.24/manifest.json><meta name=apple-mobile-web-app-title content="Istio"><meta name=application-name content="Istio"><meta name=msapplication-config content="/browserconfig.xml"><meta name=msapplication-TileColor content="#466BB0"><meta name=theme-color content="#466BB0"><link rel=stylesheet href=/v1.24/css/style.min.38f1afbdf6f8efdb4fe991ff2a53ca1c801b5c4602dea2963da44df7ceaacfb8.css integrity="sha256-OPGvvfb479tP6ZH/KlPKHIAbXEYC3qKWPaRN986qz7g=" crossorigin=anonymous><link rel=preconnect href=https://fonts.googleapis.com><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&display=swap"><script src=/v1.24/js/themes_init.min.js></script></head><body class="language-unknown archive-site"><script>const branchName="release-1.24",docTitle="Can Your Platform Do Policy? Accelerate Teams With Platform L7 Policy Functionality",iconFile="/v1.24//img/icons.svg",buttonCopy="Copy to clipboard",buttonPrint="Print",buttonDownload="Download"</script><script src="https://www.google.com/cse/brand?form=search-form" defer></script><script src=/v1.24/js/all.min.js data-manual defer></script><header class=main-navigation><nav class="main-navigation-wrapper container-l"><div class=main-navigation-header><a id=brand href=/v1.24/ aria-label=logotype><span class=logo><svg width="128" height="60" viewBox="0 0 128 60"><path d="M58.434 48.823A.441.441.0 0158.3 48.497V22.583a.444.444.0 01.134-.326.446.446.0 01.327-.134h3.527a.447.447.0 01.325.134.447.447.0 01.134.326v25.914a.443.443.0 01-.134.326.444.444.0 01-.325.134h-3.527a.444.444.0 01-.327-.134z"/><path d="m70.969 48.477a6.556 6.556.0 01-2.818-1.955 4.338 4.338.0 01-1-2.78v-.345a.443.443.0 01.134-.326.444.444.0 01.326-.135h3.374a.444.444.0 01.326.135.445.445.0 01.134.326v.077a2.014 2.014.0 001.054 1.667 4.672 4.672.0 002.664.709 4.446 4.446.0 002.492-.633 1.862 1.862.0 00.958-1.591 1.426 1.426.0 00-.786-1.322 12.7 12.7.0 00-2.549-.939l-1.457-.46a21.526 21.526.0 01-3.3-1.227 6.57 6.57.0 01-2.262-1.783 4.435 4.435.0 01-.92-2.894 5.081 5.081.0 012.109-4.275 8.993 8.993.0 015.558-1.591 10.445 10.445.0 014.1.748 6.3 6.3.0 012.722 2.07 5 5 0 01.958 3.009.441.441.0 01-.134.326.441.441.0 01-.325.134h-3.258a.441.441.0 01-.326-.134.443.443.0 01-.134-.326 1.974 1.974.0 00-.978-1.667 4.647 4.647.0 00-2.665-.671 4.741 4.741.0 00-2.435.556 1.724 1.724.0 00-.938 1.553 1.512 1.512.0 00.9 1.4 15.875 15.875.0 003.01 1.055l.843.229a27.368 27.368.0 013.412 1.246 6.67 6.67.0 012.338 1.763 4.387 4.387.0 01.958 2.933 4.988 4.988.0 01-2.146 4.275 9.543 9.543.0 01-5.712 1.552 11.626 11.626.0 01-4.227-.709z"/><path d="m97.039 32.837a.443.443.0 01-.326.135h-3.911a.169.169.0 00-.191.192v9.239a2.951 2.951.0 00.632 2.108 2.7 2.7.0 002.013.652h1.15a.444.444.0 01.325.134.441.441.0 01.134.326v2.875a.471.471.0 01-.459.5l-1.994.039a8 8 0 01-4.524-1.035q-1.495-1.035-1.533-3.91V33.166A.17.17.0 0088.164 32.974H85.978A.441.441.0 0185.652 32.839.441.441.0 0185.518 32.513V29.83a.441.441.0 01.134-.326.444.444.0 01.326-.135h2.186a.169.169.0 00.191-.192v-4.485a.438.438.0 01.134-.326.44.44.0 01.325-.134h3.336a.443.443.0 01.325.134.442.442.0 01.135.326v4.485a.169.169.0 00.191.192h3.911a.446.446.0 01.326.135.446.446.0 01.134.326v2.683a.446.446.0 01-.133.324z"/><path d="m101.694 25.917a2.645 2.645.0 01-.767-1.955 2.65 2.65.0 01.767-1.955 2.65 2.65.0 011.955-.767 2.65 2.65.0 011.955.767 2.652 2.652.0 01.767 1.955 2.647 2.647.0 01-.767 1.955 2.646 2.646.0 01-1.955.767 2.645 2.645.0 01-1.955-.767zm-.211 22.906a.441.441.0 01-.134-.326V29.79a.444.444.0 01.134-.326.446.446.0 01.326-.134h3.527a.446.446.0 01.326.134.445.445.0 01.134.326v18.707a.443.443.0 01-.134.326.443.443.0 01-.326.134h-3.527a.443.443.0 01-.326-.134z"/><path d="m114.019 47.734a8.1 8.1.0 01-3.047-4.255 14.439 14.439.0 01-.652-4.37 14.3 14.3.0 01.614-4.371A7.869 7.869.0 01114 30.56a9.072 9.072.0 015.252-1.5 8.543 8.543.0 015.041 1.5 7.985 7.985.0 013.009 4.14 12.439 12.439.0 01.69 4.37 13.793 13.793.0 01-.651 4.37 8.255 8.255.0 01-3.028 4.275 8.475 8.475.0 01-5.1 1.553 8.754 8.754.0 01-5.194-1.534zm7.629-3.1a4.536 4.536.0 001.476-2.262 11.335 11.335.0 00.383-3.221 10.618 10.618.0 00-.383-3.22 4.169 4.169.0 00-1.457-2.243 4.066 4.066.0 00-2.531-.785 3.942 3.942.0 00-2.453.785 4.376 4.376.0 00-1.5 2.243 11.839 11.839.0 00-.383 3.22 11.84 11.84.0 00.383 3.221 4.222 4.222.0 001.476 2.262 4.075 4.075.0 002.549.8 3.8 3.8.0 002.44-.809z"/><path d="m15.105 32.057v15.565a.059.059.0 01-.049.059L.069 50.25A.06.06.0 01.005 50.167l14.987-33.47a.06.06.0 01.114.025z"/><path d="m17.631 23.087v24.6a.06.06.0 00.053.059l22.449 2.507a.06.06.0 00.061-.084L17.745.032a.06.06.0 00-.114.024z"/><path d="m39.961 52.548-24.833 7.45a.062.062.0 01-.043.0L.079 52.548a.059.059.0 01.026-.113h39.839a.06.06.0 01.017.113z"/></svg></span>
|
||
</a><button id=hamburger class=main-navigation-toggle aria-label="Open navigation">
|
||
<svg class="icon menu-hamburger"><use xlink:href="/v1.24/img/icons.svg#menu-hamburger"/></svg>
|
||
</button>
|
||
<button id=menu-close class=main-navigation-toggle aria-label="Close navigation"><svg class="icon menu-close"><use xlink:href="/v1.24/img/icons.svg#menu-close"/></svg></button></div><div id=header-links class=main-navigation-links-wrapper><ul class=main-navigation-links><li class=main-navigation-links-item><a class="main-navigation-links-link has-dropdown"><span>About</span><svg class="icon dropdown-arrow"><use xlink:href="/v1.24/img/icons.svg#dropdown-arrow"/></svg></a><ul class=main-navigation-links-dropdown><li class=main-navigation-links-dropdown-item><a href=/v1.24/about/service-mesh class=main-navigation-links-link>Service mesh</a></li><li class=main-navigation-links-dropdown-item><a href=/v1.24/about/solutions class=main-navigation-links-link>Solutions</a></li><li class=main-navigation-links-dropdown-item><a href=/v1.24/about/case-studies class=main-navigation-links-link>Case studies</a></li><li class=main-navigation-links-dropdown-item><a href=/v1.24/about/ecosystem class=main-navigation-links-link>Ecosystem</a></li><li class=main-navigation-links-dropdown-item><a href=/v1.24/about/deployment class=main-navigation-links-link>Deployment</a></li><li class=main-navigation-links-dropdown-item><a href=/v1.24/about/training class=main-navigation-links-link>Training</a></li><li class=main-navigation-links-dropdown-item><a href=/v1.24/about/faq class=main-navigation-links-link>FAQ</a></li></ul></li><li class=main-navigation-links-item><a href=/v1.24/blog/ class=main-navigation-links-link><span>Blog</span></a></li><li class=main-navigation-links-item><a href=/v1.24/news/ class=main-navigation-links-link><span>News</span></a></li><li class=main-navigation-links-item><a href=/v1.24/get-involved/ class=main-navigation-links-link><span>Get involved</span></a></li><li class=main-navigation-links-item><a href=/v1.24/docs/ class=main-navigation-links-link><span>Documentation</span></a></li></ul><div class=main-navigation-footer><button id=search-show class=search-show title='Search this site' aria-label=Search><svg class="icon magnifier"><use xlink:href="/v1.24/img/icons.svg#magnifier"/></svg></button>
|
||
<a href=/v1.24/docs/setup/getting-started class="btn btn--primary" id=try-istio>Try Istio</a></div></div><form id=search-form class=search name=cse role=search><input type=hidden name=cx value=002184991200833970123:iwwf17ikgf4>
|
||
<input type=hidden name=ie value=utf-8>
|
||
<input type=hidden name=hl value=en>
|
||
<input type=hidden id=search-page-url value=/search>
|
||
<input id=search-textbox class="search-textbox form-control" name=q type=search aria-label='Search this site' placeholder=Search>
|
||
<button id=search-close title='Cancel search' type=reset aria-label='Cancel search'><svg class="icon menu-close"><use xlink:href="/v1.24/img/icons.svg#menu-close"/></svg></button></form></nav></header><div class=banner-container><a href=https://events.linuxfoundation.org/kubecon-cloudnativecon-europe/co-located-events/istio-day/ class=banner data-title="Istio Day Europe-2025-01-31 00:00:00 +0000 UTC" data-period-start=1738281600000 data-period-end=1743465600000 data-max-impressions data-timeout><div class=content><p>Join us for Istio Day Europe, a KubeCon + CloudNativeCon Europe Co-located Event. 01 April 2025, London, England. Register now!</p></div><div class=frame></div></a></div><article class=post itemscope itemtype=http://schema.org/BlogPosting><div class=header-content><h1>Can Your Platform Do Policy? Accelerate Teams With Platform L7 Policy Functionality</h1><p>Is policy your core competency? Likely not, but you need to do right. Do it once with Istio and OPA and get back team focus on what matters most.</p></div><p class=post-author>Oct 14, 2024 <span>| </span>By Antonio Berben - Solo.io, Charlie Egan - Styra</p><div><p>Shared computing platforms offer resources and shared functionality to tenant teams so that they don’t need to build everything from scratch themselves. While it can sometimes be hard to balance all the requests from tenants, it’s important that platform teams ask the question: what’s the highest value feature we can offer our tenants?</p><p>Often work is given directly to application teams to implement, but there are some features that are best implemented once, and offered as a service to all teams. One feature within the reach of most platform teams is offering a standard, responsive system for Layer 7 application authorization policy. Policy as code enables teams to lift authorization decisions out of the application layer into a lightweight and performant decoupled system. It might sound like a challenge, but it doesn’t have to be, with the right tools for the job.</p><p>We’re going to dive into how Istio and Open Policy Agent (OPA) can be used to enforce Layer 7 policies in your platform. We’ll show you how to get started with a simple example. You will come to see how this combination is a solid option to deliver policy quickly and transparently to application team everywhere in the business, while also providing the data the security teams need for audit and compliance.</p><h2 id=try-it-out>Try it out</h2><p>When integrated with Istio, OPA can be used to enforce fine-grained access control policies for microservices. This guide shows how to enforce access control policies for a simple microservices application.</p><h3 id=prerequisites>Prerequisites</h3><ul><li>A Kubernetes cluster with Istio installed.</li><li>The <code>istioctl</code> command-line tool installed.</li></ul><p>Install Istio and configure your <a href=/v1.24/docs/reference/config/istio.mesh.v1alpha1/>mesh options</a> to enable OPA:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ istioctl install -y -f - <<'EOF'
|
||
apiVersion: install.istio.io/v1alpha1
|
||
kind: IstioOperator
|
||
spec:
|
||
meshConfig:
|
||
accessLogFile: /dev/stdout
|
||
accessLogFormat: |
|
||
[OPA DEMO] my-new-dynamic-metadata: "%DYNAMIC_METADATA(envoy.filters.http.ext_authz)%"
|
||
extensionProviders:
|
||
- name: "opa.local"
|
||
envoyExtAuthzGrpc:
|
||
service: "opa.opa.svc.cluster.local"
|
||
port: "9191"
|
||
EOF</code></pre><p>Notice that in the configuration, we define an <code>extensionProviders</code> section that points to the OPA standalone installation.</p><p>Deploy the sample application. Httpbin is a well-known application that can be used to test HTTP requests and helps to show quickly how we can play with the request and response attributes.</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl create ns my-app
|
||
$ kubectl label namespace my-app istio-injection=enabled
|
||
|
||
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/httpbin/httpbin.yaml -n my-app</code></pre><p>Deploy OPA. It will fail because it expects a <code>configMap</code> containing the default Rego rule to use. This <code>configMap</code> will be deployed later in our example.</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl create ns opa
|
||
$ kubectl label namespace opa istio-injection=enabled
|
||
|
||
$ kubectl apply -f - <<EOF
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
labels:
|
||
app: opa
|
||
name: opa
|
||
namespace: opa
|
||
spec:
|
||
replicas: 1
|
||
selector:
|
||
matchLabels:
|
||
app: opa
|
||
template:
|
||
metadata:
|
||
labels:
|
||
app: opa
|
||
spec:
|
||
containers:
|
||
- image: openpolicyagent/opa:0.61.0-envoy
|
||
name: opa
|
||
args:
|
||
- "run"
|
||
- "--server"
|
||
- "--disable-telemetry"
|
||
- "--config-file=/config/config.yaml"
|
||
- "--log-level=debug" # Uncomment this line to enable debug logs
|
||
- "--diagnostic-addr=0.0.0.0:8282"
|
||
- "/policy/policy.rego" # Default policy
|
||
volumeMounts:
|
||
- mountPath: "/config"
|
||
name: opa-config
|
||
- mountPath: "/policy"
|
||
name: opa-policy
|
||
volumes:
|
||
- name: opa-config
|
||
configMap:
|
||
name: opa-config
|
||
- name: opa-policy
|
||
configMap:
|
||
name: opa-policy
|
||
---
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: opa-config
|
||
namespace: opa
|
||
data:
|
||
config.yaml: |
|
||
# Here the OPA configuration you can find in the offcial documention
|
||
decision_logs:
|
||
console: true
|
||
plugins:
|
||
envoy_ext_authz_grpc:
|
||
addr: ":9191"
|
||
path: mypackage/mysubpackage/myrule # Default path for grpc plugin
|
||
# Here you can add your own configuration with services and bundles
|
||
---
|
||
apiVersion: v1
|
||
kind: Service
|
||
metadata:
|
||
name: opa
|
||
namespace: opa
|
||
labels:
|
||
app: opa
|
||
spec:
|
||
ports:
|
||
- port: 9191
|
||
protocol: TCP
|
||
name: grpc
|
||
selector:
|
||
app: opa
|
||
---
|
||
EOF</code></pre><p>Deploy the <code>AuthorizationPolicy</code> to define which services will be protected by OPA.</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl apply -f - <<EOF
|
||
apiVersion: security.istio.io/v1
|
||
kind: AuthorizationPolicy
|
||
metadata:
|
||
name: my-opa-authz
|
||
namespace: istio-system # This enforce the policy on all the mesh being istio-system the mesh config namespace
|
||
spec:
|
||
selector:
|
||
matchLabels:
|
||
ext-authz: enabled
|
||
action: CUSTOM
|
||
provider:
|
||
name: "opa.local"
|
||
rules: [{}] # Empty rules, it will apply to selectors with ext-authz: enabled label
|
||
EOF</code></pre><p>Let’s label the app to enforce the policy:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl patch deploy httpbin -n my-app --type=merge -p='{
|
||
"spec": {
|
||
"template": {
|
||
"metadata": {
|
||
"labels": {
|
||
"ext-authz": "enabled"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}'</code></pre><p>Notice that in this resource, we define the OPA <code>extensionProvider</code> you set in the Istio configuration:</p><pre><code class=language-yaml data-expandlinks=true data-repo=istio>[...]
|
||
provider:
|
||
name: "opa.local"
|
||
[...]</code></pre><h2 id=how-it-works>How it works</h2><p>When applying the <code>AuthorizationPolicy</code>, the Istio control plane (istiod) sends the required configurations to the sidecar proxy (Envoy) of the selected services in the policy. Envoy will then send the request to the OPA server to check if the request is allowed or not.</p><figure style=width:75%><div class=wrapper-with-intrinsic-ratio style=padding-bottom:47.43008314436886%><a data-skipendnotes=true href=/v1.24/blog/2024/l7-policy-with-opa/opa1.png title><img class=element-to-stretch src=/v1.24/blog/2024/l7-policy-with-opa/opa1.png alt="Istio and OPA"></a></div><figcaption></figcaption></figure><p>The Envoy proxy works by configuring filters in a chain. One of those filters is <code>ext_authz</code>, which implements an external authorization service with a specific message. Any server implementing the correct protobuf can connect to the Envoy proxy and provide the authorization decision; OPA is one of those servers.</p><figure style=width:75%><div class=wrapper-with-intrinsic-ratio style=padding-bottom:40.17825311942959%><a data-skipendnotes=true href=/v1.24/blog/2024/l7-policy-with-opa/opa2.png title><img class=element-to-stretch src=/v1.24/blog/2024/l7-policy-with-opa/opa2.png alt=Filters></a></div><figcaption></figcaption></figure><p>Before, when you installed OPA server, you used the Envoy version of the server. This image allows the configuration of the gRPC plugin which implements the <code>ext_authz</code> protobuf service.</p><pre><code class=language-yaml data-expandlinks=true data-repo=istio>[...]
|
||
containers:
|
||
- image: openpolicyagent/opa:0.61.0-envoy # This is the OPA image version which brings the Envoy plugin
|
||
name: opa
|
||
[...]</code></pre><p>In the configuration, you have enabled the Envoy plugin and the port which will listened to:</p><pre><code class=language-yaml data-expandlinks=true data-repo=istio>[...]
|
||
decision_logs:
|
||
console: true
|
||
plugins:
|
||
envoy_ext_authz_grpc:
|
||
addr: ":9191" # This is the port where the envoy plugin will listen
|
||
path: mypackage/mysubpackage/myrule # Default path for grpc plugin
|
||
# Here you can add your own configuration with services and bundles
|
||
[...]</code></pre><p>Reviewing <a href=https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto>Envoy’s Authorization service documentation</a>, you can see that the message has these attributes:</p><pre><code class=language-json data-expandlinks=true data-repo=istio>OkHttpResponse
|
||
{
|
||
"status": {...},
|
||
"denied_response": {...},
|
||
"ok_response": {
|
||
"headers": [],
|
||
"headers_to_remove": [],
|
||
"dynamic_metadata": {...},
|
||
"response_headers_to_add": [],
|
||
"query_parameters_to_set": [],
|
||
"query_parameters_to_remove": []
|
||
},
|
||
"dynamic_metadata": {...}
|
||
}</code></pre><p>This means that based on the response from the authz server, Envoy can add or remove headers, query parameters, and even change the response status. OPA can do this as well, as documented in the <a href=https://www.openpolicyagent.org/docs/latest/envoy-primer/#example-policy-with-additional-controls>OPA documentation</a>.</p><h2 id=testing>Testing</h2><p>Let’s test the simple usage (authorization) and then let’s create a more advanced rule to show how we can use OPA to modify the request and response.</p><p>Deploy an app to run curl commands to the httpbin sample application:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl -n my-app run --image=curlimages/curl curl -- /bin/sleep 100d</code></pre><p>Apply the first Rego rule and restart the OPA deployment:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl apply -f - <<EOF
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: opa-policy
|
||
namespace: opa
|
||
data:
|
||
policy.rego: |
|
||
package mypackage.mysubpackage
|
||
|
||
import rego.v1
|
||
|
||
default myrule := false
|
||
|
||
myrule if {
|
||
input.attributes.request.http.headers["x-force-authorized"] == "enabled"
|
||
}
|
||
|
||
myrule if {
|
||
input.attributes.request.http.headers["x-force-authorized"] == "true"
|
||
}
|
||
EOF</code></pre><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl rollout restart deployment -n opa</code></pre><p>The simple scenario is to allow requests if they contain the header <code>x-force-authorized</code> with the value <code>enabled</code> or <code>true</code>. If the header is not present or has a different value, the request will be denied.</p><p>There are multiple ways to create the Rego rule. In this case, we created two different rules. Executed in order, the first one which satisfies all the conditions will be the one that will be used.</p><h3 id=simple-rule>Simple rule</h3><p>The following request will return <code>403</code>:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl exec -n my-app curl -c curl -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get</code></pre><p>The following request will return <code>200</code> and the body:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl exec -n my-app curl -c curl -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get -H "x-force-authorized: enabled"</code></pre><h3 id=advanced-manipulations>Advanced manipulations</h3><p>Now the more advanced rule. Apply the second Rego rule and restart the OPA deployment:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl apply -f - <<EOF
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: opa-policy
|
||
namespace: opa
|
||
data:
|
||
policy.rego: |
|
||
package mypackage.mysubpackage
|
||
|
||
import rego.v1
|
||
|
||
request_headers := input.attributes.request.http.headers
|
||
|
||
force_unauthenticated if request_headers["x-force-unauthenticated"] == "enabled"
|
||
|
||
default allow := false
|
||
|
||
allow if {
|
||
not force_unauthenticated
|
||
request_headers["x-force-authorized"] == "true"
|
||
}
|
||
|
||
default status_code := 403
|
||
|
||
status_code := 200 if allow
|
||
|
||
status_code := 401 if force_unauthenticated
|
||
|
||
default body := "Unauthorized Request"
|
||
|
||
body := "Authentication Failed" if force_unauthenticated
|
||
|
||
myrule := {
|
||
"body": body,
|
||
"http_status": status_code,
|
||
"allowed": allow,
|
||
"headers": {"x-validated-by": "my-security-checkpoint"},
|
||
"response_headers_to_add": {"x-add-custom-response-header": "added"},
|
||
"request_headers_to_remove": ["x-force-authorized"],
|
||
"dynamic_metadata": {"my-new-metadata": "my-new-value"},
|
||
}
|
||
EOF</code></pre><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl rollout restart deployment -n opa</code></pre><p>In that rule, you can see:</p><pre><code class=language-plain data-expandlinks=true data-repo=istio>myrule["allowed"] := allow # Notice that `allowed` is mandatory when returning an object, like here `myrule`
|
||
myrule["headers"] := headers
|
||
myrule["response_headers_to_add"] := response_headers_to_add
|
||
myrule["request_headers_to_remove"] := request_headers_to_remove
|
||
myrule["body"] := body
|
||
myrule["http_status"] := status_code</code></pre><p>Those are the values that will be returned to the Envoy proxy from the OPA server. Envoy will use those values to modify the request and response.</p><p>Notice that <code>allowed</code> is required when returning a JSON object instead of only true/false. This can be found <a href=https://www.openpolicyagent.org/docs/latest/envoy-primer/#output-document>in the OPA documentation</a>.</p><h4 id=change-returned-body>Change returned body</h4><p>Let’s test the new capabilities:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl exec -n my-app curl -c curl -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get</code></pre><p>Now we can change the response body. With <code>403</code> the body in the Rego rule is changed to “Unauthorized Request”. With the previous command, you should receive:</p><pre><code class=language-plain data-expandlinks=true data-repo=istio>Unauthorized Request
|
||
http_code=403</code></pre><h4 id=change-returned-body-and-status-code>Change returned body and status code</h4><p>Running the request with the header <code>x-force-authorized: enabled</code> you should receive the body “Authentication Failed” and error “401”:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl exec -n my-app curl -c curl -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get -H "x-force-unauthenticated: enabled"</code></pre><h4 id=adding-headers-to-request>Adding headers to request</h4><p>Running a valid request, you should receive the echo body with the new header <code>x-validated-by: my-security-checkpoint</code> and the header <code>x-force-authorized</code> removed:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl exec -n my-app curl -c curl -- curl -s httpbin:8000/get -H "x-force-authorized: true"</code></pre><h4 id=adding-headers-to-response>Adding headers to response</h4><p>Running the same request but showing only the header, you will find the response header added during the Authz check <code>x-add-custom-response-header: added</code>:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl exec -n my-app curl -c curl -- curl -s -I httpbin:8000/get -H "x-force-authorized: true"</code></pre><h4 id=sharing-data-between-filters>Sharing data between filters</h4><p>Finally, you can pass data to the following Envoy filters using <code>dynamic_metadata</code>. This is useful when you want to pass data to another <code>ext_authz</code> filter in the chain or you want to print it in the application logs.</p><figure style=width:75%><div class=wrapper-with-intrinsic-ratio style=padding-bottom:44.210204705163456%><a data-skipendnotes=true href=/v1.24/blog/2024/l7-policy-with-opa/opa3.png title><img class=element-to-stretch src=/v1.24/blog/2024/l7-policy-with-opa/opa3.png alt=Metadata></a></div><figcaption></figcaption></figure><p>To do so, review the access log format you set earlier:</p><pre><code class=language-plain data-expandlinks=true data-repo=istio>[...]
|
||
accessLogFormat: |
|
||
[OPA DEMO] my-new-dynamic-metadata: "%DYNAMIC_METADATA(envoy.filters.http.ext_authz)%"
|
||
[...]</code></pre><p><code>DYNAMIC_METADATA</code> is a reserved keyword to access the metadata object. The rest is the name of the filter that you want to access. In your case, the name <code>envoy.filters.http.ext_authz</code> is created automatically by Istio. You can verify this by dumping the Envoy configuration:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ istioctl pc all deploy/httpbin -n my-app -oyaml | grep envoy.filters.http.ext_authz</code></pre><p>You will see the configurations for the filter.</p><p>Let’s test the dynamic metadata. In the advance rule, you are creating a new metadata entry: <code>{"my-new-metadata": "my-new-value"}</code>.</p><p>Run the request and check the logs of the application:</p><pre><code class=language-bash data-expandlinks=true data-repo=istio>$ kubectl exec -n my-app curl -c curl -- curl -s -I httpbin:8000/get -H "x-force-authorized: true"
|
||
$ kubectl logs -n my-app deploy/httpbin -c istio-proxy --tail 1</code></pre><p>You will see in the output the new attributes configured by OPA Rego rules:</p><pre><code class=language-plain data-expandlinks=true data-repo=istio>[...]
|
||
my-new-dynamic-metadata: "{"my-new-metadata":"my-new-value","decision_id":"8a6d5359-142c-4431-96cd-d683801e889f","ext_authz_duration":7}"
|
||
[...]</code></pre><h2 id=conclusion>Conclusion</h2><p>In this guide, we have shown how to integrate Istio and OPA to enforce policies for a simple microservices application. We also showed how to use Rego to modify the request and response attributes. This is the foundational example for building a platform-wide policy system that can be used by all application teams.</p></div><div class=share-social><div class=heading>Share this post</div><div class=share-buttons><a href="https://www.linkedin.com/shareArticle?mini=true&url=%2fv1.24%2fblog%2f2024%2fl7-policy-with-opa%2f" target=_blank><img class=share-icon src=/v1.24/img/social/linkedin.svg alt="Share to LinkedIn">
|
||
</a><a href="https://twitter.com/intent/tweet?text=Can%20Your%20Platform%20Do%20Policy%3f%20Accelerate%20Teams%20With%20Platform%20L7%20Policy%20Functionality&url=%2fv1.24%2fblog%2f2024%2fl7-policy-with-opa%2f" target=_blank><img class=share-icon src=/v1.24/img/social/twitterx.svg alt="Share to X">
|
||
</a><a href="https://www.facebook.com/sharer/sharer.php?u=%2fv1.24%2fblog%2f2024%2fl7-policy-with-opa%2f" target=_blank><img class=share-icon src=/v1.24/img/social/facebook.svg alt="Share to Facebook"></a></div></div><nav class=pagenav><div class=left><a title="Announcing changes to our TOC charter and our first open election." href=/v1.24/blog/2024/toc-charter-elections/ class=next-link><svg class="icon left-arrow"><use xlink:href="/v1.24/img/icons.svg#left-arrow"/></svg>More community leadership: Regularly electing the Istio Technical Oversight Committee</a></div><div class=right><a title="Read about Luca Cavallin's experience getting started with Istio." href=/v1.24/blog/2024/link-the-istio-service-mesh-for-people-who-have-stuff-to-do/ class=next-link>External post: The Istio Service Mesh for People Who Have Stuff to Do<svg class="icon right-arrow"><use xlink:href="/v1.24/img/icons.svg#right-arrow"/></svg></a></div></nav></article><footer class=footer><div class="footer-wrapper container-l"><div class="user-links footer-links"><a class=channel title='GitHub is where development takes place on Istio code' href=https://github.com/istio/community aria-label=GitHub><svg class="icon github"><use xlink:href="/v1.24/img/icons.svg#github"/></svg>
|
||
</a><a class=channel title="Access our team drive if you'd like to take a look at the Istio technical design documents" href=https://groups.google.com/forum/#!forum/istio-team-drive-access aria-label="team drive"><svg class="icon drive"><use xlink:href="/v1.24/img/icons.svg#drive"/></svg>
|
||
</a><a class=channel title='Interactively discuss issues with the Istio community on Slack' href=https://slack.istio.io aria-label=slack><svg class="icon slack"><use xlink:href="/v1.24/img/icons.svg#slack"/></svg>
|
||
</a><a class=channel title='Stack Overflow is where you can ask questions and find curated answers on deploying, configuring, and using Istio' href=https://stackoverflow.com/questions/tagged/istio aria-label="Stack Overflow"><svg class="icon stackoverflow"><use xlink:href="/v1.24/img/icons.svg#stackoverflow"/></svg>
|
||
</a><a class=channel title='Follow us on LinkedIn to get the latest news' href=https://www.linkedin.com/company/istio/ aria-label=LinkedIn><svg class="icon linkedin"><use xlink:href="/v1.24/img/icons.svg#linkedin"/></svg>
|
||
</a><a class=channel title='Follow us on Twitter to get the latest news' href=https://twitter.com/IstioMesh aria-label=Twitter><svg class="icon twitter"><use xlink:href="/v1.24/img/icons.svg#twitter"/></svg>
|
||
</a><a class=channel title='Follow us on Bluesky to get the latest news' href=https://bsky.app/profile/istio.io aria-label=Bluesky><svg class="icon bluesky"><use xlink:href="/v1.24/img/icons.svg#bluesky"/></svg>
|
||
</a><a class=channel title='Follow us on Mastodon to get the latest news' href=https://mastodon.social/@istio aria-label=Mastodon rel=me><svg class="icon mastodon"><use xlink:href="/v1.24/img/icons.svg#mastodon"/></svg></a></div><hr class=footer-separator role=separator><div class="info footer-info"><a class=logo href=/v1.24/ aria-label=logotype><svg width="128" height="60" viewBox="0 0 128 60"><path d="M58.434 48.823A.441.441.0 0158.3 48.497V22.583a.444.444.0 01.134-.326.446.446.0 01.327-.134h3.527a.447.447.0 01.325.134.447.447.0 01.134.326v25.914a.443.443.0 01-.134.326.444.444.0 01-.325.134h-3.527a.444.444.0 01-.327-.134z"/><path d="m70.969 48.477a6.556 6.556.0 01-2.818-1.955 4.338 4.338.0 01-1-2.78v-.345a.443.443.0 01.134-.326.444.444.0 01.326-.135h3.374a.444.444.0 01.326.135.445.445.0 01.134.326v.077a2.014 2.014.0 001.054 1.667 4.672 4.672.0 002.664.709 4.446 4.446.0 002.492-.633 1.862 1.862.0 00.958-1.591 1.426 1.426.0 00-.786-1.322 12.7 12.7.0 00-2.549-.939l-1.457-.46a21.526 21.526.0 01-3.3-1.227 6.57 6.57.0 01-2.262-1.783 4.435 4.435.0 01-.92-2.894 5.081 5.081.0 012.109-4.275 8.993 8.993.0 015.558-1.591 10.445 10.445.0 014.1.748 6.3 6.3.0 012.722 2.07 5 5 0 01.958 3.009.441.441.0 01-.134.326.441.441.0 01-.325.134h-3.258a.441.441.0 01-.326-.134.443.443.0 01-.134-.326 1.974 1.974.0 00-.978-1.667 4.647 4.647.0 00-2.665-.671 4.741 4.741.0 00-2.435.556 1.724 1.724.0 00-.938 1.553 1.512 1.512.0 00.9 1.4 15.875 15.875.0 003.01 1.055l.843.229a27.368 27.368.0 013.412 1.246 6.67 6.67.0 012.338 1.763 4.387 4.387.0 01.958 2.933 4.988 4.988.0 01-2.146 4.275 9.543 9.543.0 01-5.712 1.552 11.626 11.626.0 01-4.227-.709z"/><path d="m97.039 32.837a.443.443.0 01-.326.135h-3.911a.169.169.0 00-.191.192v9.239a2.951 2.951.0 00.632 2.108 2.7 2.7.0 002.013.652h1.15a.444.444.0 01.325.134.441.441.0 01.134.326v2.875a.471.471.0 01-.459.5l-1.994.039a8 8 0 01-4.524-1.035q-1.495-1.035-1.533-3.91V33.166A.17.17.0 0088.164 32.974H85.978A.441.441.0 0185.652 32.839.441.441.0 0185.518 32.513V29.83a.441.441.0 01.134-.326.444.444.0 01.326-.135h2.186a.169.169.0 00.191-.192v-4.485a.438.438.0 01.134-.326.44.44.0 01.325-.134h3.336a.443.443.0 01.325.134.442.442.0 01.135.326v4.485a.169.169.0 00.191.192h3.911a.446.446.0 01.326.135.446.446.0 01.134.326v2.683a.446.446.0 01-.133.324z"/><path d="m101.694 25.917a2.645 2.645.0 01-.767-1.955 2.65 2.65.0 01.767-1.955 2.65 2.65.0 011.955-.767 2.65 2.65.0 011.955.767 2.652 2.652.0 01.767 1.955 2.647 2.647.0 01-.767 1.955 2.646 2.646.0 01-1.955.767 2.645 2.645.0 01-1.955-.767zm-.211 22.906a.441.441.0 01-.134-.326V29.79a.444.444.0 01.134-.326.446.446.0 01.326-.134h3.527a.446.446.0 01.326.134.445.445.0 01.134.326v18.707a.443.443.0 01-.134.326.443.443.0 01-.326.134h-3.527a.443.443.0 01-.326-.134z"/><path d="m114.019 47.734a8.1 8.1.0 01-3.047-4.255 14.439 14.439.0 01-.652-4.37 14.3 14.3.0 01.614-4.371A7.869 7.869.0 01114 30.56a9.072 9.072.0 015.252-1.5 8.543 8.543.0 015.041 1.5 7.985 7.985.0 013.009 4.14 12.439 12.439.0 01.69 4.37 13.793 13.793.0 01-.651 4.37 8.255 8.255.0 01-3.028 4.275 8.475 8.475.0 01-5.1 1.553 8.754 8.754.0 01-5.194-1.534zm7.629-3.1a4.536 4.536.0 001.476-2.262 11.335 11.335.0 00.383-3.221 10.618 10.618.0 00-.383-3.22 4.169 4.169.0 00-1.457-2.243 4.066 4.066.0 00-2.531-.785 3.942 3.942.0 00-2.453.785 4.376 4.376.0 00-1.5 2.243 11.839 11.839.0 00-.383 3.22 11.84 11.84.0 00.383 3.221 4.222 4.222.0 001.476 2.262 4.075 4.075.0 002.549.8 3.8 3.8.0 002.44-.809z"/><path d="m15.105 32.057v15.565a.059.059.0 01-.049.059L.069 50.25A.06.06.0 01.005 50.167l14.987-33.47a.06.06.0 01.114.025z"/><path d="m17.631 23.087v24.6a.06.06.0 00.053.059l22.449 2.507a.06.06.0 00.061-.084L17.745.032a.06.06.0 00-.114.024z"/><path d="m39.961 52.548-24.833 7.45a.062.062.0 01-.043.0L.079 52.548a.059.059.0 01.026-.113h39.839a.06.06.0 01.017.113z"/></svg></a><div class=footer-languages><a tabindex=-1 lang=en id=switch-lang-en class="footer-languages-item active"><svg class="icon tick"><use xlink:href="/v1.24/img/icons.svg#tick"/></svg>
|
||
English
|
||
</a><a tabindex=-1 lang=zh id=switch-lang-zh class=footer-languages-item>中文
|
||
</a><a tabindex=-1 lang=uk id=switch-lang-uk class=footer-languages-item>Українська</a></div></div><ul class=footer-policies><li class=footer-policies-item><a class=footer-policies-link href=https://www.linuxfoundation.org/legal/terms>Terms and Conditions
|
||
</a>|
|
||
<a class=footer-policies-link href=https://www.linuxfoundation.org/legal/privacy-policy>Privacy policy
|
||
</a>|
|
||
<a class=footer-policies-link href=https://www.linuxfoundation.org/legal/trademark-usage>Trademarks
|
||
</a>|
|
||
<a class=footer-policies-link href=https://github.com/istio/istio.io/edit/release-1.24/content/en/blog/2024/l7-policy-with-opa/index.md>Edit this Page on GitHub</a></li></ul><div class=footer-base><span class=footer-base-copyright>© 2024 the Istio Authors.</span>
|
||
<span class=footer-base-version>Version
|
||
Archive
|
||
1.24.3</span><ul class=footer-base-releases><li class=footer-base-releases-item><a tabindex=-1 class=footer-base-releases-link onclick='return navigateToUrlOrRoot("https://istio.io/blog/2024/l7-policy-with-opa/"),!1'>current release</a></li><li class=footer-base-releases-item><a tabindex=-1 class=footer-base-releases-link onclick='return navigateToUrlOrRoot("https://preliminary.istio.io/blog/2024/l7-policy-with-opa/"),!1'>next release</a></li><li class=footer-base-releases-item><a tabindex=-1 class=footer-base-releases-link href=https://istio.io/archive>older releases</a></li></ul></div></div></footer><div id=scroll-to-top-container aria-hidden=true><button id=scroll-to-top title='Back to top' tabindex=-1><svg class="icon top"><use xlink:href="/v1.24/img/icons.svg#top"/></svg></button></div></body></html> |