sigs.k8s.io/gateway-api@v1.0.0/geps/gep-1619.md (about)

     1  # GEP-1619: Session Persistence
     2  
     3  * Issue: [#1619](https://github.com/kubernetes-sigs/gateway-api/issues/1619)
     4  * Status: Provisional
     5  
     6  (See definitions in [GEP Status][/contributing/gep#status].)
     7  
     8  ## Graduation Criteria for Implementable Status
     9  
    10  This GEP was accidentally merged as Provisional before the required approval
    11  from 2 maintainers had been received. Before this graduates to implementable,
    12  we need to get at least one of @robscott or @youngnick to also approve this GEP.
    13  
    14  There are some open questions that will need to be answered before this can
    15  graduate:
    16  
    17  1. Should we leave room in this policy to add additional concepts in the future
    18     such as Session Affinity? If so, how would we adjust the naming and overall
    19     scope of this policy?
    20  2. Should we leave room for configuring different forms of Session Persistence?
    21     If so, what would that look like?
    22  
    23  ## TLDR
    24  
    25  This GEP initially proposes a definitions for session persistence, followed by the definition of an API spec for
    26  configuring it. Additionally, it explores example scenarios of session persistence and examines the approaches that
    27  implementations have taken to design APIs for session persistence. It intentionally refrains from defining an API for
    28  session affinity, as this design is expected to be addressed within a separate GEP.
    29  
    30  ## Goals
    31  
    32  - Define session persistence and session affinity to establish a common language
    33  - Identify differences in session persistence functionality between implementations
    34  - Define an API for session persistence
    35  - Establish anticipated outcomes for specific API configurations or scenarios
    36  
    37  ## Non-Goals
    38  
    39  - Define an API for session affinity
    40  - Mandate a default session persistence or session affinity functionality for implementations
    41  - Prescribe the precise manner (the "how") in which implementations should achieve session persistence or handle
    42    specific scenarios
    43  - Add API configuration for supporting backend initiated sessions
    44  
    45  ## Introduction
    46  
    47  ### Defining Session Persistence
    48  
    49  Session persistence is when a client request is directed to the same backend server for the duration of a "session". It is achieved when a client directly provides information, such as a header, that a proxy uses as a reference to direct traffic to a specific server. Persistence is an exception to load balancing: a persistent client request bypasses the proxy's load balancing algorithm, going directly to a backend server it has previously established a session with.
    50  
    51  Session persistence enables more efficient application workflows:
    52  1. Better performance: Maintaining a single session allows a server to cache information about a client locally reducing the need for servers to exchange session data and overall storage needs.
    53  2. Seamless client experience: Clients can reconnect to the same server without re-authenticating or re-entering their information.
    54  
    55  Some of the concerns of session persistence are the duration and expiration of the session, security of the transaction stream, and storage of the context or state.
    56  
    57  Session affinity, not to be confused with session persistence, uses an existing attribute of the request to consistently send to the same backend. Session affinity can be considered a weaker form of session persistence: it is not guaranteed to persist a connection to the same backend server if certain attributes of the request or the backends are changed.
    58  
    59  ### Security and Privacy Implications
    60  
    61  Session persistence can introduce security and privacy vulnerabilities if not properly implemented. These vulnerabilities can include:
    62  
    63  1. Session hijacking: Attackers intercepting or predicting a valid session token to gain unauthorized access.
    64  2. Session fixation: Attackers setting a client's session ID to a known value, which they can then use to hijack the session.
    65  3. Session replay attacks: Attackers capturing and resending a client's message with a valid session ID.
    66  4. Data leakage: Attackers can exploit sensitive session information cached on servers if not properly secured.
    67  5. Denial of service attacks: Attackers can use up server resources by creating and maintaining large numbers of sessions.
    68  
    69  To mitigate these security concerns, it is important to implement session persistence using secure practices, such as using strong session ID generation algorithms, implementing session timeouts, encrypting sensitive data, and monitoring server resources for unusual activity.
    70  
    71  IP address reuse may also be a security or privacy concern when using session persistence or session affinity. If Kubernetes reuses an IP address of previously shutdown pod, the new pod may receive session persistent traffic meant for the old pod.
    72  
    73  Session affinity introduces fewer security and privacy vulnerabilities since there are no session tokens to protect or exploit.
    74  
    75  ### Achieving Session Persistence
    76  
    77  Session persistence is achieved using attributes residing in the application layer. The following are mechanisms for achieving session persistence:
    78  
    79  **1. Cookie-Based Session Persistence**
    80  
    81  The most common mechanism is by using cookies (described by [RFC6265](https://www.rfc-editor.org/rfc/rfc6265)) with the set-cookie HTTP response header. A client will use the provided value in the set-cookie response header in a cookie request header in subsequent requests. Proxies can use this cookie header to maintain a persistent connection to a single backend server on behalf of the client.
    82  
    83  **2. Header-Based Session Persistence**
    84  
    85  Header-based stateful sessions are achieved by a backend or gateway providing an HTTP response header and the client using the same header in subsequent HTTP requests. Proxies can use this header to maintain a persistent connection to a single backend server on behalf of the client.
    86  
    87  **3. URL-Encoded Session Persistence**
    88  
    89  Session information can be also encoded into the request URL to establish a persistent session. The server rewrites the client's URL to encode the new session information automatically. The server or the gateway then decodes the session information from the URL to identify the session.
    90  
    91  ### Session Persistence Initiation
    92  
    93  For all implementations of session persistence, the initiation of the persistent session is possible from various
    94  sources, including the gateway, intermediary gateway, backend, a sidecar in a backend, or any other infrastructure
    95  component.
    96  
    97  Let's consider a simple implementation comprised of gateways and backends. The following rules apply based on who initiates the session:
    98  - If the gateway initiates the session, the backend will be presented with session attributes regardless if it enabled them.
    99  - If the backend initiates the session, the gateway should allow this and not force persistent connections, unless
   100    specifically configured to[^1]. The gateway may decode and alter the cookie established by the backend to achieve
   101    session persistence.
   102  
   103  It's important to note that we can have more complex implementations which involve traversing global load balancers,
   104  regional load balancers, intermediary internal gateways, sidecars, or waypoints before reaching the backend. At any
   105  point within this architecture, a persistent session can be initiated. See [Global Load Balancer Initiated Session Example](#global-load-balancer-initiated-session-example)
   106  for an example of one of these alternative implementations.
   107  
   108  In the next sections, we will take a closer look at the initiation of sessions in both the gateway and the backend.
   109  Please note that in the next sections, we are examining the various scenarios in which a session can be initiated. We
   110  are not prescribing specific implementations for session persistence. The intention is to understand the possibilities
   111  and behaviors related to session initiation while the [API](#api) section will provide more details on specific
   112  implementation details.
   113  
   114  #### Gateway Initiated Session Example
   115  
   116  To illustrate how a gateway can initiate a session, let's examine an implementation that uses cookies for persistence.
   117  This represents the most straightforward scenario for utilizing cookies. When a request is made, the gateway includes
   118  the `set-cookie` header in the final response, prompting the client to store the cookie. This cookie is subsequently
   119  used in future requests, allowing the gateway to consistently choose the same upstream, establishing a persistent
   120  session.
   121  
   122  Here an example implementation of a gateway initiating a session through cookies:
   123  ```mermaid
   124  sequenceDiagram
   125      actor C as Client
   126      participant G as Gateway
   127      participant B as Backend
   128      C->>+G: Request Web Page
   129      activate G
   130      G->>+B: Request
   131      B-->>-G: Response
   132      G->>G: Add set-cookie header
   133      G-->>-C: Response<br>[set-cookie]
   134      Note right of G: [set-cookie] indicates a response<br> with a set-cookie header.<br>May include other set-cookie<br>headers from backend.
   135      C->>C: Create Cookie(s)<br>from set-cookie header(s)
   136      Note right of C: [cookie] indicates a request<br> with one or more cookies
   137      C->>+G: Request Web Page<br>[cookie]
   138      G->>G: Consistent lookup of<br>server using cookie value
   139      G->>+B: Request<br>[cookie]
   140      B-->>-G: Response
   141      G-->>-C: Response
   142  ```
   143  
   144  #### Backend Initiated Session Example
   145  
   146  **Important**: While we took it into consideration, this GEP does not support configuring backend-initiated sessions.
   147  This could potentially affect frameworks that initiate sessions in the backend. Implementing this feature is complicated
   148  and requires careful design, making it suitable for exploration in a separate GEP.
   149  
   150  Continuing with the cookie example, when dealing with backend-initiated sessions, the process becomes somewhat more
   151  complex. For cookie-based session persistence, the gateway needs to store a value within a cookie containing a backend
   152  identifier. This identifier can be then used as a reference to maintain a persistent session to a specific backend.
   153  There are several approaches a gateway could use in this situation to achieve session persistence:
   154  
   155  1. Insert an additional cookie
   156  2. Modify the existing cookie's value
   157  3. Prefix the existing cookie
   158  
   159  Additionally, there are variations to each of these approaches, such as making new or updated cookies transparent to the
   160  backend, either by remove an inserted cookie or reversing modifications of the cookie's value.
   161  
   162  Alternatively, if the backend is not configured for session persistence, the gateway should refrain from modifying or
   163  inserting a cookie. In this situation, the gateway should remain passive and simply forward the `set-cookie` header as
   164  it is.
   165  
   166  Refer to the [Session Initiation Guidelines](#session-initiation-guidelines) section of the API for implementation
   167  guidance.
   168  
   169  Here's an example implementation of a backend initiating a session and the gateway modifies the cookie's value:
   170  ```mermaid
   171  sequenceDiagram
   172      actor C as Client
   173      participant G as Gateway
   174      participant B as Backend
   175      C->>+G: Request Web Page
   176      activate G
   177      G->>+B: Request
   178      B->>B: Add set-cookie<br>header
   179      B-->>-G: Response<br>[set-cookie]
   180      G->>G: Modify set-cookie<br>header per configuration
   181      G-->>-C: Response<br>[set-cookie*]
   182      Note right of G: [set-cookie] indicates a response<br> with a set-cookie header<br>[set-cookie*] indicates a response<br>with a MODIFIED set-cookie header
   183      C->>C: Create Cookie<br>from set-cookie header
   184      Note right of C: [cookie] indicates a request<br>or response with a cookie
   185      C->>+G: Request Web Page<br>[cookie]
   186      G->>G: Consistent lookup<br>of server using cookie value
   187      G->>+B: Request<br>[cookie]
   188      B-->>-G: Response
   189      G-->>-C: Response
   190  ```
   191  
   192  #### Global Load Balancer Initiated Session Example
   193  
   194  In a more complex architecture example, a global load balancer may need to use cookies in order to maintain persistent
   195  connections to a regional load balancer. The regional cluster load balancer initiates the session by issuing the
   196  `set-cookie` header and subsequently uses the cookie to maintain persistent connections to a specific backend. The
   197  global load balancer then adds or modifies a cookie in order to establish persistent connection to a regional cluster
   198  load balancer.
   199  
   200  Here an example implementation of a global load balancer and a regional load balancer creating sessions through cookies:
   201  ```mermaid
   202  sequenceDiagram
   203      actor C as Client
   204      participant G as Global<br>Load Balancer
   205      participant R as Regional Cluster<br>Load Balancer
   206      participant B as Backend
   207      C->>+G: Request Web Page
   208      G->>+R: Request
   209      R->>+B: Request
   210      B-->>-R: Response
   211      R->>R: Initiates session by<br>adding set-cookie header
   212      R-->>-G: Response<br>[set-cookie]
   213      G->>G: Add or modify<br>set-cookie header
   214      G-->>-C: Response<br>[set-cookie]
   215      Note right of G: [set-cookie] indicates a response<br> with a set-cookie header.<br>May include other set-cookie<br>headers from backend or GLB.
   216      C->>C: Create Cookie<br>from set-cookie header
   217      Note right of C: [cookie] indicates a request<br> with one or more cookies
   218      C->>+G: Request Web Page<br>[cookie]
   219      G->>G: Consistent lookup of<br>regional cluster load balancer<br>using cookie value
   220      G->>+R: Request<br>[cookie]
   221      R->>R: Consistent lookup of backend<br>using cookie value
   222      R->>+B: Request<br>[cookie]
   223      B-->>-G: Response
   224      G-->>-C: Response
   225  ```
   226  
   227  ### When does an application require session persistence?
   228  
   229  Enabling session persistence is a required configuration for applications intentionally designed by the application
   230  developer to use it, as they will encounter failures or malfunctions when it's not enabled. However, it's worth noting
   231  that certain applications may be designed to function both with and without session persistence. Regardless, the
   232  importance of Gateway API supporting session persistence remains emphasized because it is frequently seen as a necessary
   233  feature.
   234  
   235  Conversely, apps that have not been designed or tested with session persistence in mind may misbehave when it is
   236  enabled, primarily because of the impacts of load distribution on the app. Apps using session persistence must account
   237  for aspects like load shedding, draining, and session migration as a part of their application design.
   238  
   239  ### The Relationship of Session Persistence and Session Affinity
   240  
   241  Though this GEP's intention is not to define a spec for session affinity, it is important to recognize and understand
   242  its distinction with session persistence. While session persistence uses attributes in the application layer, session
   243  affinity often uses, but is not limited to, attributes below the application layer. Session affinity doesn't require a
   244  session identifier like session persistence (e.g. a cookie), but instead uses existing connection attributes to
   245  establish a consistent hashing load balancing algorithm. It is important to note the session affinity doesn't guarantee
   246  persistent connections to the same backend server.
   247  
   248  Session affinity can be achieved by deterministic load balancing algorithms or a proxy feature that tracks IP-to-backend associations such as [HAProxy's stick tables](https://www.haproxy.com/blog/introduction-to-haproxy-stick-tables/) or [Cilium's session affinity](https://docs.cilium.io/en/v1.12/gettingstarted/kubeproxy-free/#id2).
   249  
   250  We can also examine how session persistence and session affinity functionally work together, by framing the relationship into a two tiered logical decision made by the data plane:
   251  1. If the request contains a session persistence identity (e.g. a cookie or header), then route it directly to the backend it has previously established a session with.
   252  2. If no session persistence identity is present, load balance as per load balancing configuration, taking into account the session affinity configuration (e.g. by utilizing a hashing algorithm that is deterministic).
   253  
   254  This tiered decision-based logic is consistent with the idea that session persistence is an exception to load balancing. Though there are different ways to frame this relationship, this design will influence the separation between persistence and affinity API design.
   255  
   256  ### Implementations
   257  In this section, we will describe how implementations achieve session persistence, along with a breakdown of related configuration options. Input from implementations is appreciated to complete this information.
   258  
   259  In the following tables, we will example two types of APIs:
   260  1. Dataplane APIs
   261  2. Implementation APIs
   262  
   263  Generally, the implementation API programs the dataplane API; however these two are not always clearly separated. The two types of APIs can use different API structures for configuring the same feature. Examining the dataplane APIs helps to remove the layer of API abstraction that implementations provide. Removing this layer avoids situations where implementations don’t fully implement all capabilities of a dataplane API or obfuscate certain configuration around session persistence. On the other hand, examining implementation APIs provides valuable data points in what implementations are interested in configuring.
   264  
   265  | **Technology** 	| **Technology Type** 	| **Session Persistence Type** 	| **Configuration Options** 	| **Configuration Association (Global, Gateway, Route, or Backends)** 	| **Notes** 	|
   266  |---	|---	|---	|---	|---	|---	|
   267  | Acnodal EPIC 	| Implementation (Envoy) 	| N/A 	| Supports Gateway API Only* 	| N/A 	| *Acnodal Epic solely uses Gateway API; therefore, it doesn’t yet have a way to configure session persistence. [Acnodal EPIC Docs](https://www.epick8sgw.io/docs/) 	|
   268  | Apache APISIX 	| Implementation (Nginx) 	| [Cookie-Based](https://apisix.apache.org/docs/apisix/admin-api/#upstream) 	| hash_on=[vars \| header \| cookie \| consumer]<br>key=cookie_name 	| [Upstream](https://apisix.apache.org/docs/apisix/admin-api/#upstream) (Route or Backends) 	| N/A 	|
   269  |  	| Implementation (Nginx) 	| [Header-Based](https://apisix.apache.org/docs/apisix/terminology/upstream/#header) 	| hash_on=[vars \| header \| cookie \| consumer]<br>key=header_name 	| [Upstream](https://apisix.apache.org/docs/apisix/admin-api/#upstream) (Route or Backends) 	| N/A 	|
   270  | Apache httpd 	| Web Server 	| [Cookie-Based / URL-Encoded](https://httpd.apache.org/docs/2.4/mod/mod_proxy_balancer.html) 	| Cookie Attributes 	| N/A 	| N/A 	|
   271  | Cilium 	| Implementation / Dataplane 	| None 	| None 	| None 	| Cilium has no documented way of doing session persistence. [Cilium Docs](https://cilium.io/)  	|
   272  | Contour 	| Implementation (Envoy) 	| [Cookie-Based](https://projectcontour.io/docs/1.24/config/api/#projectcontour.io/v1.CookieRewritePolicy)  	| Name=name<br>pathRewrite=path<br>domainRewrite=domain<br>secure<br>sameSite 	| [Route](https://projectcontour.io/docs/1.24/config/api/#projectcontour.io/v1.Route) and [Service](https://projectcontour.io/docs/1.24/config/api/#projectcontour.io/v1.Service) (Backends) 	| Envoy does not natively support cookie attribute rewriting nor adding attributes other than path and TTL, but rewriting and adding additional attributes is possible via Lua ([Contour design reference](https://github.com/projectcontour/contour/blob/main/design/cookie-rewrite-design.md), [Envoy Issue](https://github.com/envoyproxy/envoy/issues/15612)). 	|
   273  | Emissary-Ingress 	| Implementation (Envoy) 	| [Cookie-Based](https://www.getambassador.io/docs/emissary/latest/topics/running/load-balancer#cookie) 	| Name=name<br>Path=path<br>TTL=duration 	| [Module or Mapping](https://www.getambassador.io/docs/emissary/latest/topics/running/load-balancer#cookie) (Global or Route) 	| N/A 	|
   274  |  	|  	| [Header-Based](https://www.getambassador.io/docs/emissary/latest/topics/running/load-balancer#header) 	| Name=name 	| [Module or Mapping](https://www.getambassador.io/docs/emissary/latest/topics/running/load-balancer#cookie) (Global or Route) 	| N/A 	|
   275  | Envoy 	| Dataplane 	| [Cookie-Based](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/http/stateful_session/cookie/v3/cookie.proto) 	| Name=name<br>Path=path<br>TTL=duration 	| [HttpConnectionManager](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto) (Route) 	| Envoy does not natively support cookie attribute rewriting nor adding attributes other than path and TTL, but rewriting and adding additional attributes is possible via Lua ([Contour design reference](https://github.com/projectcontour/contour/blob/main/design/cookie-rewrite-design.md), [Envoy Issue](https://github.com/envoyproxy/envoy/issues/15612)). 	|
   276  |  	|  	| [Header-Based](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/http/stateful_session/header/v3/header.proto) 	| Name=name 	| [HttpConnectionManager](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto) (Route) 	| N/A 	|
   277  | Envoy Gateway 	| Implementation (Envoy) 	| N/A 	| Supports Gateway API Only* 	| N/A 	| *Envoy Gateway solely uses Gateway API; therefore, it doesn’t yet have a way to configure session persistence. [Envoy Gateway Docs](https://gateway.envoyproxy.io/v0.3.0/index.html) 	|
   278  | Flomesh Service Mesh 	| Implementation / Dataplane (Pipy) 	| ? 	| ? 	| ? 	| ? 	|
   279  | Gloo Edge 2.0 	| Implementation (Envoy) 	| [Cookie-Based](https://docs.solo.io/gloo-edge/latest/reference/api/envoy/api/v2/route/route.proto.sk/#cookie) 	| Name=name<br>Path=path<br>TTL=duration 	| [Route](https://docs.solo.io/gloo-edge/latest/reference/api/envoy/api/v2/route/route.proto.sk/#route) (Route) 	| N/A 	|
   280  |  	|  	| [Header-Based](https://docs.solo.io/gloo-edge/latest/reference/api/envoy/api/v2/route/route.proto.sk/#hashpolicy) 	| Name=name 	| [Route](https://docs.solo.io/gloo-edge/latest/reference/api/envoy/api/v2/route/route.proto.sk/#route) (Route) 	| N/A 	|
   281  | Google CloudRun 	| Implementation / Dataplane 	| [Cookie-Based](https://cloud.google.com/run/docs/configuring/session-affinity) 	| Enabled / Disabled 	| [Service](https://cloud.google.com/run/docs/configuring/session-affinity) (Backends) 	| Only allowed to turn off or on, no other configuration items 	|
   282  | Google Kubernetes Engine 	| Implementation / Dataplane 	| [Cookie-Based](https://cloud.google.com/load-balancing/docs/backend-service#session_affinity) 	| GENERATED_COOKIE or HTTP_COOKIE=name<br>cookieTtlSec 	| [Backend Policy](https://cloud.google.com/kubernetes-engine/docs/how-to/configure-gateway-resources#session_affinity) (Backends) 	| Google Kubernetes Engine [lists](https://cloud.google.com/load-balancing/docs/backend-service#bs-session-affinity) the products that can do persistence/affinity mode. All persistence/affinity options are exclusive and can’t be used at the same time.<br>Note: Google Kubernetes Engine defines everything (persistence and affinity) as session affinity. 	|
   283  |  	|  	| [Header-Based](https://cloud.google.com/load-balancing/docs/backend-service#header_field_affinity) 	| httpHeaderName=name 	| [Backend Policy](https://cloud.google.com/kubernetes-engine/docs/how-to/configure-gateway-resources#session_affinity) (Backends) 	| N/A 	|
   284  | HAProxy 	| Dataplane 	| [Cookie-Based](https://docs.haproxy.org/2.6/configuration.html#4.2-cookie) 	| name=name<br>[rewrite \| insert \| prefix ]<br>indirect<br>nocache<br>postonly<br>preserve<br>httponly<br>secure<br>domain=domain<br>maxidle=idle<br>maxlife=life<br>dynamic<br>attr=value 	| [Default or Backends](https://docs.haproxy.org/2.6/configuration.html#4.2-cookie) (Global or Backends) 	| HAProxy allows for operational cookie strategy configuration (i.e. when/how HAProxy should inject cookies) 	|
   285  | HAProxy Ingress 	| Implementation (HAProxy) 	| [Cookie-Based](https://haproxy-ingress.github.io/docs/configuration/keys/#affinity) 	| affinity (enable/disable)<br>cookie-key=key<br>session-cookie-domain=domain<br>session-cookie-dynamic=[true \| false]<br>session-cookie-keywords=keywords<br>session-cookie-name=name<br>session-cookie-preserve=[true \| false]<br>session-cookie-same-site=[true \| false]<br>session-cookie-shared=[true \| false]<br>session-cookie-strategy=strategy<br>session-cookie-value-strategy=value_strategy	 	| [Backend](https://haproxy-ingress.github.io/docs/configuration/keys/#affinity) (Backends) 	| N/A 	|
   286  | Hashicorp Consul  	| Implementation (Envoy) 	| N/A 	| Supports Gateway API Only* 	| N/A 	| *Hashicorp Consul solely uses Gateway API; therefore, it doesn’t yet have a way to configure session persistence. [Hashicorp Consul API Gateway Docs](https://developer.hashicorp.com/consul/docs/api-gateway) 	|
   287  | Istio 	| Implementation (Envoy) 	| [Cookie-Based](https://istio.io/latest/docs/reference/config/networking/destination-rule/#LoadBalancerSettings-ConsistentHashLB-HTTPCookie) 	| Name=name<br>Path=path<br>TTL=duration 	| [ConsistentHashLB](https://istio.io/latest/docs/reference/config/networking/destination-rule/#LoadBalancerSettings-ConsistentHashLB) (Backends) 	| Istio also supports turning on cookie-based session persistence via the Pilot ENV variable [PILOT_PERSISTENT_SESSION_LABEL](https://istio.io/latest/docs/reference/commands/pilot-discovery/#envvars). 	|
   288  |  	| Implementation (Envoy) 	| [Header-Based](https://istio.io/latest/docs/reference/config/networking/destination-rule/#LoadBalancerSettings-ConsistentHashLB) 	| Name=name 	| [ConsistentHashLB](https://istio.io/latest/docs/reference/config/networking/destination-rule/#LoadBalancerSettings-ConsistentHashLB) (Backends) 	| N/A 	|
   289  | Java Servlet 	| Web Server 	| [Cookie-Based / URL-Encoded](https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSession.html) 	| invalidate()<br>setAttribute(String name, Object value)<br>setMaxInactiveInterval(int interval) 	| N/A 	| Java Servlets do not natively support proxy functions. 	|
   290  | Kong 	| Implementation / Dataplane 	| [Cookie-Based](https://docs.konghq.com/hub/kong-inc/session/) 	| cookie_name=name<br>rolling_timeout=timeout<br>absolute_timeout=timeout<br>idling_timeout=timeout<br>cookie_path=path<br>cookie_domain=domain<br>cookie_same_site=[Strict \| Lax \| None \| off]<br>cookie_http_only<br>cookie_secure=[true \| false]<br>stale_ttl=duration<br>cookie_persistent=[true \| false]<br>storage=storage_type 	| [Route, Service, Global](https://docs.konghq.com/hub/kong-inc/session/) (Route or Backends or Global) 	| N/A 	|
   291  |  	|  	| [Header-Based](https://docs.konghq.com/gateway/latest/how-kong-works/load-balancing/#balancing-algorithms) 	| name 	| [Upstreams](https://docs.konghq.com/gateway/3.2.x/admin-api/#add-upstream) (Backends) 	| N/A 	|
   292  | Kuma 	| Implementation (Envoy) 	| None 	| None 	| None 	| Kuma has no documentation on how it supports session persistence or cookies. [Kuma Docs](https://kuma.io/docs/2.1.x/) 	|
   293  | Nginx  	| Dataplane 	| [Cookie-Based (Nginx Plus Only)](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#enabling-session-persistence) 	| Name=name<br>Expires=time<br>Domain=domain<br>HttpOnly<br>SameSite = [strict \| lax \| none \| $variable]<br>Secure<br>path=path 	| [Upstream](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#enabling-session-persistence) (Backends) 	| See also [Sticky Cookie](https://nginx.org/en/docs/http/ngx_http_upstream_module.html?&_ga=2.184452070.1306763907.1680031702-1761609832.1671225057#sticky_cookie) 	|
   294  | NGINX Gateway Fabric 	| Implementation (Nginx) 	| N/A 	| Supports Gateway API Only* 	| N/A 	| *NGINX Gateway Fabric solely uses Gateway API; therefore, it doesn’t yet have a way to configure session persistence. [NGINX Gateway Fabric Docs](https://github.com/nginxinc/nginx-gateway-fabric) 	|
   295  | Traefik 	| Implementation / Dataplane 	| [Cookie-Based](https://doc.traefik.io/traefik/routing/services/#sticky-sessions) 	| name=name<br>secure<br>httpOnly<br>sameSite=[none \| lax \| strict ] 	| [Services](https://doc.traefik.io/traefik/routing/services/#sticky-sessions) (Backends) 	| N/A 	|
   296  
   297  ### Sessions in Java
   298  
   299  Java application servers such as Tomcat and Jetty, were the first to standardize the API around cookies and sessions.
   300  These Java applications introduced the “jsessionid” cookie and session IDs encoded in URL parameters as well as more
   301  advanced features such as session migration, replication, and on demand session activation. It’s important for Gateway
   302  API to examine cookie use cases and history from Java APIs to ensure the API is designed appropriately.
   303  
   304  ### Session Affinity in K8S Services
   305  
   306  Kubernetes provides an API that allows you to enable [session affinity](https://kubernetes.io/docs/reference/networking/virtual-ips/#session-affinity)
   307  on service objects. It ensures consistent sessions by utilizing the client's IP address and also offers the option to
   308  set a timeout for the maximum session duration. Implementations of Gateway API, such as service mesh use cases, may use
   309  the service IP directly. In these cases where both Kubernetes service session affinity and Gateway API session
   310  persistence are both enabled, the route must be rejected, and a status should be set describing the incompatibility of
   311  these two configurations.
   312  
   313  ## API
   314  
   315  In this section, we will explore the questions and design elements associated with a session persistence API.
   316  
   317  ### GO
   318  
   319  ```go
   320  // SessionPersistencePolicy provides a way to define session persistence rules
   321  // for a service or route.
   322  //
   323  // Support: Core
   324  type SessionPersistencePolicy struct {
   325      metav1.TypeMeta   `json:",inline"`
   326      metav1.ObjectMeta `json:"metadata,omitempty"`
   327  
   328      // Spec defines the desired state of SessionPersistencePolicy.
   329      Spec SessionPersistencePolicySpec `json:"spec"`
   330  
   331      // Status defines the current state of SessionPersistencePolicy.
   332      Status SessionPersistencePolicyStatus `json:"status,omitempty"`
   333  }
   334  
   335  // SessionPersistencePolicySpec defines the desired state of
   336  // SessionPersistencePolicy.
   337  // Note: there is no Override or Default policy configuration.
   338  type SessionPersistencePolicySpec struct {
   339      // TargetRef identifies an API object to apply policy to.
   340      // The TargetRef may be a Service, HTTPRoute, GRPCRoute,
   341      // or a HTTPRouteRule or GRPCRouteRule section.
   342      // At least one of these targets must be supported for
   343      // core-level compliance.
   344      //
   345      TargetRef gatewayv1a2.PolicyTargetReference `json:"targetRef"`
   346  
   347      // AbsoluteTimeoutSeconds defines the absolute timeout of the
   348      // persistent session measured in seconds. Once
   349      // AbsoluteTimeoutSeconds has elapsed, the session becomes invalid.
   350      //
   351      // Support: Core
   352      //
   353      // +optional
   354      AbsoluteTimeoutSeconds int64 `json:"absoluteTimeoutSeconds,omitempty"`
   355  
   356      // IdleTimeoutSeconds defines the idle timeout of the
   357      // persistent session measured in seconds. Once the session
   358      // has been idle for more than specified IdleTimeoutSeconds
   359      // duration, the session becomes invalid.
   360      //
   361      // Support: Core
   362      //
   363      // +optional
   364      IdleTimeoutSeconds int64 `json:"idleTimeoutSeconds,omitempty"`
   365  
   366      // SessionName defines the name of the persistent session token
   367      // (e.g. a cookie name).
   368      //
   369      // Support: Extended
   370      //
   371      // +optional
   372      // +kubebuilder:validation:MaxLength=4096
   373      SessionName String `json:"sessionName,omitempty"`
   374  }
   375  
   376  // SessionPersistencePolicyStatus defines the observed state of SessionPersistencePolicy.
   377  type SessionPersistencePolicyStatus struct {
   378      // Conditions describe the current conditions of the SessionPersistencePolicy.
   379      //
   380      // Implementations should prefer to express SessionPersistencePolicy
   381      // conditions using the `SessionPersistencePolicyConditionType` and
   382      // `SessionPersistencePolicyConditionReason` constants so that
   383      // operators and tools can converge on a common vocabulary to
   384      // describe SessionPersistencePolicy state.
   385      // Known condition types are:
   386      //
   387      // * “Accepted”
   388      //
   389      // +optional
   390      // +listType=map
   391      // +listMapKey=type
   392      // +kubebuilder:validation:MaxItems=8
   393      // +kubebuilder:default={type: "Accepted", status: "Unknown", reason:"Pending", message:"Waiting for validation", lastTransitionTime: "1970-01-01T00:00:00Z"}
   394      Conditions []metav1.Condition `json:"conditions,omitempty"`
   395  }
   396  
   397  // SessionPersistencePolicyConditionType is the type of condition used
   398  // as a signal by SessionPersistencePolicy. This type should be used with
   399  // the SessionPersistencePolicy.Conditions field.
   400  type SessionPersistencePolicyConditionType string
   401  
   402  // SessionPersistencePolicyConditionReason is a reason that explains why a
   403  // particular SessionPersistencePolicyConditionType was generated. This reason
   404  // should be used with the SessionPersistencePolicy.Conditions field.
   405  type SessionPersistencePolicyConditionReason string
   406  
   407  const (
   408      // This condition indicates that the SessionPersistencePolicyStatus has been
   409      // accepted as valid.
   410      // Possible reason for this condition to be True is:
   411      //
   412      // * “Accepted”
   413      //
   414      // Possible reasons for this condition to be False are:
   415      //
   416      // * “Invalid”
   417      // * “Pending”
   418      SessionPersistencePolicyConditionAccepted SessionPersistencePolicyConditionType = “Accepted”
   419  
   420      // This reason is used with the “Accepted” condition when the condition is true.
   421      SessionPersistencePolicyReasonAccepted SessionPersistencePolicyConditionReason = “Valid”
   422  
   423  	// This reason is used with the “Accepted” condition when the SessionPersistencePolicy is invalid, e.g. crossing namespace boundaries.
   424      SessionPersistencePolicyReasonInvalid SessionPersistencePolicyConditionReason = “Invalid”
   425  
   426      // This reason is used with the “Accepted” condition when the SessionPersistencePolicy is pending validation.
   427      SessionPersistencePolicyReasonPending SessionPersistencePolicyConditionReason = “Pending”
   428  )
   429  ```
   430  
   431  ### API Granularity
   432  
   433  The purpose of this session persistence API spec is to enable developers to specify that a specific backend expects a
   434  persistent session. However, it intentionally avoids specifying low-level details or configurations related to the
   435  session persistence implementation, such as cookie attributes. This decision is because the Gateway API supports various
   436  infrastructure types, and some implementations that already provide session persistence may not be able to adhere to a
   437  low-level API.
   438  
   439  For instance, platforms using global load balancers to maintain persistent sessions between regional load balancers, or
   440  Tomcat servlets generating distinct cookies per server. In such scenarios, it is important that this GEP does not
   441  obstruct the existing use of cookies while enabling session persistence. Enabling particular low-level API
   442  configurations, like allowing customization of the cookie name, could prevent certain implementations from conforming to
   443  the spec. In other words, opting for a higher-level API provides better interoperability among our implementations.
   444  
   445  ### Target Persona
   446  
   447  Referring to the [Gateway API Security Model](https://gateway-api.sigs.k8s.io/concepts/security-model/#roles-and-personas),
   448  the target kubernetes role/persona for session persistence are application developers, as mentioned in the [When does an application require session persistence?](#when-does-an-application-require-session-persistence)
   449  section. It is the responsibility of the application developers to adjust the persistence configuration to ensure the
   450  functionality of their applications.
   451  
   452  ### Prior Art
   453  
   454  Referring to our [Implementations](#Implementations) table on session persistence, the majority of Gateway API
   455  implementations designed session persistence in their APIs to be attached to a service or backends. This should be
   456  considered cautiously, as making associations to Gateway API's notion of Gateway, Route, and Service to other
   457  implementation's objects is hard to directly translate. The idea of a route in Gateway API is often not the same as a
   458  route in any given implementation.
   459  
   460  ### Metaresource Policy Design
   461  
   462  In order to apply session persistence configuration to both a service and a route, we will implement it as a
   463  metaresource policy, as outlined in [Policy Attachment](https://gateway-api.sigs.k8s.io/reference/policy-attachment/#direct-policy-attachment).
   464  The metaresource is named `SessionPersistencePolicy` and is only responsible for configuring session persistence for
   465  services or routes. It is defined as a [Direct Policy Attachment](https://gateway-api.sigs.k8s.io/v1alpha2/reference/policy-attachment/#direct-policy-attachment)
   466  without defaults or overrides, applied to the targeted service, HTTPRoute, or GRPCRoute.
   467  
   468  Attaching the `SessionPersistencePolicy` metaresource to a service will be a core support level feature, while attaching
   469  it to a route will be considered extended. Implementations must support services for conformance, while routes are
   470  considered optional. This distinction arises because most existing implementations primarily support attaching to a
   471  service, while attaching to a route is less common and involves greater complexities (see [API Attachment Points](#api-attachment-points)
   472  for more details).
   473  
   474  ### API Attachment Points
   475  
   476  The `SessionPersistencePolicy` metaresource can target a service, route, and a route rule section (e.g. [HTTPRouteRule](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io%2fv1beta1.HTTPRouteRule)).
   477  The core functionality of attaching it to any of these is generally identical. The distinction lies in where the
   478  configuration gets propagated: when attached to a route or a route rule, it can define session persistence for multiple
   479  services, whereas attaching it to a service defines it for a single service. Enabling session persistence doesn't
   480  require configuration on the route, route rule, and service; configuring it on any one is sufficient.
   481  
   482  Given that certain implementations will only support attaching to services, and considering that some implementations
   483  might want to solely support attachment to routes, implementations must provide support for at least one of the options
   484  (route, route rule, or service) to be compliant with this GEP.
   485  
   486  Attaching `SessionPersistencePolicy` to a route ensures session persistence for all possible paths the route can take.
   487  Conversely, attaching to a route rule section provides session persistence exclusively for that route rule. Without the
   488  capability to target a specific rule section within a route, users might be required to decompose their route into
   489  multiple routes to satisfy different session persistence requirements for each individual route path.
   490  
   491  Edge cases will arise when providing supporting services, routes, and the route rule section. For guidance on addressing
   492  conflicting attachments, please consult the [Expected API Behavior](#expected-api-behavior) section, which outlines API
   493  use cases.
   494  
   495  To learn more about the process of attaching to services, routes, and the route rule section, please refer to
   496  [GEP-713](/geps/gep-713).
   497  
   498  ### Traffic Splitting
   499  
   500  In scenarios involving traffic splitting, session persistence impacts load balancing done after routing, regardless if
   501  it is attached to a service or a route. When a persistent session is established and traffic splitting is configured
   502  across services, the persistence to a single backend should be maintained across services. Consequently, a persistent
   503  session takes precedence over traffic split weights when selecting a backend after route matching. It's important to
   504  note that session persistence does not impact the process of route matching.
   505  
   506  When using multiple backends in traffic splitting, all backend services should have session persistence enabled.
   507  Nonetheless, implementations should also support traffic splitting scenarios in which one service has persistence
   508  enabled while the other does not. This support is necessary, particularly in scenarios where users are transitioning
   509  to or from an implementation version designed with or without persistence.
   510  
   511  See [Expected API Behavior](#expected-api-behavior) for more use cases on traffic splitting.
   512  
   513  ### Cookie Attributes
   514  
   515  While the API is intended to be generic, as described in [API Granularity](#api-granularity), a majority of
   516  implementations will employ session persistence through cookies. Therefore, let's explore the possibilities of cookie
   517  configuration for these APIs.
   518  
   519  A cookie is composed of various attributes, each represented as key=value pairs. While some attributes may have optional
   520  values, the cookie name attribute is the only mandatory one, and the rest are considered optional.
   521  
   522  The cookie attributes defined by [RFC6265](https://www.rfc-editor.org/rfc/rfc6265#section-5.2) are:
   523  - Name=_value_
   524  - Expires=_date_
   525  - Max-Age=_number_
   526  - Domain=_domain_
   527  - Path=_path-value_
   528  - Secure
   529  - HttpOnly
   530  
   531  Other cookie attributes not defined by RFC6265, but are captured in draft RFCs and could be considered de facto
   532  standards due to wide acceptance are:
   533  - SameSite=[Strict|Lax|None]
   534  - Partitioned
   535  
   536  Unless a `SessionPersistencePolicy` API field can be satisfied through a manipulating a cookie attribute, the attributes
   537  of the cookies are considered as opaque values in this spec and are to be determined by the individual implementations.
   538  Let's discuss some of these cookie attributes in more detail.
   539  
   540  #### Name
   541  
   542  The `Name` cookie attribute can be configured via the `SessionName` field on `SessionPersistencePolicy`. However, this
   543  field is considered extended support level. This is because some implementations, such as ones supporting global load
   544  balancers, don't have the capability to configure the cookie name.
   545  
   546  #### TTL
   547  
   548  The `TTL` cookie attribute may be influenced by the `AbsoluteTimeoutSeconds` field on `SessionPersistencePolicy`.
   549  However, it's important to understand that `AbsoluteTimeoutSeconds` represents the duration of the entire session, not
   550  just the cookie duration. Conversely, the cookie's `TTL` attribute does not have to be configured in order to implement
   551  `AbsoluteTimeoutSeconds`.
   552  
   553  #### Path
   554  
   555  The cookie's `Path` attribute defines the URL path that must exist in order for the client to send the `cookie` header.
   556  Whether attaching session persistence to an xRoute or a service, it's important to consider the relationship the cookie
   557  `Path` attribute has with the route path.
   558  
   559  When the `SessionPersistencePolicy` policy is attached to an xRoute, the implementor should interpret the path as
   560  configured on the xRoute. To interpret the `Path` attribute from an xRoute, implementors should take note of the
   561  following:
   562  
   563  1. For an xRoute that matches all paths, the `Path` should be set to `/`.
   564  2. For an xRoute that has multiple paths, the `Path` should be interpreted based on the route path that was matched.
   565  3. For an xRoute using a path that is a regex, the `Path` should be set to the longest non-regex prefix (.e.g. if the
   566     path is /p1/p2/*/p3 and the request path was /p1/p2/foo/p3, then the cookie path would be /p1/p2).
   567  
   568  It is also important to note that this design makes persistent session unique per route path. For instance, if two
   569  distinct routes, one with path prefix `/foo` and the other with `/bar`, both target the same service, the persistent
   570  session won't be shared between these two paths.
   571  
   572  Conversely, if the `SessionPersistencePolicy` policy is attached to a service, the `Path` attribute should be left
   573  unset. This is because multiple routes can target a single service. If the `Path` cookie attribute is configured in this
   574  scenario, it could result in problems due to the possibility of different paths being taken for the same cookie.
   575  Implementations should also handle the case where client is a browser making requests to multiple persistent services
   576  from the same page.
   577  
   578  #### Secure, HttpOnly, SameSite
   579  
   580  The `Secure`, `HttpOnly`, and `SameSite` cookie attributes are security-related. The API implementers should follow the
   581  security-by-default principle and configure these attributes accordingly. This means enabling `Secure` and `HttpOnly`,
   582  and setting `SameSite` to `Strict`. However, in certain implementation use cases such as service mesh, secure values
   583  might not function as expected. In such cases, it's acceptable to make appropriate adjustments.
   584  
   585  ### Session Persistence API with GAMMA
   586  
   587  The object of the [GAMMA (Gateway API for Mesh Management and Administration)](https://gateway-api.sigs.k8s.io/contributing/gamma/)
   588  initiative is to provide support for service mesh and mesh-adjacent use-cases with Gateway API. GAMMA is focused on
   589  defining how Gateway API could also be used for inter-service or [east/west](https://gateway-api.sigs.k8s.io/concepts/glossary/#eastwest-traffic)
   590  traffic within the same cluster.
   591  
   592  Given that service meshes commonly have session persistence requirements, this API design should take into consideration
   593  session persistence needs in GAMMA and service mesh scenarios.
   594  
   595  ### Session Initiation Guidelines
   596  
   597  As illustrated in the examples provided in [Session Persistence Initiation](#session-persistence-initiation),
   598  implementations must consider how to manage sessions initiated by other components. As mentioned in [Backend Initiated Session Example](#backend-initiated-session-example),
   599  this GEP does not support configuring backend-initiated persistent sessions. We leave the decision of handling existing
   600  sessions with each specific implementation. In the case of cookie-based session persistence, an implementation has the
   601  freedom to either rewrite the cookie or insert an additional cookie, or to do nothing (resulting in the lack of a
   602  persistent session). In general, inserting an additional cookie is a generally safe option, but it's important for
   603  implementations to exercise their own discretion. However, regardless of the implementation's design choice, the
   604  implementation must be able to handle multiple cookies.
   605  
   606  ### Expected API Behavior
   607  
   608  Implementing session persistence is complex and involves many edge cases. In this section, we will outline API
   609  configuration scenarios (use cases) and how implementations should handle them.
   610  
   611  #### Attaching Session Persistence to both Service And Route
   612  
   613  In a situation which:
   614  - `ServiceA` with `SessionPersistencePolicy` attached
   615  - `RouteX` with `SessionPersistencePolicy` attached and backend `ServiceA`
   616  
   617  The `SessionPersistencePolicy` policy attached to `RouteX` should take precedence. Since routes effectively group
   618  services, the policy attached to xRoutes operates at a higher-level and should override policies applied to individual
   619  services.
   620  
   621  ```mermaid
   622  graph TB
   623     RouteX ----> ServiceA((ServiceA))
   624     SessionPersistencePolicyServiceA[SessionPersistence] -.-> ServiceA
   625     SessionPersistencePolicyRouteA[SessionPersistence] -.Precedence.-> RouteX
   626     linkStyle 2 stroke:red;
   627  ```
   628  
   629  #### Two Routes Share Backends with Session Persistence Applied only on one Route
   630  
   631  In the situation in which:
   632  - `ServiceA` with`SessionPersistencePolicy` attached
   633  - `ServiceB` with no session persistence
   634  - `RouteX` with no session persistence backends `ServiceA` and `ServiceB`
   635  
   636  At this point, traffic through `RouteX` to `ServiceB` doesn't use session persistence.
   637  
   638  A new route is added:
   639  - `RouteY` with `SessionPersistencePolicy` attached
   640  
   641  Even though it's not explicitly configured, traffic flowing through `RouteX` to `ServiceB` should utilize session
   642  persistence. This behavior occurs because `ServiceB` has session persistence configured via `RouteY`.
   643  
   644  ```mermaid
   645  graph TB
   646     RouteX ----> ServiceA((ServiceA))
   647     RouteX --Persistence--> ServiceB
   648     RouteY ----> ServiceA
   649     RouteY ----> ServiceB((ServiceB))
   650     SessionPersistencePolicyServiceA[SessionPersistence] -.-> ServiceA
   651     SessionPersistencePolicyRouteB[SessionPersistence] -.-> RouteY
   652     linkStyle 1 stroke:red;
   653  ```
   654  
   655  #### Traffic Splitting
   656  
   657  Consider the scenario where a route is traffic splitting between two backends, and additionally, a
   658  `SessionPersistencePolicy` is attached to the route:
   659  ```yaml
   660  kind: HTTPRoute
   661  metadata:
   662    name: split-route
   663  spec:
   664    rules:
   665    - backendRefs:
   666      - name: servicev1
   667        weight: 50
   668      - name: servicev2
   669        weight: 50
   670  ---
   671  kind: SessionPersistencePolicy
   672  metadata:
   673    name: spp-split-route
   674  spec:
   675    targetRef:
   676      kind: HTTPRoute
   677      name: split-route
   678    type: HTTPCookie
   679  ```
   680  
   681  When persistent sessions are established, the persistence to a single backend should override the traffic splitting
   682  configuration.
   683  
   684  #### Traffic Splitting with two Backends and one with Weight 0
   685  
   686  Consider the scenario where a route has two path matches, but one of those paths involves traffic splitting with a
   687  backendRef that has a weight of 0, and additionally, a `SessionPersistencePolicy` is attached to the route:
   688  ```yaml
   689  kind: HTTPRoute
   690  metadata:
   691    name: split-route
   692  spec:
   693    rules:
   694    - matches:
   695      - path:
   696        value: /a
   697      backendRefs:
   698      - name: servicev1
   699    - matches:
   700      - path:
   701        value: /b
   702      backendRefs:
   703      - name: servicev1
   704        weight: 0
   705      - name: servicev2
   706        weight: 100
   707  ---
   708  kind: SessionPersistencePolicy
   709  metadata:
   710    name: spp-split-route
   711  spec:
   712    targetRef:
   713      kind: HTTPRoute
   714      name: split-route
   715    type: HTTPCookie
   716  ```
   717  
   718  A potentially unexpected situation occurs when:
   719  1. Curl to `/a` which establishes a persistent session with `servicev1`
   720  2. Curl to `/b` routes to `servicev1` due to route persistence despite `weight: 0` configuration
   721  
   722  In this scenario, implementations should give precedence to session persistence, regardless of the `weight`
   723  configuration.
   724  
   725  #### A Service's Selector is Dynamically Updated
   726  
   727  In Kubernetes, it's possible to modify the [selector](https://kubernetes.io/docs/concepts/services-networking/service/#services-in-kubernetes)
   728  of a service after the gateway has established persistent sessions with it.
   729  
   730  ```yaml
   731  kind: Service
   732  metadata:
   733    name: my-service
   734  spec:
   735    selector:
   736      app.kubernetes.io/name: MyApp # Service selector can change
   737  ```
   738  
   739  The expected behavior is that the gateway will retain existing persistent sessions, even if the pod is no longer
   740  selected, and establish new persistent sessions after a selector update. This use case is uncommon and may not be
   741  supported by some implementations due to their current designs.
   742  
   743  ### Open Questions
   744  
   745  - What happens when session persistence is broken because the backend is not up or healthy? If that's an error case, how should that be handled? Should the API dictate the http error code? Or should the API dictate fall back behavior?
   746  - What happens when session persistence causes traffic splitting scenarios to overload a backend?
   747  - Should we add status somewhere when a user gets in to a "risky" configuration with session persistence?
   748  - Should there be an API configuration field that specifies how already established sessions are handled?
   749  
   750  ## TODO
   751  The following are items that we intend to resolve before we consider this GEP implementable:
   752  
   753  - We need to identify and document requirements regarding session draining and migration. How do implementations drain established sessions during backend upgrades without disruption?
   754    - Do we need a "session draining timeout" as documented by [A55: xDS-Based Stateful Session Affinity for Proxyless gRPC](https://github.com/grpc/proposal/blob/master/A55-xds-stateful-session-affinity.md#background)
   755      defined in this API?
   756  - We need to document sessions with Java in greater detail. Java standardized the API and behavior of session persistence long ago and would be worth examining.
   757  - We need to add a small section on compliance regarding the browser and client relationship.
   758  - We need to finish enumerating all the edge cases in [Expected API Behavior](#expected-api-behavior) and identify
   759  potential scenarios where session persistence could break so an implementation can implement session persistence in a
   760  predicable way.
   761  
   762  ## Alternatives
   763  
   764  ### Alternate Session Persistence API
   765  
   766  Alternatively, the API for Session Persistence could define a loosely-typed list of attributes instead of strongly-typed
   767  attribute fields. This approach offers a more flexible specification, particularly when new attributes need to be
   768  introduced. However, loosely-typed lists may not be as user-friendly due to the lack of validation.
   769  
   770  ```go
   771  // HttpCookie defines a cookie to achieve session persistence.
   772  //
   773  // Support: Core
   774  type HttpCookie struct {
   775      // Name defines the cookie's name.
   776      //
   777      // Support: Core
   778      //
   779      // +kubebuilder:validation:MaxLength=4096
   780      Name String `json:"name,omitempty"`
   781  
   782      // CookieAttributes defines the cookie's attributes.
   783      //
   784      // Support: Core
   785      // +optional
   786      CookieAttributes []CookieAttribute `json:cookieAttributes`
   787  }
   788  
   789  // CookieAttribute defines the cookie's attributes.
   790  type CookieAttribute map[string][]string
   791  )
   792  ```
   793  
   794  The API could also be a mix of individual fields and listed attributes. More specifically, we could separate the key
   795  attributes with no value into a list. This approach is taken by [Haproxy Ingress](https://haproxy-ingress.github.io/docs/configuration/keys/#affinity)
   796  with their `session-cookie-keywords` field. This provides flexibility for simple boolean-typed attributes, while
   797  validating attributes that have values. However, this approach may be confusing to users as uses two different API
   798  patterns for cookie attributes.
   799  
   800  ```go
   801  // HttpCookie defines a cookie to achieve session persistence.
   802  //
   803  // Support: Core
   804  type HttpCookie struct {
   805      // Name defines the cookie's name.
   806      //
   807      // Support: Core
   808      //
   809      // +kubebuilder:validation:MaxLength=4096
   810      Name String `json:"name,omitempty"`
   811  
   812      // SameSite defines the cookie's SameSite attribute.
   813      //
   814      // Support: Extended
   815      //
   816      // +optional
   817      // +kubebuilder:validation:Enum=Strict;Lax;None
   818      SameSite SameSiteType `json:"sameSite,omitempty"`
   819  
   820      // Domain defines the cookie's Domain attribute.
   821      //
   822      // Support: Extended
   823      //
   824      // +optional
   825      // +kubebuilder:validation:MaxLength=4096
   826      Domain String `json:"domain,omitempty"`
   827  
   828      // CookieKeywords defines the cookie's attributes that have no value.
   829      //
   830      // Support: Extended
   831      // +optional
   832      CookieKeywords []CookieKeyword `json:cookieKeywords`
   833  }
   834  
   835  // CookieKeyword defines the cookie's attributes that have no value.
   836  type CookieKeyword string
   837  
   838  const (
   839      // CookieKeywordsHttpOnly specifies the HttpOnly cookie attribute.
   840      CookieKeywordsHttpOnly HttpOnlyMode = "HttpOnly"
   841      // CookieKeywordsSecure specifies the Secure cookie attribute.
   842      CookieKeywordsSecure HttpOnlyMode = "Secure"
   843  )
   844  ```
   845  
   846  Taking a different approach, this GEP could design a generic approach to configuring load balancing policy. Instead of
   847  only having a metaresource specifically for session persistence, a metaresource called `LoadBalancerPolicy` of which
   848  includes a field for session persistence along with other load balancer related configuration. It's important to note
   849  that persistent session takes priority over balance algorithm.
   850  
   851  The `LoadBalancerPolicy` design provides tighter coupling with other load balancing configuration which help reduce CRD
   852  proliferation, but may limit API flexibility as it forces coupling between concepts that may not be appropriate for
   853  current and future implementations.
   854  
   855  ```go
   856  // LoadBalancerPolicy provides a way to define load balancing rules
   857  // for a service.
   858  //
   859  // Support: Core
   860  type LoadBalancerPolicy struct {
   861      metav1.TypeMeta   `json:",inline"`
   862      metav1.ObjectMeta `json:"metadata,omitempty"`
   863  
   864      // Spec defines the desired state of LoadBalancerPolicy.
   865      Spec LoadBalancerPolicySpec `json:"spec"`
   866  
   867      // Status defines the current state of LoadBalancerPolicy.
   868      Status LoadBalancerPolicyStatus `json:"status,omitempty"`
   869  }
   870  
   871  // LoadBalancerPolicySpec defines the desired state of
   872  // LoadBalancerPolicy.
   873  // Note: there is no Override or Default policy configuration.
   874  type LoadBalancerPolicySpec struct {
   875      // TargetRef identifies an API object to apply policy to.
   876      // Services are the only valid API target references.
   877      TargetRef gatewayv1a2.PolicyTargetReference `json:"targetRef"`
   878  
   879      // SessionPersistence defines and configures session persistence.
   880      SessionPersistence *SessionPersistence `json:"sessionPersistence"`
   881  
   882      // BalanceAlgorithm defines and configures the load balancer
   883      // balancing algorithm.
   884      BalanceAlgorithm *BalanceAlgorithm `json:"balanceAlgorithm"`
   885  }
   886  
   887  // SessionPersistence defines and configures session persistence.
   888  //
   889  // Support: Core
   890  type SessionPersistence struct {
   891      // HttpCookie defines and configures a cookie to achieve
   892      // session persistence.
   893      //
   894      // Support: Core
   895      HttpCookie *HttpCookie `json:"httpCookie"`
   896  }
   897  ```
   898  
   899  ### Alternate Naming
   900  
   901  This GEP describes session persistence and session affinity as the idea of strong and weak connection persistence respectively. Other technologies use different names or define persistence and affinity differently:
   902  
   903  - Envoy defines [stateful sessions](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/http/stateful_session/cookie/v3/cookie.proto) as what we've defined as session persistence
   904  - Google Cloud defines [session affinity](https://cloud.google.com/run/docs/configuring/session-affinity) as what we've defined as session persistence
   905  - Nginx defines [session persistence](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#enabling-session-persistence) as what we've defined as both session persistence and affinity
   906  - Traefik defines [sticky sessions](https://doc.traefik.io/traefik/routing/services/#sticky-sessions) as what we've defined as session persistence
   907  - Apache httpd defines [sticky sessions or stickiness](https://httpd.apache.org/docs/2.4/mod/mod_proxy_balancer.html) as what we've defined as session persistence
   908  - Kubernetes defines [session affinity](https://kubernetes.io/docs/reference/networking/virtual-ips/#session-affinity) based on client IP hashing (same as our session affinity)
   909  
   910  Though session persistence is a ubiquitous name, session affinity is more inconsistently used. An alternate decision could be made to use a different name for session affinity based on the prevalence of other naming conventions.
   911  
   912  ## References
   913  
   914  - [LBPolicy](https://static.sched.com/hosted_files/kccnceu2023/c4/Autoscaling%20Elastic%20Kubernetes%20Infrastructure%20for%20Stateful%20Applications%20using%20Proxyless%20gRPC%20and%20Istio.pdf#page=25) (proposed extension for session persistence API)
   915  - [gRPC Stateful Session Affinity Proposal](https://github.com/grpc/proposal/blob/master/A55-xds-stateful-session-affinity.md) (info on session draining and session persistence in gRPC)
   916  - [Kube-Proxy Session Affinity](https://kubernetes.io/docs/reference/networking/virtual-ips/#session-affinity)
   917  - [GEP-713: Metaresources and PolicyAttachment](/geps/gep-713/)
   918  - [RFC6265](https://www.rfc-editor.org/rfc/rfc6265)
   919  - [Policy Attachment](https://gateway-api.sigs.k8s.io/reference/policy-attachment/#direct-policy-attachment)
   920  - [Envoy Session Persistence Design Doc](https://docs.google.com/document/d/1IU4b76AgOXijNa4sew1gfBfSiOMbZNiEt5Dhis8QpYg/edit#heading=h.sobqsca7i45e)
   921  - [Envoy Session Persistence Issue](https://github.com/envoyproxy/envoy/issues/16698)