Skip to main content
Version: development

Classifier

A Classifier can be used to create additional flow labels based on request attributes, if the existing flow labels aren't sufficient. Classifiers are defined using rules based on the Rego query language.

Classifiers are available only at HTTP control points. For feature-based control points, developers can provide arbitrary flow labels by setting baggage or directly as arguments to the startFlow() call.

Defining a Classifier

To define a Classifier, it needs to be added as a resource in a policy. Like any other flow control component, a Classifier is applied only to the flows matching the specified selectors

A Classifier defines a set of rules to create new flow labels based on request attributes. Envoy's External Authorization definition is used by Aperture to describe the request attributes, specifically the AttributeContext.

An example of how the request attributes might look can be seen in the INPUT section at this Rego playground.

Example of a Classifier that creates a flow label user-type based on the value of the user-type HTTP header:

policy:
policy_name: checkout-service-policy
resources:
flow_control:
classifiers: # resource name
- selectors: # Selector Defining Classifier's Scope
- service: checkout.default.svc.cluster.local
control_point: ingress
rules: # classification rules
user_type: # flow label key
extractor: # Declarative Extractor
from: request.http.headers.user-type # HTTP header
caution

Although Classifier is defined as a resource in a policy, flow labels aren't isolated in any way and are shared across policies.

Any Flow Labels created through the Classifier become available in subsequent stages of flow processing. Additionally, the Flow Label is injected as baggage, so it will be available as a flow label in downstream flows too (assuming you have baggage propagation configured in your system). If FluxNinja ARC extension plugin is enabled, all flow labels including the ones created through classifier are available in traffic analytics.

note

The extracted label's baggage propagation and inclusion in traffic analytics can be disabled in the classifier configuration.

Live Previewing Request Attributes

Live previewing of request attributes is a feature that allows real-time examination of attributes flowing through services and control points. This can be done using the aperturectl tool, aiding in setting up or debugging a Classifier. It provides insights into the data a Classifier will handle, enabling the creation of more effective classification rules.

For example:

aperturectl flow-control preview --kube checkout.default.svc.cluster.local ingress --http

Returns:

{
"samples": [
{
"attributes": {
"destination": {
"address": {
"socketAddress": {
"address": "10.244.1.20",
"portValue": 8099
}
}
},
"metadataContext": {},
"request": {
"http": {
"headers": {
":authority": "checkout.default.svc.cluster.local",
":method": "POST",
":path": "/request",
":scheme": "http",
"content-length": "201",
"content-type": "application/json",
"cookie": "session=eyJ1c2VyIjoia2Vub2JpIn0.YbsY4Q.kTaKRTyOIfVlIbNB48d9YH6Q0wo",
"user-agent": "k6/0.42.0 (https://k6.io/)",
"user-id": "19",
"user-type": "guest",
"x-forwarded-proto": "http",
"x-request-id": "26f01736-ec45-4b07-a202-bdec8930c7f8"
},
"host": "checkout.default.svc.cluster.local",
"id": "14553976531353216255",
"method": "POST",
"path": "/request",
"protocol": "HTTP/1.1",
"scheme": "http"
},
"time": "2023-01-15T07:07:48.693035Z"
},
"source": {
"address": {
"socketAddress": {
"address": "10.244.2.36",
"portValue": 35388
}
}
}
},
"parsed_body": null,
"parsed_path": ["request"],
"parsed_query": {},
"truncated_body": false,
"version": {
"encoding": "protojson",
"ext_authz": "v3"
}
}
]
}

Alternatively, use the Introspection API directly on an Aperture Agent:

Example:

curl -X POST localhost:8080/v1/flowcontrol/preview/http_requests/checkout.default.svc.cluster.local/ingress?samples=1

Classification Rules

See Also

Rules (reference)

Each classification rule consists of two main components:

  • Flow Label Key: This is the identifier for the flow label. It is used to reference the flow label in other parts of the system.
  • Extraction Rule: A rule how to extract the flow label value based on request attributes.

There are two ways to specify a classification rule:

  • Declarative extractors
  • Rego modules
Request body availability

The possibility of extracting values from the request body depends on how External Authorization in Envoy was configured. The default Istio Configuration does not enable request body buffering, as it might break some streaming APIs.

Declarative Extractors

Extractors provide a high-level way to specify how to extract a flow label value given HTTP request attributes, eliminating the need to write Rego code. Provided extractors include:

Aperture aims to expand the set of extractors to cover the most-common use cases.

Naming Conventions for Flow Label Keys

Keys of flow labels created by extractors must be valid Rego identifiers (alphanumeric characters and underscore are allowed; also, label name cannot be a Rego keyword, like if or default).

Benefits of Explicit Flow Label Extraction from Headers

Extracting the value from the header might not seem useful, as the value is already available as Flow Label (as http.request.header.<header>), but adding flow label explicitly might still be useful, as it enables baggage propagation and telemetry for this flow label.

Advanced Classification with Rego Language

See Also

For more complex scenarios, the Rego language can be used to define the extractor. Rego allows you to define a set of labels that are extracted after evaluating a Rego module.

Example of Rego module which also disables telemetry visibility of label:


rego:
labels:
user:
telemetry: false
module: |
package user_from_cookie
cookies := split(input.attributes.request.http.headers.cookie, "; ")
user := user {
cookie := cookies[_]
startswith(cookie, "session=")
session := substring(cookie, count("session="), -1)
parts := split(session, ".")
object := json.unmarshal(base64url.decode(parts[0]))
user := object.user
}