Skip to content

Commit 2aada9b

Browse files
committed
add api details gep-3779
1 parent de6d664 commit 2aada9b

File tree

1 file changed

+233
-1
lines changed

1 file changed

+233
-1
lines changed

geps/gep-3779/index.md

Lines changed: 233 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,242 @@ Cilium has the concept of CiliumIdentity. Pods are assigned identities derived f
246246
More on https://docs.cilium.io/en/stable/internals/security-identities/ & https://docs.cilium.io/en/stable/security/network/identity/
247247

248248

249-
250249
## API
251250

251+
This GEP introduces a new policy resource, `AuthorizationPolicy`, for **identity-based** authorization. The policy defines a target, a single action (`ALLOW` or `DENY`), and a set of rules that include sources (the “who”) and an optional port attribute.
252+
253+
### **Policy Rules**
254+
255+
Each `AuthorizationPolicy` resource contains a list of rules. A request matches the policy if it matches **any** rule in the list (logical OR). Each rule defines multiple matching criteria; a request matches a rule only if it matches **all** criteria within that rule (logical AND).
256+
257+
A rule may specify:
258+
259+
* **Sources:** The source identities to which the rule applies. A request’s identity must match one of the listed sources. Supported source kinds are:
260+
* **SPIFFE ID**
261+
* **Kubernetes ServiceAccount**
262+
* **Kubernetes Namespace**
263+
* **Attributes:** Conditions on the target workload, at the time of writing this, only port is supported. If no attributes are specified, the rule applies to all traffic toward the target.
264+
265+
### **ALLOW Policies**
266+
267+
* An **ALLOW** policy is permissive.
268+
* A request is allowed if:
269+
* It matches at least one rule in any ALLOW policy targeting the workload **and**
270+
* It is not explicitly denied by any DENY policy.
271+
* If no ALLOW policy exists for a workload, traffic is permitted by default, unless any DENY policy applies.
272+
273+
### **DENY Policies**
274+
275+
* A **DENY** policy is restrictive and takes precedence over ALLOW.
276+
* If a request matches any rule in a DENY policy, it is immediately rejected, regardless of matching ALLOW rules elsewhere.
277+
* DENY policies enable to define global blocks or exceptions (for example: “block all traffic from Namespace X”).
278+
279+
### **ALLOW vs. DENY Semantics**
280+
281+
* **DENY always wins.** If both an ALLOW and a DENY policy match a request, the DENY policy blocks it.
282+
* The presence of any authorization policy causes the system to default to **deny-by-default** for matching workloads.
283+
* Another bullet to re-clarify the one above - the default behavior when no policies select a target workload is to allow all traffic. However, **as soon as at least one `AuthorizationPolicy` targets a workload, the model becomes implicitly deny-if-not-allowed**.
284+
285+
### **Target of Authorization**
286+
287+
The `targetRef` of the policy specifies the workload(s) to which the policy applies. Two options are available for `targetRef`:
288+
289+
#### **Option 1: Targeting a Service**
290+
291+
The `targetRef` can point to a Kubernetes `Service`.
292+
293+
**Benefits:**
294+
295+
* **No API Extension Required:** Works with the current PolicyAttachment model in Gateway API without modification.
296+
* **Simplicity:** Intuitive for users familiar with Kubernetes networking concepts.
297+
298+
**Downsides and Open Questions:**
299+
300+
However, targeting a `Service` introduces several challenges:
301+
302+
1. Authorization cannot be enforced on workloads not exposed via a `Service` - excluding use cases of pods/jobs without a Service.
303+
2. If a Pod belongs to multiple Services targeted by different authorization policies, precedence rules, may become unclear, leading to unpredictable or insecure outcomes. Even if such rules are explicitly defined, UX could potentially be confusing for users.
304+
3. UX and implementation challenges - are implementations expected to enforce the policy only if the traffic arrived through the specific Service? Or just to take the service selectors and enforce the policy regardless of how the traffic got to the destination?
305+
306+
#### **Option 2: Targeting Pods via Label Selectors**
307+
308+
Alternatively, the `targetRef` can specify a set of pods using a `LabelSelector` for a more flexible and direct approach.
309+
310+
**Benefits:**
252311

312+
* Aligns with established practices. Mesh implementations (Istio, Linkerd, Cilium) already use label selectors as the primary mechanism for targeting workloads in their native authorization policies, creating a consistent user experience.
313+
* Directly applies policy to pods, avoiding ambiguity present when targeting services. Ensures policies are enforced exactly where intended, regardless of how many services a pod might belong to.
314+
* Policies can apply to any workload, including pods not exposed via a `Service`, providing a comprehensive authorization solution.
315+
316+
**Downsides and Open Questions:**
317+
318+
The main downside of `LabelSelector` is the huge increase to the complexity of policy discoverability. See below for more info.
319+
320+
**Requirement: Enhancing Policy Attachment:**
321+
322+
This option depends on enhancements to Gateway API’s policy attachment model to support `LabelSelector` as a valid `targetRef`. This capability was discussed and received consensus at KubeCon North America 2024 and was originally in scope for GEP-713 but deferred for a future PR to keep GEP-713 focused on stabilizing what we already have (See [https://github.com/kubernetes-sigs/gateway-api/pull/3609#discussion_r2053376938](https://github.com/kubernetes-sigs/gateway-api/pull/3609#discussion_r2053376938)).
323+
324+
##### **Experimental Pattern**
325+
326+
To mitigate some of the concerns, `LabelSelector` support in policy attachment is designated as an **experimental pattern**.
327+
328+
* **Gateway API Community First:** Allows experimentation within Gateway API policies (like the one in this GEP).
329+
* Implementations **should not** adopt `LabelSelector` targeting in their own custom policies attached to Gateway API resources until the pattern is sufficiently battle-tested and promoted to a standard feature. This staged approach mitigates risks of ecosystem fragmentation.
330+
331+
Here is how it is going to look like:
332+
333+
```go
334+
335+
// PolicyTargetReferenceWithLabelSelectors specifies a reference to a set of Kubernetes
336+
// objects by Group and Kind, with an optional label selector to narrow down the matching
337+
// objects.
338+
//
339+
// Currently, we only support label selectors when targeting Pods.
340+
// This restriction is intentional to limit the complexity and potential
341+
// ambiguity of supporting label selectors for arbitrary Kubernetes kinds.
342+
// Unless there is a very strong justification in the future, we plan to keep this
343+
// functionality limited to selecting Pods only.
344+
//
345+
// This is currently experimental in the Gateway API and should only be used
346+
// for policies implemented within Gateway API. It is currently not intended for general-purpose
347+
// use outside of Gateway API resources.
348+
// +kubebuilder:validation:XValidation:rule="!(has(self.selector)) || (self.kind == 'Pod' && (self.group == '' || self.group == 'core'))",message="Selector may only be set when targeting Pods."
349+
type PolicyTargetReferenceWithLabelSelectors struct {
350+
// Group is the group of the target object.
351+
Group Group `json:"group"`
352+
353+
// Kind is the kind of the target object.
354+
Kind Kind `json:"kind"`
355+
356+
// Selector is the label selector of target objects of the specified kind.
357+
Selector *metav1.LabelSelector `json:"selector"`
358+
}
359+
360+
```
361+
362+
##### **Enhanced Discoverability with `gwctl`**
363+
364+
A key challenge with `LabelSelector` is the loss of discoverability. It’s easier to see which policies target a `Service` but difficult to determine which policies might affect a specific pod.
365+
366+
To address this, **investment in tooling is required.** Specifically, the `gwctl` CLI tool should be enhanced to provide insights such as:
367+
368+
```sh
369+
TODO: complete gwctl commands
370+
```
371+
372+
Without dedicated tooling, the `LabelSelector` approach could significantly degrade the user experience and observability.
373+
374+
### API Design
375+
376+
```go
377+
378+
type AuthorizationPolicy struct {
379+
metav1.TypeMeta `json:",inline"`
380+
metav1.ObjectMeta `json:"metadata,omitempty"`
381+
382+
// Spec defines the desired state of AuthorizationPolicy.
383+
Spec AuthorizationPolicySpec `json:"spec,omitempty"`
384+
385+
// Status defines the current state of AuthorizationPolicy.
386+
Status PolicyStatus `json:"status,omitempty"`
387+
}
388+
389+
// AuthorizationPolicyAction specifies the action to take.
390+
// +kubebuilder:validation:Enum=ALLOW;DENY
391+
type AuthorizationPolicyAction string
392+
393+
const (
394+
// ActionAllow allows requests that match the policy rules.
395+
ActionAllow AuthorizationPolicyAction = "ALLOW"
396+
// ActionDeny denies requests that match the policy rules.
397+
ActionDeny AuthorizationPolicyAction = "DENY"
398+
)
399+
400+
// AuthorizationPolicySpec defines the desired state of AuthorizationPolicy.
401+
type AuthorizationPolicySpec struct {
402+
// TargetRef identifies the resource this policy is attached to.
403+
// +kubebuilder:validation:Required
404+
TargetRef gatewayv1.PolicyTargetReference `json:"targetRef"`
405+
406+
// Action specifies the action to take when a request matches the rules.
407+
// +kubebuilder:validation:Required
408+
Action AuthorizationPolicyAction `json:"action"`
409+
410+
// TCPRules defines the list of matching criteria. A request is considered to
411+
// match the policy if it matches any of the rules.
412+
// +optional
413+
TCPRules []AuthorizationTCPRule `json:"tcpRules,omitempty"`
414+
}
415+
416+
// AuthorizationTCPRule defines a set of criteria for matching a TCP request.
417+
// A request must match all specified criteria.
418+
type AuthorizationTCPRule struct {
419+
// Sources specifies a list of identities that are matched by this rule.
420+
// If this field is empty, this rule matches all sources.
421+
// A request matches if its identity is present in this list.
422+
// +optional
423+
Sources []AuthorizationSource `json:"sources,omitempty"`
424+
425+
// Attributes specifies a list of properties that are matched by this rule.
426+
// If this field is empty, this rule matches all attributes.
427+
// A request matches if its attributes are present in this list.
428+
//
429+
// +optional
430+
Attributes []AuthorizationTCPAttributes `json:"attributes,omitempty"`
431+
}
432+
433+
434+
// AuthorizationSource specifies the source of a request.
435+
// Only one of its fields may be set.
436+
// +kubebuilder:validation:XValidation:rule="(size(self.identities) > 0 ? 1 : 0) + (size(self.serviceAccounts) > 0 ? 1 : 0) + (size(self.namespaces) > 0 ? 1 : 0) == 1",message="Exactly one of identities, serviceAccounts, or namespaces must be specified."
437+
type AuthorizationSource struct {
438+
439+
// Identities specifies a list of identities in SPIFFE format that are
440+
// matched by this rule. A request's identity must be present in this list
441+
// to match the rule.
442+
443+
// Identities for authorization can be derived in various ways by the underlying
444+
// implementation. Common methods include:
445+
// - From peer mTLS certificates: The identity is extracted from the client's
446+
// mTLS certificate presented during connection establishment.
447+
// - From IP-to-identity mappings: The implementation might maintain a dynamic
448+
// mapping between source IP addresses (pod IPs) and their associated
449+
// identities (e.g., Service Account, SPIFFE IDs).
450+
// - From JWTs or other request-level authentication tokens.
451+
//
452+
// Note for reviewers: While this GEP primarily focuses on identity-based
453+
// authorization where identity is often established at the transport layer,
454+
// some implementations might derive identity from authenticated tokens
455+
// within the request itself.
456+
//
457+
// +optional
458+
Identities []string `json:"identities,omitempty"`
459+
460+
// ServiceAccounts specifies a list of Kubernetes Service Accounts that are
461+
// matched by this rule. Each service account must be specified in the format
462+
// "<namespace>/<service-account-name>". A request originating from a pod
463+
// associated with one of these service accounts will match the rule.
464+
// +optional
465+
ServiceAccounts []string `json:"serviceAccounts,omitempty"`
466+
467+
// Namespaces specifies a list of Kubernetes Namespaces that are matched
468+
// by this rule. A request originating from any pod within one of these
469+
// namespaces will match the rule, regardless of its specific Service Account.
470+
// This provides a broader scope for authorization.
471+
// +optional
472+
Namespaces []string `json:"namespaces,omitempty"`
473+
}
474+
475+
// AuthorizationAttribute defines L4 properties of a request destination.
476+
type AuthorizationTCPAttributes struct {
477+
// Ports specifies a list of destination ports to match on.
478+
// Traffic is matched if it is going to any of these ports.
479+
// If not specified, the rule applies to all ports.
480+
// +optional
481+
Ports []gatewayv1.PortNumber `json:"ports,omitempty"`
482+
}
483+
484+
```
253485

254486
## Conformance Details
255487

0 commit comments

Comments
 (0)