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. Such as 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
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 extension plugin is enabled, all flow labels including the ones created through classifier are available in traffic analytics.
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
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
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:
Extracting values from headers
classifiers:
- selectors:
- service: checkout.default.svc.cluster.local
control_point: ingress # control point
rules: # classification rules
user_type: # flow label key
extractor: # extractor
from: request.http.headers.user-type # HTTP headerParsing a field from JSON encoded request payload
from: request.http.body
pointer: /user/namefrom: request.http.bearer
json_pointer: /user/email
Aperture aims to expand the set of extractors to cover the most-common use cases.
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, such as if
or default
).
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
Rego reference
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
}