-
Notifications
You must be signed in to change notification settings - Fork 55
CFP: Cilium policy selector expression #94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
fristonio
wants to merge
1
commit into
main
Choose a base branch
from
pr/fristonio/policy-selector-expressions
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+152
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| # CFP-XXXXX: Policy Selector Expressions | ||
|
|
||
| **SIG: SIG-Policy** | ||
|
|
||
| **Begin Design Discussion:** 2026-05-04 | ||
|
|
||
| **Cilium Release:** TBD | ||
|
|
||
| **Authors:** Deepesh Pathak | ||
|
|
||
| **Status:** Draft | ||
|
|
||
| ## Summary | ||
|
|
||
| This CFP proposes extending [Cilium's policy selectors](https://github.com/cilium/cilium/blob/f7c0ac66d9d781ecb5bd72c59025620b01c912d8/pkg/policy/api/selector.go#L17), allowing users to write complex selection expressions than what is supported today with [`LabelSelector`](https://github.com/cilium/cilium/blob/f7c0ac66d9d781ecb5bd72c59025620b01c912d8/pkg/k8s/slim/k8s/apis/meta/v1/types.go#L267) - specifically operators such as prefix, suffix, and substring matching, along with full boolean composition. | ||
|
|
||
| ## Motivation | ||
|
|
||
| Currently cilium's policy selector type([`EndpointSelector`](https://github.com/cilium/cilium/blob/f7c0ac66d9d781ecb5bd72c59025620b01c912d8/pkg/policy/api/selector.go#L17)) wraps the Kubernetes [`LabelSelector`](https://github.com/cilium/cilium/blob/f7c0ac66d9d781ecb5bd72c59025620b01c912d8/pkg/k8s/slim/k8s/apis/meta/v1/types.go#L267) type, which supports only equality-based matching (`matchLabels`) and a small fixed set of set-membership operators (`In`, `NotIn`, `Exists`, `DoesNotExist`). For most workloads this is sufficient, but there are common cases where users need more expressive matching. | ||
|
|
||
| While some of these use-cases can be hacked with the current selector semantics(eg. splitting selection constraints across multiple selectors, enumerating every concrete value in `matchExpression` entry etc.), they become very verbose, brittle and hard to maintain. Moreover, defining the selection constraints in this way sometimes comes with runtime cost in cilium-agent(eg. high selector cardinality), that might not be apparent to the user. | ||
|
|
||
| ### User stories | ||
|
|
||
| - [Support multiple endpoint selectors in CNP rules](https://github.com/cilium/cilium/issues/45682) | ||
|
|
||
| ## Goals | ||
|
|
||
| - Allow users to write richer label matching conditions in policy selectors that go beyond equality and set membership. | ||
| - Allow users to compose multiple boolean expressions in a selector. | ||
| - Preserve full backward compatibility: existing policies that use only `matchLabels` / `matchExpressions` should continue to work without change. | ||
|
|
||
| ## Non-Goals | ||
|
|
||
| - Migrate/Replace existing policy label selector semantics. | ||
|
|
||
| ## Proposal | ||
|
|
||
| ### Overview | ||
|
|
||
| The proposal is to add a new optional field in policy selector type [`EndpointSelector`](https://github.com/cilium/cilium/blob/f7c0ac66d9d781ecb5bd72c59025620b01c912d8/pkg/policy/api/selector.go#L17) that allows users to write a serialized structured match expression. The expression is validated and compiled during policy import and used as a requirement in policy [`LabelSelector`](https://github.com/cilium/cilium/blob/f7c0ac66d9d781ecb5bd72c59025620b01c912d8/pkg/k8s/slim/k8s/apis/meta/v1/types.go#L267) type for matching labels. | ||
|
|
||
| ```go | ||
| // EndpointSelector is a wrapper for k8s LabelSelector. | ||
| type EndpointSelector struct { | ||
| *slim_metav1.LabelSelector `json:",inline"` | ||
|
|
||
| // MatchCELExpression is an optional, serialized, boolean CEL expression | ||
| // that provides additional label match conditions for this selector. | ||
| // When set, an endpoint must satisfy this expression **AND** the | ||
| // k8s LabelSelector constraints. | ||
| // | ||
| // +kubebuilder:validation:Optional | ||
| // +kubebuilder:validation:MaxLength=4096 | ||
| MatchCELExpression *string `json:"matchCELExpression,omitzero"` | ||
| } | ||
| ``` | ||
|
|
||
| ## Impacts / Key Questions | ||
|
|
||
| ### Expression Language | ||
|
|
||
| The central design aspect for the implementation is to decide the expression language to use. | ||
|
|
||
| #### Option 1: Custom expression language | ||
|
|
||
| The idea with this option is to define a custom selection expression language. As a reference, this is how [Calico selector expressions](https://docs.tigera.io/calico-enterprise/latest/reference/resources/networkpolicy#selector) are implemented. This will involve designing the SPEC for the language and writing the compiler. | ||
|
|
||
| ##### Pros | ||
|
|
||
| - Match evaluation can be made zero-allocation with a very low overhead(see POC benchmarks). | ||
| - The operator set and language spec will be narrow, implementing exactly the requirements for label selectors which limits the surface area for misuse and bugs. | ||
| - No new external dependencies. | ||
|
|
||
| ##### Cons | ||
|
|
||
| - The parser implementation needs to be maintained in cilium source tree. | ||
| - Language spec is not standard and users must learn a new format. | ||
|
|
||
| #### Option 2: CEL Expression | ||
|
|
||
| A more natural option for the use-case is to use CEL(Common Expression Language) based expression for label selectors. Cilium already imports the `cel-go` library and has experimental support for CEL based expression language in hubble filters. This option will give us the opportunity to build a strong foundation for CEL usage in Cilium project, allowing easier adoption for similar usecases in future. | ||
|
|
||
| ##### Pros | ||
|
|
||
| - CEL is a CNCF standard, heavily utilized in Kubernetes ecosystem (admission validation, DRA device selectors etc.). | ||
| - Users already using CEL in other contexts will find the syntax familiar. | ||
| - CEL's type system, inbuilt macro's, operators and extension model allow for a rich selector expression language. | ||
| - CEL has a well-defined cost model and supports configurable evaluation cost limits, which can be used to bound the runtime cost of an expression and prevent accidental or malicious policy expressions. | ||
|
|
||
| ##### Cons | ||
|
|
||
| - Parse and match cost is significantly higher, making policy computation/identity updates more expensive. | ||
|
|
||
| ##### Implementation (CEL Expression) | ||
|
|
||
| The draft implementation of CEL based selection expression in policy selectors is present here: https://github.com/cilium/cilium/pull/46199 | ||
|
|
||
| The environment for LabelSelector CEL expression provides a `label(<source>:<key>)` macro | ||
| to lookup value of a key(with/without source prefix) against the Labels being evaluated for selection. | ||
| The return type of this macro is the label value(type: `string`) wrapped in [CEL's optional type](https://pkg.go.dev/github.com/google/cel-go/cel#OptionalTypes) | ||
|
|
||
| > Currently the [internal representation of identity labels](https://github.com/fristonio/cilium/blob/4b557bd47740671e51bd5aa09cf6677eae4e6b94/pkg/policy/selectorcache.go#L31) is implemented as label array instead of a map. | ||
| > Exposing a high level macro for selection expression provides us the opportunity to simplify internal types | ||
| > without any user impact in the future(out of scope for this proposal). | ||
|
|
||
| Example Usage: | ||
|
|
||
| | Description | Sample Expression | | ||
| | ------------------------------------------ | ------------------------------------------------------------------------------------- | | ||
| | Check if the key exists in identity labels | `label("k8s:app").hasValue()` | | ||
| | | `label("any:missing") != optional.none()` | | ||
| | Value of a key from identity labels | `label("k8s:app").value()` | | ||
| | | `label("k8s:app").orValue("test")` | | ||
| | | `label("k8s:app") == optional.of("myapp")` | | ||
| | Set membership/comparision operations | `label("k8s:env").value() in ["prod", "staging", "dev"]` | | ||
| | | `[label("k8s:env").value(), label("app").value()] == ["prod", "myapp"]` | | ||
| | Label value string operations | `label("k8s:env").orValue("").startsWith("prod")` | | ||
| | | `label("k8s:env").orValue("").endsWith("-us-east")` | | ||
| | | `label("k8s:env").orValue("").contains("db-staging")` | | ||
| | Boolean composition | `label("k8s:env").value() == "dev" \|\| label("k8s:env").value() == "staging"` | | ||
| | | `label("k8s:env") == optional.of("prod") && label("k8s:app") == optional.of("myapp")` | | ||
| | | `label("k8s:env") != optional.of("prod")` | | ||
| | Iterators | `["k8s:app", "k8s:env"].all(k, label(k).hasValue())` | | ||
| | | `["k8s:app", "k8s:env"].map(k, label(k).orValue("")) == ["myapp", "prod"]` | | ||
|
|
||
| ### Performance Implication (POC) | ||
|
|
||
| A rough POC implementation for both options is [present here](https://github.com/fristonio/cilium/commits/pr/fristonio/poc/policy-selector-expressions/) | ||
|
|
||
| #### Parse/Compile | ||
|
|
||
| | | ns/op | B/op | allocs/op | | ||
| | ----------------- | ------- | ------- | --------- | | ||
| | Custom Expression | 3,585 | 7,704 | 72 | | ||
| | CEL Expression | 182,470 | 184,860 | 2,716 | | ||
|
|
||
| #### Match | ||
|
|
||
| | | ns/op | B/op | allocs/op | | ||
| | ----------------- | ----- | ---- | --------- | | ||
| | Custom Expression | 17.6 | 0 | 0 | | ||
| | CEL Expression | 320 | 512 | 7 | | ||
|
|
||
| As mentioned before, CEL based selector expression have a very high cost as compared to a custom implementation. However, this is a reasonable trade-off, given for most usecases and simple expressions user can continue to use `LabelSelector` type. | ||
|
|
||
| ### References | ||
|
|
||
| - [Calico policy selector parser](https://github.com/projectcalico/calico/tree/master/libcalico-go/lib/selector) | ||
| - [CEL Golang library extensions](https://github.com/google/cel-go/tree/master/ext) | ||
| - [Kubernetes CEL API](https://kubernetes.io/docs/reference/using-api/cel/) | ||
| - [Kubernetes DRA CEL environment](https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/dynamic-resource-allocation/cel) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably include some examples here, along with the expected CEL variables and functions we intend to expose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of keeping the implementation details separate from the high level CFP. I am working on the implementation of CEL env here. Do you think it makes sense to keep this detailed documentation there(working on a package level README) or should I include everything here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say that the CEL environment is probably worth briefly discussing, since it's essentially unchangeable once landed. We don't need to consider every possible function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some examples with the link to draft PR. For now the plan is to expose just a simple macro that allows label value lookup based on key in the same format as supported in
k8s.LabelSelector(<source>:<key>).