Chapter 6. Configure and deploy the PlanPolicy extension
With a PlanPolicy extension, you can define different service tiers, or plans, with their associated rate limits and automatically categorize authenticated users into these plans.
The PlanPolicy extension for Connectivity Link is a Technology Preview feature only. Technology Preview features are not supported with Red Hat production service level agreements (SLAs) and might not be functionally complete. Red Hat does not recommend using them in production. These features offer early access to upcoming product features, enabling customers to test functionality and give feedback during the development process.
For more information about the support scope of Red Hat Technology Preview features, see Technology Preview Features Support Scope.
6.1. PlanPolicy for tiered service offerings Copy linkLink copied to clipboard!
You can use a PlanPolicy object to implement tiered service offerings where different users receive different rate limits based on their subscription plan or other attributes.
The PlanPolicy object uses the spec.plans.limits parameter to enforce rate limits at the plan tier level instead of the individual subscriber level.
A PlanPolicy works in conjunction with AuthPolicy and RateLimitPolicy custom resources (CRs) in the following sequence:
-
An
AuthPolicyCR authenticates a user request with stored identity metadata, including their plan information. -
The
PlanPolicyobject evaluates CEL predicate expressions against the authenticated identity to determine the user’s plan tier. - Connectivity Link then enforces the rate limits defined for the user-matched plan tier.
The PlanPolicy object has the following known limitations:
-
A
PlanPolicyobject can only targetHTTPRoute,GRPCRoute, andGatewaycustom resources defined in the same namespace as thePlanPolicyobject. - Plan predicates are evaluated in order. More specific predicates must appear before more general ones to ensure correct plan assignment.
- Plan identification requires authentication to be configured through an AuthPolicy.
6.1.1. Create an AuthPolicy with API key authentication Copy linkLink copied to clipboard!
You can configure API key authentication on an HTTPRoute object by deploying an AuthPolicy custom resource (CR) and creating per-plan API key secrets, so that Connectivity Link can identify which rate-limiting plan applies to each request.
Prerequisites
- You installed Connectivity Link.
-
You installed OpenShift CLI (
oc). - You are logged in to your cluster.
-
You created the
HTTPRouteobject for your deployment.
A PlanPolicy can target an HTTPRoute, GRPCRoute, or Gateway custom resource (CR).
Procedure
Create an
AuthPolicyCR that targets theHTTPRouteobject and enables API key authentication by using the following example:Example API key
AuthPolicyCRapiVersion: kuadrant.io/v1 kind: AuthPolicy metadata: name: <sample_app> namespace: ${KUADRANT_DEVELOPER_NS} spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: <sample_app> rules: authentication: "api-key-plan": apiKey: selector: matchLabels: app: <sample_app> allNamespaces: true credentials: authorizationHeader: prefix: APIKEY-
Replace
<sample_app>with the name of your application. -
Replace
$KUADRANT_DEVELOPER_NSwith the namespace for the object. -
The
spec.rules.authentication."api-key-plan".apiKey.selector.matchLabelsfield value points to the API keySecretsobjects that carry theapp: <sample_app>label. -
Setting
spec.rules.rules.authentication."api-key-plan".apiKey.selector.allNamespaces: trueallows secrets from any namespace to be matched.
-
Replace
Apply the policy by running the following command:
$ oc apply -f <authpolicy.yaml>Replace
<authpolicy.yaml>with the name of your YAML.
6.1.2. Verify the acceptance of the AuthPolicy CR Copy linkLink copied to clipboard!
You can confirm the acceptance of the AuthPolicy custom resource (CR) by running a command.
Procedure
Confirm that the
AuthPolicyCR was accepted by checking its status by running the following command:$ oc get authpolicy <sample_app> -n $(KUADRANT_DEVELOPER_NS) -o jsonpath='{.status.conditions[?(@.type=="Accepted")].message}{"\n"}{.status.conditions[?(@.type=="Enforced")].message}'-
Replace
<sample_app>with the name of your application. Replace
$(KUADRANT_DEVELOPER_NS)with the namespace for the object.Example output
AuthPolicy has been accepted AuthPolicy has been successfully enforced
-
Replace
6.1.3. Create API key Secret objects Copy linkLink copied to clipboard!
You can create API key 'Secret` objects for each plan tier.
The following example creates API key Secret objects for each plan tier that you want to use. The following example uses API keys to authenticate the requests. Other options, such as OpenID Connect, might be more appropriate for production use. Use the "least-access" approach that is best for your use case.
Procedure
Create API key
Secretobjects for each plan tier that you want to use. EachSecretmust carry theauthorino.kuadrant.io/managed-by: authorinolabel and asecret.kuadrant.io/plan-idannotation that identifies the plan tier as shown in the following example:Example
Secretobject for plan tiersapiVersion: v1 kind: Secret metadata: name: <gold_key> namespace: ${KUADRANT_DEVELOPER_NS} labels: authorino.kuadrant.io/managed-by: authorino app: <sample_app> annotations: secret.kuadrant.io/plan-id: gold stringData: api_key: <gold_api_key_value> type: Opaque --- apiVersion: v1 kind: Secret metadata: name: <silver_key> namespace: ${KUADRANT_DEVELOPER_NS} labels: authorino.kuadrant.io/managed-by: authorino app: <sample_app> annotations: secret.kuadrant.io/plan-id: silver stringData: api_key: <silver_api_key_value> type: Opaque --- apiVersion: v1 kind: Secret metadata: name: <bronze_key> namespace: ${KUADRANT_DEVELOPER_NS} labels: authorino.kuadrant.io/managed-by: authorino app: <sample_app> annotations: secret.kuadrant.io/plan-id: bronze stringData: api_key: <bronze_api_key_value> type: Opaque- Replace 'KUADRANT_DEVELOPER_NS' with the namespace for the object.
-
Replace
<sample_app>with the name of your application. -
Replace
<gold_key>,<silver_key>, and<bronze_key>with the actual API keys for each tier. -
Replace
<gold_api_key_value>,<silver_api_key_value>, and<bronze_api_key_value>with the actual API key strings you want to assign to each tier.
Apply the
Secretobjects by running the following command:$ oc apply -f <secrets.yaml>Replace
<secrets.yaml>with the name of your YAML.
6.1.4. Verify the presence of the API key secrets Copy linkLink copied to clipboard!
You can confirm the presence of the API key secrets by running a few commands.
Procedure
Confirm that all three API key secrets are present in the gateway namespace by running the following command:
$ oc get secrets -n ${KUADRANT_DEVELOPER_NS} -l app=<sample_app>-
Replace
${KUADRANT_DEVELOPER_NS}with the developer namespace for Connectivity Link. Replace
<sample_app>with the name of your application.Example output
bronze_key Opaque 1 2 mins gold_key Opaque 1 2 mins silver_key Opaque 1 2 mins
-
Replace
Send a request without an API key and confirm that it is rejected by running the following command:
$ curl http://<gateway_hostname>/toy -iReplace
<gateway_hostname>with the hostname for your API.Example output
HTTP/1.1 401 UnauthorizedSend a request with a valid API key and confirm that it succeeds by running the following command:
$ curl -H "Authorization: <gold_api_key_value>" http://<gateway_hostname>/toy -i-
Replace
<gold_api_key_value>with the actual API key string. Replace
<gateway_hostname>with the hostname for your API.Example output
HTTP/1.1 200 OK
-
Replace
6.1.5. Deploy plan-based rate limiting with a PlanPolicy Copy linkLink copied to clipboard!
After you create your AuthPolicy and Secret objects, you can create a PlanPolicy object to enforce tiered rate limits on specific authenticated users.
You can create a PlanPolicy object by using the Connectivity Link web console plugin.
Prerequisites
- You installed Connectivity Link
-
You installed OpenShift CLI (
oc). - You are logged in to your cluster.
-
You created the
HTTPRouteobject for your deployment. - You configured API key-based authentication.
A PlanPolicy can target an HTTPRoute, GRPCRoute, or Gateway custom resource (CR).
Procedure
Create a
PlanPolicyobject to enforce plan-based rate limits on theHTTPRouteobject by using the following example:Example
PlanPolicyobjectapiVersion: extensions.kuadrant.io/v1alpha1 kind: PlanPolicy metadata: name: <sample_app> namespace: ${KUADRANT_DEVELOPER_NS} spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: <sample_app> plans: - tier: gold predicate: | has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "gold" limits: daily: 5 - tier: silver predicate: | has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "silver" limits: daily: 2 - tier: bronze predicate: | has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "bronze" limits: daily: 1NoteThe
PlanPolicyobject uses thespec.plans.limitsparameter to enforce rate limits at the plan tier level instead of the individual subscriber level.-
Replace
<sample_app>with the name of your application. - Replace 'KUADRANT_DEVELOPER_NS' with the namespace for your object.
Each
spec.plans.predicateis a CEL expression. Thepredicatesare evaluated in order, and the first matching plan is applied to the authenticated user.NoteThe Common Expression Language (CEL) expressions can be anything that applies to your use case. You can use an expression different from the
secret.kuadrant.io/plan-idexpression.The
limitsfield supports the following windows:daily,weekly,monthly,yearly, andcustom. Custom limits take alimitvalue and awindowduration string, for example:limits: daily: 100 weekly: 500 monthly: 2000 custom: - limit: 10 window: "1m"
-
Replace
Apply the
PlanPolicyobject by running the following command:$ oc apply -f <planpolicy.yaml>Replace
<planpolicy.yaml>with the name of your YAML.
6.1.6. Verify the acceptance of the PlanPolicy CR Copy linkLink copied to clipboard!
You can confirm the acceptance of the PlanPolicy custom resource (CR) by running a command.
Procedure
Confirm that the
PlanPolicyCR is accepted by running the following command:$ oc get planpolicy <sample_app> -n ${KUADRANT_DEVELOPER_NS} -o jsonpath='{.status.conditions[?(@.type=="Accepted")].message}{"\n"}{.status.conditions[?(@.type=="Enforced")].message}'-
Replace
<sample_app>with the name of your application. Replace
${KUADRANT_DEVELOPER_NS}with the namespace for your object.Example output
PlanPolicy has been accepted PlanPolicy has been successfully enforced
-
Replace
6.1.7. Verify the PlanPolicy enforcement of ratelimiting Copy linkLink copied to clipboard!
You can verify that rate limiting is enforced by the PlanPolicy object by running several commands.
Procedure
Confirm that the
AuthConfigobject was updated with plan data from thePlanPolicyobject by running the following command:$ oc get authconfig -n <kuadrant_system> -o yaml-
Replace
<kuadrant_system>with the namespace where your Connectivity Link instance is deployed. -
The output is expected to include plan information in the response section of the
AuthConfigobject.
-
Replace
Send a request without an API key and confirm that it is rejected by running the following command:
$ curl <gateway_hostname>/toy -iReplace
<gateway_hostname>with the hostname for your API.Example output
HTTP/1.1 401 UnauthorizedSend a request using the
bronze-tierAPI key and confirm that it succeeds by running the following command:$ curl -H 'Authorization: <bronze_api_key_value>' <gateway_hostname>/toy -i-
Replace
<bronze_api_key_value>with the actual API key string. Replace
<gateway_hostname>with the hostname for your API.Example output
HTTP/1.1 200 OK
-
Replace
Send a second request using the
bronze-tierAPI key and confirm that it is rate limited by running the following command:$ curl -H 'Authorization: <bronze_api_key_value>' <gateway_hostname>/toy -i-
Replace
<bronze_api_key_value>with the actual API key string. Replace
<gateway_hostname>with the hostname for your API.Example output
HTTP/1.1 429 Too Many Requests
-
Replace
-
Confirm that the
gold-tierkey allows up to 5 requests per day before the limit is enforced.
6.2. PlanPolicy custom resource parameters Copy linkLink copied to clipboard!
Understand the configuration parameters available for the PlanPolicy custom resource so that you can use the object effectively.
| Parameter | Description |
|---|---|
|
|
A reference to an existing Gateway API resource. A |
|
|
An ordered list of plan definitions. Plans are evaluated in sequence and the first plan whose predicate evaluates to |
|
|
A unique string identifier for the plan tier, for example |
|
| Specifies a Common Expression Language (CEL) expression evaluated against the authenticated request context. |
|
| Specifies the rate limiting configuration for the tier. |
6.2.1. Target an HTTPRoute in a PlanPolicy Copy linkLink copied to clipboard!
When a PlanPolicy object targets an HTTPRoute custom resource (CR), you can use the following example to enforce the plan-based rate limits on all traffic flowing through that specific route.
Example enforcement of plan-based rate limits on HTTPRoute CR traffic
apiVersion: extensions.kuadrant.io/v1alpha1
kind: PlanPolicy
metadata:
name: <sample_app>
namespace: ${KUADRANT_DEVELOPER_NS}
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: <sample_app>
plans:
- tier: gold
predicate: |
has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "gold"
limits:
daily: 1000
weekly: 5000
- tier: silver
predicate: |
has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "silver"
limits:
daily: 500
weekly: 2000
- tier: bronze
predicate: |
has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "bronze"
limits:
daily: 100
weekly: 500
-
Replace
<sample_app>with the name of your application. Replace
${KUADRANT_DEVELOPER_NS}with the namespace for your object.NoteThe Common Expression Language (CEL) expressions can be anything that applies to your use case. You can use an expression different from the
secret.kuadrant.io/plan-idexpression.
6.2.2. Target a GRPCRoute in a PlanPolicy Copy linkLink copied to clipboard!
When a PlanPolicy object targets a GRPCRoute custom resource (CR), you can use the following example to enforce the plan-based rate limits on all traffic flowing through that specific route:
Example enforcement of plan-based rate limits on GRPCRoute CR traffic
apiVersion: extensions.kuadrant.io/v1alpha1
kind: PlanPolicy
metadata:
name: <sample_app>
namespace: ${KUADRANT_DEVELOPER_NS}
spec:
targetRef:
group: gateway.networking.k8s.io
kind: GRPCRoute
name: <sample_app>
plans:
- tier: gold
predicate: |
has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "gold"
limits:
daily: 1000
weekly: 5000
- tier: silver
predicate: |
has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "silver"
limits:
daily: 500
weekly: 2000
- tier: bronze
predicate: |
has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "bronze"
limits:
daily: 100
weekly: 500
-
Replace
<sample_app>with the name of your application. -
Replace
{KUADRANT_DEVELOPER_NS}with the namespace for your object.
The Common Expression Language (CEL) expressions can be anything that applies to your use case. You can use an expression different from the secret.kuadrant.io/plan-id expression.
6.2.3. Target a Gateway in a PlanPolicy Copy linkLink copied to clipboard!
When a PlanPolicy object targets a Gateway custom resource (CR), Connectivity Link enforces the plan-based rate limits on all routes attached to that Gateway CR, as shown in the following example.
Example enforcement of plan-based rate limits on a Gateway
apiVersion: extensions.kuadrant.io/v1alpha1
kind: PlanPolicy
metadata:
name: <sample_app>
namespace: ${KUADRANT_GATEWAY_NS}
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: _<sample_app>
plans:
# ...
-
Replace
<sample_app>with the name of the application. -
Replace
${KUADRANT_GATEWAY_NS}with the namespace for theGatewayobject
6.2.4. Plan predicates in PlanPolicies Copy linkLink copied to clipboard!
Plan predicates are CEL expressions that determine which plan tier applies to a request. Connectivity Link evaluates predicates in the order they appear in spec.plans and selects the first plan whose predicate returns true.
The following patterns are supported:
- Plan identified by an annotation on the API key Secret
predicate: | has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "gold"- Plan identified by a JWT claim
predicate: | has(auth.identity) && auth.identity.sub == "premium-user"- Plan identified by multiple conditions
predicate: | has(auth.identity) && auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "gold" && auth.identity.metadata.labels["app"] == "my-app"
The has(auth.identity) check at the start of each predicate confirms that the request is authenticated before evaluating plan-specific conditions.
6.2.5. Plan limits for PlanPolicies Copy linkLink copied to clipboard!
The limits field in each plan definition sets the maximum number of requests allowed within each time window. All fields are optional. Specify only the windows relevant to your use case.
| Field | Description |
|---|---|
|
| Maximum requests per calendar day. |
|
| Maximum requests per calendar week. |
|
| Maximum requests per calendar month. |
|
| Maximum requests per calendar year. |
|
|
One or more custom windows. Each entry requires a |
The following example shows a plan snippet using all available limit types:
#...
limits:
daily: 1000
weekly: 5000
monthly: 20000
yearly: 200000
custom:
- limit: 100
window: "1h"
#...