OPA with Rego rules in config maps
Enforce Open Policy Agent (OPA) policies with Rego rules in Kubernetes config maps.
The Solo Enterprise for kgateway external auth service can use an OPA module to enforce Rego rules stored in Kubernetes config maps. You create an AuthConfig resource that references the config map, and the external auth service loads and evaluates the rules on each request.
This guide walks through two examples:
- Simple path-based authorization: Allow or deny requests by path and HTTP method.
- Response manipulation and header control: Customize response status codes, headers, and body, and strip sensitive request headers before they reach the upstream.
Before you begin
Follow the Get started guide to install Solo Enterprise for kgateway.
- Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.
Get the external address of the gateway and save it in an environment variable.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESSkubectl port-forward deployment/http -n kgateway-system 8080:8080
Path-based authorization
This example creates a Rego rule that allows or denies requests based on the request path and HTTP method. This approach is convenient for basic environments, quick testing, and small OPA use cases.
Setup
Create a Rego rule.
cat <<EOF > policy.rego package test default allow = false allow { startswith(input.http_request.path, "/anything") input.http_request.method == "GET" } allow { input.http_request.path == "/status/200" any({input.http_request.method == "GET", input.http_request.method == "DELETE" }) } EOFReview the following table to understand this configuration.
Setting Description default allow = falseDenies all requests by default. allow {...}Allows requests that match either condition: - The path starts with
/anythingand the HTTP method isGET. - The path is exactly
/status/200and the HTTP method is eitherGETorDELETE.
If you decide to modify the policy example and add in your own allow rules, make sure to also use thepackage testvalue in your policy.- The path starts with
Store the OPA policy in a Kubernetes config map.
kubectl -n httpbin create configmap allow-get-users --from-file=policy.regoCreate an AuthConfig resource and add your external authentication rules. The following example configures OPA authentication with the Rego rules that you created earlier.
kubectl apply -f - <<EOF apiVersion: extauth.solo.io/v1 kind: AuthConfig metadata: name: opa-auth namespace: httpbin spec: configs: - opaAuth: modules: - name: allow-get-users namespace: httpbin query: "data.test.allow == true" EOFReview the following table to understand this configuration.
Setting Description opaAuthConfigure the OPA authentication details. modulesRefer to the name and namespace of the config map that has the OPA policy. Then, Solo Enterprise for kgateway can use the OPA policy to resolve the query. This example uses the config map that you previously created. queryThe query that determines the authentication decision. The result of this query must be either a boolean or an array with a boolean as the first element. A value of truemeans that the request is authorized. Any other value or error means that the request is denied. In this example,data.test.allowis set totrue.datais the section in the config map.test.allowis part of the OPA policy that you previously created. Access is allowed only if the response meets theallowconditions in the policy.Create an EnterpriseKgatewayTrafficPolicy resource that refers to the AuthConfig that you created. The following policy applies external auth to all routes that the Gateway serves.
kubectl apply -f - <<EOF apiVersion: enterprisekgateway.solo.io/v1alpha1 kind: EnterpriseKgatewayTrafficPolicy metadata: name: opa-auth namespace: kgateway-system spec: targetRefs: - name: http group: gateway.networking.k8s.io kind: Gateway entExtAuth: authConfigRef: name: opa-auth namespace: httpbin EOFVerify that the AuthConfig is
ACCEPTED.kubectl get authconfig opa-auth -n httpbin -o yaml
Verify the OPA policy
Send a request to the httpbin app on a path that is not allowed by the OPA policy. Verify that your request is denied and that you get back a 403 HTTP response code.
curl -v http://$INGRESS_GW_ADDRESS:8080/headers -H "host: www.example.com:8080"curl -v localhost:8080/headers -H "host: www.example.com"Example output:
< HTTP/1.1 403 Forbidden < server: envoy < content-length: 0Send another request to the httpbin app. This time, you include the
/status/200path that is allowed in the OPA policy. Verify that the request succeeds and that you get back a 200 HTTP response code.curl -v http://$INGRESS_GW_ADDRESS:8080/status/200 -H "host: www.example.com:8080"curl -v localhost:8080/status/200 -H "host: www.example.com"Example output:
< HTTP/1.1 200 OK < access-control-allow-credentials: true < access-control-allow-origin: * < content-length: 0 < x-envoy-upstream-service-time: 1 < server: envoy
Cleanup
You can optionally remove the resources that you set up as part of this guide.kubectl delete authconfig opa-auth -n httpbin
kubectl delete EnterpriseKgatewayTrafficPolicy opa-auth -n kgateway-system
kubectl delete configmap allow-get-users -n httpbin
rm policy.regoResponse manipulation and header control
OPA policies can do more than allow or deny requests. You can use Rego rules to manipulate response headers and body, remove sensitive request headers before they reach the upstream, and set dynamic metadata for use by other filters such as rate limiting.
The following example creates a policy that checks for an api-key request header and controls the response based on the result.
Setup
If you still have the resources from the path-based authorization example, delete them first.
kubectl delete authconfig opa-auth -n httpbin kubectl delete EnterpriseKgatewayTrafficPolicy opa-auth -n kgateway-system kubectl delete configmap allow-get-users -n httpbin rm policy.regoCreate a config map with an advanced Rego rule. This policy checks for a valid
api-keyheader and controls the response behavior for both allowed and denied requests.kubectl apply -f - <<EOF apiVersion: v1 kind: ConfigMap metadata: name: opa-policy namespace: httpbin data: policy.rego: |- package httpbin import future.keywords.if # Default response when the request is denied: return a 403 with a # custom body and response header. default allow := { "allow": false, "response_headers_to_add": {"x-opa-deny-reason": "missing-or-invalid-api-key"}, "body": "Request denied by OPA policy", "http_status": 403, } # When the api-key header matches, allow the request. Strip the # api-key header so it is not forwarded to the upstream, and add a # response header visible only to the client. allow := returned_value if { input.http_request.headers["api-key"] == "my-secret-key" returned_value := { "allow": true, "http_status": 200, "headers": {"x-validated-by": "opa-policy"}, "response_headers_to_add": {"x-opa-status": "approved"}, "request_headers_to_remove": ["api-key"], } } EOFReview the following table to understand this configuration.
Setting Description default allowThe fallback decision when no other rule matches. Returns a 403 status, a custom body, and an x-opa-deny-reasonresponse header.allow := returned_value if {...}Allows the request when the api-keyheader equalsmy-secret-key. Strips theapi-keyheader from the upstream request and addsx-validated-byandx-opa-statusheaders.Create an AuthConfig that references the config map. The
queryfield points to theallowrule in thehttpbinpackage.kubectl apply -f - <<EOF apiVersion: extauth.solo.io/v1 kind: AuthConfig metadata: name: opa-advanced namespace: httpbin spec: configs: - opaAuth: modules: - name: opa-policy namespace: httpbin query: "data.httpbin.allow" EOFCreate an EnterpriseKgatewayTrafficPolicy that references the AuthConfig.
kubectl apply -f - <<EOF apiVersion: enterprisekgateway.solo.io/v1alpha1 kind: EnterpriseKgatewayTrafficPolicy metadata: name: opa-advanced namespace: kgateway-system spec: targetRefs: - name: http group: gateway.networking.k8s.io kind: Gateway entExtAuth: authConfigRef: name: opa-advanced namespace: httpbin EOFVerify that the AuthConfig is
ACCEPTED.kubectl get authconfig opa-advanced -n httpbin -o yaml
Verify the advanced OPA policy
Send a request without an API key. The request is denied with a 403 status, a custom body, and an
x-opa-deny-reasonresponse header.curl -v http://$INGRESS_GW_ADDRESS:8080/get -H "host: www.example.com:8080"curl -v localhost:8080/get -H "host: www.example.com"Example output:
< HTTP/1.1 403 Forbidden < x-opa-deny-reason: missing-or-invalid-api-key < content-length: 29 < content-type: text/plain < server: envoy < Request denied by OPA policySend the request again with a valid
api-keyheader. The request succeeds. Notice that theapi-keyheader is removed from the upstream request, and thex-validated-byandx-opa-statusresponse headers are added.curl -v http://$INGRESS_GW_ADDRESS:8080/get -H "host: www.example.com:8080" -H "api-key: my-secret-key"curl -v localhost:8080/get -H "host: www.example.com" -H "api-key: my-secret-key"Example output:
< HTTP/1.1 200 OK < x-opa-status: approved < x-validated-by: opa-policy < access-control-allow-credentials: true < access-control-allow-origin: * < content-type: application/json; encoding=utf-8 < server: envoyIn the JSON response body from httpbin, notice that the
api-keyheader is not present in the forwarded headers, confirming the OPA policy stripped it before reaching the upstream.
Cleanup
You can optionally remove the resources that you set up as part of this guide.kubectl delete authconfig opa-advanced -n httpbin
kubectl delete EnterpriseKgatewayTrafficPolicy opa-advanced -n kgateway-system
kubectl delete configmap opa-policy -n httpbin