Skip to content

Well-known Attributes

Summary

Define a well-known structure for users to declare request data selectors in their RateLimitPolicies and AuthPolicies. This structure is referred to as the Kuadrant Well-known Attributes.

Motivation

The well-known attributes let users write policy rules – conditions and, in general, dynamic values that refer to attributes in the data plane - in a concise and seamless way.

Decoupled from the policy CRDs, the well-known attributes:

  1. define a common language for referring to values of the data plane in the Kuadrant policies;
  2. allow dynamically evolving the policy APIs regarding how they admit references to data plane attributes;
  3. encompass all common and component-specific selectors for data plane attributes;
  4. have a single and unified specification, although this specification may occasionally link to additional, component-specific, external docs.

Guide-level explanation

One who writes a Kuadrant policy and wants to build policy constructs such as conditions, qualifiers, variables, etc, based on dynamic values of the data plane, must refer the attributes that carry those values, using the declarative language of Kuadrant's Well-known Attributes.

A dynamic data plane value is typically a value of an attribute of the request or an Envoy Dynamic Metadata entry. It can be a value of the outer request being handled by the API gateway or proxy that is managed by Kuadrant ("context request") or an attribute of the direct request to the Kuadrant component that delivers the functionality in the data plane (rate-limiting or external auth).

A Well-known Selector is a construct of a policy API whose value contains a direct reference to a well-known attribute. The language of the well-known attributes and therefore what one would declare within a well-known selector resembles a JSON path for navigating a possibly complex JSON object.

Example 1. Well-known selector used in a condition

apiGroup: examples.kuadrant.io
kind: PaintPolicy
spec:
  rules:

  - when:
    - selector: auth.identity.group
      operator: eq
      value: admin
    color: red

In the example, auth.identity.group is a well-known selector of an attribute group, known to be injected by the external authorization service (auth) to describe the group the user (identity) belongs to. In the data plane, whenever this value is equal to admin, the abstract PaintPolicy policy states that the traffic must be painted red.

Example 2. Well-known selector used in a variable

apiGroup: examples.kuadrant.io
kind: PaintPolicy
spec:
  rules:

  - color: red
    alpha:
      dynamic: request.headers.x-color-alpha

In the example, request.headers.x-color-alpha is a selector of a well-known attribute request.headers that gives access to the headers of the context HTTP request. The selector retrieves the value of the x-color-alpha request header to dynamically fill the alpha property of the abstract PaintPolicy policy at each request.

Reference-level explanation

The Well-known Attributes are a compilation inspired by some of the Envoy attributes and Authorino's Authorization JSON and its related JSON paths.

From the Envoy attributes, only attributes that are available before establishing connection with the upstream server qualify as a Kuadrant well-known attribute. This excludes attributes such as the response attributes and the upstream attributes.

As for the attributes inherited from Authorino, these are either based on Envoy's AttributeContext type of the external auth request API or from internal types defined by Authorino to fulfill the Auth Pipeline.

These two subsets of attributes are unified into a single set of well-known attributes. For each attribute that exists in both subsets, the name of the attribute as specified in the Envoy attributes subset prevails. Example of such is request.id (to refer to the ID of the request) superseding context.request.http.id (as the same attribute is referred in an Authorino AuthConfig).


The next sections specify the well-known attributes organized in the following groups:

Request attributes

The following attributes are related to the context HTTP request that is handled by the API gateway or proxy managed by Kuadrant.

Attribute

Type

Description

Auth

RL

request.id

String

Request ID corresponding to x-request-id header value

request.time

Timestamp

Time of the first byte received

request.protocol

String

Request protocol (“HTTP/1.0”, “HTTP/1.1”, “HTTP/2”, or “HTTP/3”)

request.scheme

String

The scheme portion of the URL e.g. “http”

request.host

String

The host portion of the URL

request.method

String

Request method e.g. “GET”

request.path

String

The path portion of the URL

request.url_path

String

The path portion of the URL without the query string

request.query

String

The query portion of the URL in the format of “name1=value1&name2=value2”

request.headers

Map<String, String>

All request headers indexed by the lower-cased header name

request.referer

String

Referer request header

request.useragent

String

User agent request header

request.size

Number

The HTTP request size in bytes. If unknown, it must be -1

request.body

String

The HTTP request body. (Disabled by default. Requires additional proxy configuration to enabled it.)

request.raw_body

Array<Number>

The HTTP request body in bytes. This is sometimes used instead of body depending on the proxy configuration.

request.context_extensions

Map<String, String>

This is analogous to request.headers, however these contents are not sent to the upstream server. It provides an extension mechanism for sending additional information to the auth service without modifying the proto definition. It maps to the internal opaque context in the proxy filter chain. (Requires additional configuration in the proxy.)

Connection attributes

The following attributes are available once the downstream connection with the API gateway or proxy managed by Kuadrant is established. They apply to HTTP requests (L7) as well, but also to proxied connections limited at L3/L4.

Attribute

Type

Description

Auth

RL

source.address

String

Downstream connection remote address

source.port

Number

Downstream connection remote port

source.service

String

The canonical service name of the peer

source.labels

Map<String, String>

The labels associated with the peer. These could be pod labels for Kubernetes or tags for VMs. The source of the labels could be an X.509 certificate or other configuration.

source.principal

String

The authenticated identity of this peer. If an X.509 certificate is used to assert the identity in the proxy, this field is sourced from “URI Subject Alternative Names“, “DNS Subject Alternate Names“ or “Subject“ in that order. The format is issuer specific – e.g. SPIFFE format is spiffe://trust-domain/path, Google account format is https://accounts.google.com/{userid}.

source.certificate

String

The X.509 certificate used to authenticate the identify of this peer. When present, the certificate contents are encoded in URL and PEM format.

destination.address

String

Downstream connection local address

destination.port

Number

Downstream connection local port

destination.service

String

The canonical service name of the peer

destination.labels

Map<String, String>

The labels associated with the peer. These could be pod labels for Kubernetes or tags for VMs. The source of the labels could be an X.509 certificate or other configuration.

destination.principal

String

The authenticated identity of this peer. If an X.509 certificate is used to assert the identity in the proxy, this field is sourced from “URI Subject Alternative Names“, “DNS Subject Alternate Names“ or “Subject“ in that order. The format is issuer specific – e.g. SPIFFE format is spiffe://trust-domain/path, Google account format is https://accounts.google.com/{userid}.

destination.certificate

String

The X.509 certificate used to authenticate the identify of this peer. When present, the certificate contents are encoded in URL and PEM format.

connection.id

Number

Downstream connection ID

connection.mtls

Boolean

Indicates whether TLS is applied to the downstream connection and the peer ceritificate is presented

connection.requested_server_name

String

Requested server name in the downstream TLS connection

connection.tls_session.sni

String

SNI used for TLS session

connection.tls_version

String

TLS version of the downstream TLS connection

connection.subject_local_certificate

String

The subject field of the local certificate in the downstream TLS connection

connection.subject_peer_certificate

String

The subject field of the peer certificate in the downstream TLS connection

connection.dns_san_local_certificate

String

The first DNS entry in the SAN field of the local certificate in the downstream TLS connection

connection.dns_san_peer_certificate

String

The first DNS entry in the SAN field of the peer certificate in the downstream TLS connection

connection.uri_san_local_certificate

String

The first URI entry in the SAN field of the local certificate in the downstream TLS connection

connection.uri_san_peer_certificate

String

The first URI entry in the SAN field of the peer certificate in the downstream TLS connection

connection.sha256_peer_certificate_digest

String

SHA256 digest of the peer certificate in the downstream TLS connection if present

Metadata and filter state attributes

The following attributes are related to the Envoy proxy filter chain. They include metadata exported by the proxy throughout the filters and information about the states of the filters themselves.

Attribute

Type

Description

Auth

RL

metadata

Metadata

Dynamic request metadata

filter_state

Map<String, String>

Mapping from a filter state name to its serialized string value

Auth attributes

The following attributes are exclusive of the external auth service (Authorino).

Attribute

Type

Description

Auth

RL

auth.identity

Any

Single resolved identity object, post-identity verification

auth.metadata

Map<String, Any>

External metadata fetched

auth.authorization

Map<String, Any>

Authorization results resolved by each authorization rule, access granted only

auth.response

Map<String, Any>

Response objects exported by the auth service post-access granted

auth.callbacks

Map<String, Any>

Response objects returned by the callback requests issued by the auth service

The auth service also supports modifying selected values by chaining modifiers in the path.

Rate-limit attributes

The following attributes are exclusive of the rate-limiting service (Limitador).

Attribute

Type

Description

Auth

RL

ratelimit.domain

String

The rate limit domain. This enables the configuration to be namespaced per application (multi-tenancy).

ratelimit.hits_addend

Number

Specifies the number of hits a request adds to the matched limit. Fixed value: `1`. Reserved for future usage.

Drawbacks

The decoupling of the well-known attributes and the language of well-known attributes and selectors from the individual policy CRDs is what makes it somewhat flexible and common across the components (rate-limiting and auth). However, it's less structured and it introduces another syntax for users to get familiar with.

This additional language competes with the language of the route selectors (RFC 0001), based on Gateway API's HTTPRouteMatch type.

Being "soft-coded" in the policy specs (as opposed to a hard-coded sub-structure inside of each policy type) does not mean it's completely decoupled from implementation in the control plane and/or intermediary data plane components. Although many attributes can be supported almost as a pass-through, from being used in a selector in a policy, to a corresponding value requested by the wasm-shim to its host, that is not always the case. Some translation may be required for components not integrated via wasm-shim (e.g. Authorino), as well as for components integrated via wasm-shim (e.g. Limitador) in special cases of composite or abstraction well-known attributes (i.e. attributes not available as-is via ABI, e.g. auth.identity in a RLP). Either way, some validation of the values introduced by users in the selectors may be needed at some point in the control plane, thus requiring arguably a level of awaresness and coupling between the well-known selectors specification and the control plane (policy controllers) or intermediary data plane (wasm-shim) components.

Rationale and alternatives

As an alternative to JSON path-like selectors based on a well-known structure that induces the proposed language of well-known attributes, these same attributes could be defined as sub-types of each policy CRD. The Golang packages defining the common attributes across CRDs could be shared by the policy type definitions to reduce repetition. However, that approach would possibly involve a staggering number of new type definitions to cover all the cases for all the groups of attributes to be supported. These are constructs that not only need to be understood by the policy controllers, but also known by the user who writes a policy.

Additionally, all attributes, including new attributes occasionally introduced by Envoy and made available to the wasm-shim via ABI, would always require translation from the user-level abstraction how it's represented in a policy, to the actual form how it's used in the wasm-shim configuration and Authorino AuthConfigs.

Not implementing this proposal and keeping the current state of things mean little consistency between these common constructs for rules and conditions on how they are represented in each type of policy. This lack of consistency has a direct impact on the overhead faced by users to learn how to interact with Kuadrant and write different kinds of policies, as well as for the maintainers on tasks of coding for policy validation and reconciliation of data plane configurations.

Prior art

Authorino's dynamic JSON paths, related to Authorino's Authorization JSON and used in when conditions and inside of multiple other constructs of the AuthConfig, are an example of feature of very similar approach to the one proposed here.

Arguably, Authorino's perceived flexibility would not have been possible with the Authorization JSON selectors. Users can write quite sophisticated policy rules (conditions, variable references, etc) by leveraging the those dynamic selectors. Because they are backed by JSON-based machinery in the code, Authorino's selectors have very little to, in some cases, none at all variation compared Open Policy Agent's Rego policy language, which is often used side by side in the same AuthConfigs.

Authorino's Authorization JSON selectors are, in one hand, more restrict to the structure of the CheckRequest payload (context.* attributes). At the same time, they are very open in the part associated with the internal attributes built along the Auth Pipeline (i.e. auth.* attributes). That makes Authorino's Authorization JSON selectors more limited, compared to the Envoy attributes made available to the wasm-shim via ABI, but also harder to validate. In some cases, such as of deep references to inside objects fetched from external sources of metadata, resolved OPA objects, JWT claims, etc, it is impossible to validate for correct references.

Another experience learned from Authorino's Authorization JSON selectors is that they depend substantially on the so-called "modifiers". Many use cases involving parsing and breaking down attributes that are originally available in a more complex form would not be possible without the modifiers. Examples of such cases are: extracting portions of the path and/or query string parameters (e.g. collection and resource identifiers), applying translations on HTTP verbs into corresponding operations, base64-decoding values from the context HTTP request, amongst several others.

Unresolved questions

  1. How to deal with the differences regarding the availability and data types of the attributes across clients/hosts?

  2. Can we make more attributes that are currently available to only one of the components common to both?

  3. Will we need some kind of global support for modifiers (functions) in the well-known selectors or those can continue to be an Authorino-only feature?

  4. Does Authorino, which is more strict regarding the data structure that induces the selectors, need to implement this specification or could/should it keep its current selectors and a translation be performed by the AuthPolicy controller?

Future possibilities

  1. Extend with more well-known attributes that abstract common patterns and/or for rather opinioned use cases. Examples:
  2. auth.* attributes supported in the rate limit service
  3. request.authenticated
  4. request.operation.(read|write)
  5. request.param.my-param
  6. connection.secure

  7. Other Envoy attributes

Wasm attributes

Attribute

Type

Description

Auth

RL

wasm.plugin_name

String

Plugin name

wasm.plugin_root_id

String

Plugin root ID

wasm.plugin_vm_id

String

Plugin VM ID

wasm.node

Node

Local node description

wasm.cluster_name

String

Upstream cluster name

wasm.cluster_metadata

Metadata

Upstream cluster metadata

wasm.listener_direction

Number

Enumeration value of the listener traffic direction

wasm.listener_metadata

Metadata

Listener metadata

wasm.route_name

String

Route name

wasm.route_metadata

Metadata

Route metadata

wasm.upstream_host_metadata

Metadata

Upstream host metadata

Proxy configuration attributes

Attribute

Type

Description

Auth

RL

xds.cluster_name

String

Upstream cluster name

xds.cluster_metadata

Metadata

Upstream cluster metadata

xds.route_name

String

Route name

xds.route_metadata

Metadata

Route metadata

xds.upstream_host_metadata

Metadata

Upstream host metadata

xds.filter_chain_name

String

Listener filter chain name

  1. Add some support for value modifiers (functions), along the lines of Authorino's JSON path modifiers and/or Envoy attributes' path expressions.