Auth chaining
Chain multiple authentication and authorization methods together with boolean expressions.
When you configure multiple authentication methods, the external auth service processes each named config in the order that they appear in the configs list of an AuthConfig resource. By default, all configs must succeed for the request to be authorized. To change this behavior, you can use the booleanExpr field to chain configs together with logical operators.
With booleanExpr, you can build expressions such as:
apikey && opa- Both API key and OPA must succeed.passthrough || basic- Either passthrough or basic auth must succeed.(jwt || oidc) && extservice- Either JWT or OIDC must succeed, and then an external service check must also succeed.
Each operand in the expression must match the name field of a config in the configs list. The expression is evaluated left to right, honors parentheses, and supports short-circuiting. For example, if the first operand of an || succeeds, the second operand is skipped.
Supported operators
| Operator | Description |
|---|---|
&& | Logical AND. Both operands must succeed. |
|| | Logical OR. At least one operand must succeed. |
! | Logical NOT. Inverts the result of the operand. |
() | Parentheses. Groups expressions to control evaluation order. |
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
Combine API key and OPA authorization
The following example sets up an AuthConfig that requires both API key authentication and OPA authorization to succeed. The API key config verifies the caller’s identity, and the OPA config enforces a Rego policy that controls which request paths are allowed.
Step 1: Create an API key
Store an API key in a Kubernetes secret so that the external auth service can verify incoming requests.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: infra-apikey
namespace: httpbin
labels:
team: infrastructure
type: extauth.solo.io/apikey
stringData:
api-key: my-test-api-key
EOFStep 2: Create a Rego policy
Create a Rego rule that allows requests only to specific paths. This example allows
GETrequests to paths starting with/getor/status/200.cat <<EOF > policy.rego package test default allow = false allow { startswith(input.http_request.path, "/get") input.http_request.method == "GET" } allow { input.http_request.path == "/status/200" input.http_request.method == "GET" } EOFStore the OPA policy in a Kubernetes config map.
kubectl -n httpbin create configmap httpbin-rego --from-file=policy.rego
Step 3: Create the AuthConfig
Create an AuthConfig resource that chains API key authentication and OPA authorization together with a boolean expression.
kubectl apply -f - <<EOF
apiVersion: extauth.solo.io/v1
kind: AuthConfig
metadata:
name: apikey-opa-auth
namespace: httpbin
spec:
booleanExpr: "apikey && opa"
configs:
- name: apikey
apiKeyAuth:
headerName: api-key
labelSelector:
team: infrastructure
- name: opa
opaAuth:
modules:
- name: httpbin-rego
namespace: httpbin
query: "data.test.allow == true"
EOFReview the following table to understand this configuration.
| Setting | Description |
|---|---|
booleanExpr | A logical expression that controls how the named configs are evaluated. The apikey && opa expression requires both the apikey and opa configs to succeed. The expression is evaluated left to right and supports short-circuiting, so if apikey fails, opa is skipped. |
configs.name: apikey | The name of the API key auth config. The apiKeyAuth setting checks for a valid API key in the api-key request header and matches it against secrets with the team: infrastructure label. The name must match the operand used in the booleanExpr. |
configs.name: opa | The name of the OPA auth config. The opaAuth setting references a Rego policy stored in a config map. In the example, the policy allows GET requests only to paths starting with /get or exactly matching /status/200. |
Step 4: Create the traffic policy
Create an EnterpriseKgatewayTrafficPolicy resource that refers to the AuthConfig. 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: apikey-opa-policy
namespace: kgateway-system
spec:
targetRefs:
- name: http
group: gateway.networking.k8s.io
kind: Gateway
entExtAuth:
authConfigRef:
name: apikey-opa-auth
namespace: httpbin
EOFStep 5: Verify the AuthConfig
Verify that the AuthConfig is ACCEPTED.
kubectl get authconfig apikey-opa-auth -n httpbin -o yamlStep 6: Test the combined auth
Send a request without any credentials. The request is denied with a 401 because no API key is provided, and the OPA check is skipped due to short-circuiting.
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 401 Unauthorized < content-length: 0 < server: envoySend a request with a valid API key to a path that is not allowed by the OPA policy. The API key check succeeds, but the OPA policy denies the request with a 403.
curl -v http://$INGRESS_GW_ADDRESS:8080/headers -H "host: www.example.com:8080" \ -H "api-key: my-test-api-key"curl -v localhost:8080/headers -H "host: www.example.com" \ -H "api-key: my-test-api-key"Example output:
< HTTP/1.1 403 Forbidden < content-length: 0 < server: envoySend a request with a valid API key to an allowed path. Both the API key and OPA checks succeed, and the request is allowed.
curl -v http://$INGRESS_GW_ADDRESS:8080/get -H "host: www.example.com:8080" \ -H "api-key: my-test-api-key"curl -v localhost:8080/get -H "host: www.example.com" \ -H "api-key: my-test-api-key"Example output:
< HTTP/1.1 200 OK < access-control-allow-credentials: true < access-control-allow-origin: * < content-type: application/json; encoding=utf-8 < server: envoy
More boolean expression examples
You can test these examples by following a similar setup flow as the previous API key and OPA walkthrough, substituting the AuthConfig with the examples in the following sections.
Fallback authentication
Use || to allow either API key or basic auth to succeed, so clients can authenticate with whichever method they have available.
apiVersion: extauth.solo.io/v1
kind: AuthConfig
metadata:
name: fallback-auth
namespace: httpbin
spec:
booleanExpr: "apikey || basic"
configs:
- name: apikey
apiKeyAuth:
headerName: api-key
labelSelector:
team: infrastructure
- name: basic
basicAuth:
apr:
users:
user:
hashedPassword: <password>
salt: TYiryv0/With this configuration, if a valid API key is provided, the basic auth check is skipped entirely due to short-circuiting. If no API key is present, the external auth service falls back to checking basic auth credentials.
| Scenario | Expression evaluation | Response |
|---|---|---|
| Valid API key provided | apikey succeeds, basic skipped | 200 Success |
| No API key, valid basic auth credentials | apikey fails, basic succeeds | 200 Success |
| No API key, invalid basic auth credentials | apikey fails, basic fails | 401 Unauthorized |
| Invalid API key, no basic auth credentials | apikey fails, basic fails | 401 Unauthorized |
Authentication with authorization
Use parentheses to group JWT and OIDC authentication, and then require an OPA authorization check. This example requires an external identity provider, such as Keycloak, for the OIDC configuration.
apiVersion: extauth.solo.io/v1
kind: AuthConfig
metadata:
name: auth-authz
namespace: httpbin
spec:
booleanExpr: "(jwt || oidc) && opa"
configs:
- name: jwt
jwt: {}
- name: oidc
oauth2:
oidcAuthorizationCode:
appUrl: https://example.com
callbackPath: /callback
clientId: ${CLIENT_ID}
clientSecretRef:
name: oidc-secret
namespace: httpbin
issuerUrl: https://your-issuer.example.com
scopes:
- openid
- name: opa
opaAuth:
modules:
- name: httpbin-rego
namespace: httpbin
query: "data.test.allow == true"The following table shows the expected responses for different authentication scenarios.
| Scenario | Expression evaluation | Response |
|---|---|---|
| Valid JWT provided, OPA policy passes | jwt succeeds, oidc skipped, opa succeeds | 200 Success |
| Valid JWT provided, OPA policy fails | jwt succeeds, oidc skipped, opa fails | 403 Forbidden |
| No JWT, OIDC succeeds, OPA policy passes | jwt fails, oidc succeeds, opa succeeds | 200 Success |
| No JWT, OIDC succeeds, OPA policy fails | jwt fails, oidc succeeds, opa fails | 403 Forbidden |
| No JWT, OIDC fails | jwt fails, oidc fails, opa skipped | 401 Unauthorized |
Cleanup
You can optionally remove the resources that you set up as part of this guide.kubectl delete authconfig apikey-opa-auth -n httpbin
kubectl delete EnterpriseKgatewayTrafficPolicy apikey-opa-policy -n kgateway-system
kubectl delete configmap httpbin-rego -n httpbin
kubectl delete secret infra-apikey -n httpbin
rm policy.rego