`contains` provides an alternative way to declare partial sets:
p contains x {
x := { "foo": "bar"
}
which is the same as
p[x] {
x := { "foo": "bar"
}
The keyword is enabled by importing `future.keywords.contains`, and
when it _is enabled_, the format will be used for all partial sets in
that file for pretty-printing.
`if` is a new keyword allowing for more readable rule definitions:
The syntax is
NAME [if] { EXPR [EXPR...] }
and the is a shorthand allows dropping the braces around the expression
if there is only one:
NAME if EXPR
For example, this allows expressions like
allow if not deny
f(xs) if every x in xs { x != "foo" }
The one exception here are partial sets: they cannot use `if` UNLESS
they use `contains`:
p[x] { x := "foo" } # valid
p contains x { x := "bar" } # valid
p contains x if { x := "bar" } # valid
p[x] if { x := "foo" } # invalid
This is because we want to interpret that differently (as an object
rule defining `p.foo = true`) in the near future.
The formatter works in the same way: if `future.keywords.if` is imported, it
will be used where it can be used.
We don't want to be too eager when it comes to introducing syntactic sugar.
So this will be rewritten, because head and body expression are on the same
line:
p := 5 if { time.day_of_week() == "Monday" }
# => p := 5 if time.day_of_week() == "Monday"
but this won't:
p := 5 if {
time.day_of_week() == "Monday"
}
The rationale here is that if the policy author decided that they want this on
an extra line, we won't mess with it.
This also sidesteps the need to check if both the head and the single body
expression have a comment.
This change includes various docs updates. Notable exceptions are the GK docs,
since it will take a while for these keywords to be come available there; and
the frontpage: merging a PR would update the frontpage immediately, and we
don't want to show something there that isn't available in the latest release.
Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
6.9 KiB
title, kind, weight
| title | kind | weight |
|---|---|---|
| Tutorial: Istio | envoy | 11 |
Istio is an open source service mesh for managing the different microservices that make up a cloud-native application. Istio provides a mechanism to customize the Envoy configuration generated by Istio Pilot using EnvoyFilter.
This tutorial shows how Istio's EnvoyFilter can be configured to include Envoy's External Authorization filter to delegate authorization decisions to OPA.
Prerequisites
This tutorial requires Kubernetes 1.20 or later. To run the tutorial locally ensure you start a cluster with Kubernetes version 1.20+, we recommend using minikube or KIND.
The tutorial also requires Istio v1.8.0 or later. It assumes you have Istio deployed on top of Kubernetes. See Istio's Quick Start page to get started.
Steps
1. Install OPA-Envoy
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/opa-envoy-plugin/main/examples/istio/quick_start.yaml
The quick_start.yaml manifest defines the following resources:
-
External Authorization Filter to direct authorization checks to the OPA-Envoy sidecar. See
kubectl -n istio-system get envoyfilter ext-authzfor details. -
Kubernetes namespace (
opa-istio) for OPA-Envoy control plane components. -
Kubernetes admission controller in the
opa-istionamespace that automatically injects the OPA-Envoy sidecar into pods in namespaces labelled withopa-istio-injection=enabled. -
OPA configuration file and an OPA policy into ConfigMaps in the namespace where the app will be deployed, e.g.,
default. The following is the example OPA policy:- alice is granted a guest role and can perform a
GETrequest to/productpage. - bob is granted an admin role and can perform a
GETto/productpageand/api/v1/products.
package istio.authz import future.keywords import input.attributes.request.http as http_request import input.parsed_path default allow := false allow if { parsed_path[0] == "health" http_request.method == "GET" } allow if { some r in roles_for_user r in required_roles } roles_for_user contains r if { some r in user_roles[user_name] } required_roles contains r if { some perm in role_perms[r] perm.method == http_request.method perm.path == http_request.path } user_name := parsed if { [_, encoded] := split(http_request.headers.authorization, " ") [parsed, _] := split(base64url.decode(encoded), ":") } user_roles := { "alice": ["guest"], "bob": ["admin"], } role_perms := { "guest": [{"method": "GET", "path": "/productpage"}], "admin": [ {"method": "GET", "path": "/productpage"}, {"method": "GET", "path": "/api/v1/products"}, ], }OPA is configured to query for the
data.istio.authz.allowdecision. If the response istruethe operation is allowed, otherwise the operation is denied. Sample input received by OPA is shown below:data.istio.authz.allow{ "attributes": { "request": { "http": { "method": "GET", "path": "/productpage", "headers": { "authorization": "Basic YWxpY2U6cGFzc3dvcmQ=" } } } } }With the input value above, the answer is:
An example of the complete input received by OPA can be seen here.
In typical deployments the policy would either be built into the OPA container image or it would fetched dynamically via the Bundle API. ConfigMaps are used in this tutorial for test purposes.
- alice is granted a guest role and can perform a
2. Enable automatic injection of the Istio Proxy and OPA-Envoy sidecars in the namespace where the app will be deployed, e.g., default
kubectl label namespace default opa-istio-injection="enabled"
kubectl label namespace default istio-injection="enabled"
3. Deploy the BookInfo application and make it accessible outside the cluster
kubectl apply -f https://raw.githubusercontent.com/istio/istio/master/samples/bookinfo/platform/kube/bookinfo.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/master/samples/bookinfo/networking/bookinfo-gateway.yaml
4. Set the SERVICE_HOST environment variable in your shell to the public IP/port of the Istio Ingress gateway
Run this command in a new terminal window to start a Minikube tunnel that sends traffic to your Istio Ingress Gateway:
minikube tunnel
Check that the Service shows an EXTERNAL-IP:
kubectl -n istio-system get service istio-ingressgateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway LoadBalancer 10.98.42.178 127.0.0.1 15021:32290/TCP,80:30283/TCP,443:32497/TCP,31400:30216/TCP,15443:30690/TCP 5s
minikube:
export SERVICE_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
For other platforms see the Istio documentation on determining ingress IP and ports.
5. Exercise the OPA policy
Check that alice can access /productpage BUT NOT /api/v1/products.
curl --user alice:password -i http://$SERVICE_HOST/productpage
curl --user alice:password -i http://$SERVICE_HOST/api/v1/products
Check that bob can access /productpage AND /api/v1/products.
curl --user bob:password -i http://$SERVICE_HOST/productpage
curl --user bob:password -i http://$SERVICE_HOST/api/v1/products
Wrap Up
Congratulations for finishing the tutorial !
This tutorial showed how Istio's EnvoyFilter can be configured to use OPA as an External authorization service.
This tutorial also showed a sample OPA policy that returns a boolean decision
to indicate whether a request should be allowed or not.
More details about the tutorial can be seen here.