PlanPolicy for tiered service offerings

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.

Note

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:

  1. An AuthPolicy CR authenticates a user request with stored identity metadata, including their plan information.

  2. The PlanPolicy object evaluates CEL predicate expressions against the authenticated identity to determine the user’s plan tier.

  3. Connectivity Link then enforces the rate limits defined for the user-matched plan tier.

The PlanPolicy object has the following known limitations:

  • A PlanPolicy object can only target HTTPRoute, GRPCRoute, and Gateway custom resources defined in the same namespace as the PlanPolicy object.

  • 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.

Create an AuthPolicy with API key authentication

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 HTTPRoute object for your deployment.

Note

A PlanPolicy can target an HTTPRoute, GRPCRoute, or Gateway custom resource (CR).

Procedure
  1. Create an AuthPolicy CR that targets the HTTPRoute object and enables API key authentication by using the following example:

    Example API key AuthPolicy CR
    apiVersion: 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_NS with the namespace for the object.

    • The spec.rules.authentication."api-key-plan".apiKey.selector.matchLabels field value points to the API key Secrets objects that carry the app: <sample_app> label.

    • Setting spec.rules.rules.authentication."api-key-plan".apiKey.selector.allNamespaces: true allows secrets from any namespace to be matched.

  2. Apply the policy by running the following command:

    $ oc apply -f <authpolicy.yaml>

    Replace <authpolicy.yaml> with the name of your YAML.

Verify the acceptance of the AuthPolicy CR

You can confirm the acceptance of the AuthPolicy custom resource (CR) by running a command.

Procedure
  1. Confirm that the AuthPolicy CR 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

Create API key Secret objects

You can create API key 'Secret` objects for each plan tier.

Important

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
  1. Create API key Secret objects for each plan tier that you want to use. Each Secret must carry the authorino.kuadrant.io/managed-by: authorino label and a secret.kuadrant.io/plan-id annotation that identifies the plan tier as shown in the following example:

    Example Secret object for plan tiers
    apiVersion: 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.

  2. Apply the Secret objects by running the following command:

    $ oc apply -f <secrets.yaml>

    Replace <secrets.yaml> with the name of your YAML.

Verify the presence of the API key secrets

You can confirm the presence of the API key secrets by running a few commands.

Procedure
  1. 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
  2. Send a request without an API key and confirm that it is rejected by running the following command:

    $ curl http://<gateway_hostname>/toy -i

    Replace <gateway_hostname> with the hostname for your API.

    Example output
    HTTP/1.1 401 Unauthorized
  3. Send 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

Deploy plan-based rate limiting with a PlanPolicy

After you create your AuthPolicy and Secret objects, you can create a PlanPolicy object to enforce tiered rate limits on specific authenticated users.

Note

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 HTTPRoute object for your deployment.

  • You configured API key-based authentication.

Note

A PlanPolicy can target an HTTPRoute, GRPCRoute, or Gateway custom resource (CR).

Procedure
  1. Create a PlanPolicy object to enforce plan-based rate limits on the HTTPRoute object by using the following example:

    Example PlanPolicy object
    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: 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: 1
    Note

    The PlanPolicy object uses the spec.plans.limits parameter 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.predicate is a CEL expression. The predicates are evaluated in order, and the first matching plan is applied to the authenticated user.

      Note

      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.

    • The limits field supports the following windows: daily, weekly, monthly, yearly, and custom. Custom limits take a limit value and a window duration string, for example:

      limits:
        daily: 100
        weekly: 500
        monthly: 2000
        custom:
          - limit: 10
            window: "1m"
  2. Apply the PlanPolicy object by running the following command:

    $ oc apply -f  <planpolicy.yaml>

    Replace <planpolicy.yaml> with the name of your YAML.

Verify the acceptance of the PlanPolicy CR

You can confirm the acceptance of the PlanPolicy custom resource (CR) by running a command.

Procedure
  1. Confirm that the PlanPolicy CR 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

Verify the PlanPolicy enforcement of ratelimiting

You can verify that rate limiting is enforced by the PlanPolicy object by running several commands.

Procedure
  1. Confirm that the AuthConfig object was updated with plan data from the PlanPolicy object 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 AuthConfig object.

  2. Send a request without an API key and confirm that it is rejected by running the following command:

    $ curl <gateway_hostname>/toy -i

    Replace <gateway_hostname> with the hostname for your API.

    Example output
    HTTP/1.1 401 Unauthorized
  3. Send a request using the bronze-tier API 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
  4. Send a second request using the bronze-tier API 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
  5. Confirm that the gold-tier key allows up to 5 requests per day before the limit is enforced.