Skip to content

Commit c1d3a92

Browse files
committed
feature(cookie-affinity): Add partitioned cookie support (#10428)
1 parent ecb38de commit c1d3a92

File tree

11 files changed

+134
-4
lines changed

11 files changed

+134
-4
lines changed

docs/examples/affinity/cookie/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Session affinity can be configured using the following annotations:
1717
|nginx.ingress.kubernetes.io/session-cookie-domain|Domain that will be set on the cookie|string|
1818
|nginx.ingress.kubernetes.io/session-cookie-samesite|`SameSite` attribute to apply to the cookie|Browser accepted values are `None`, `Lax`, and `Strict`|
1919
|nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none|Will omit `SameSite=None` attribute for older browsers which reject the more-recently defined `SameSite=None` value|`"true"` or `"false"`
20+
|nginx.ingress.kubernetes.io/session-cookie-partitioned|Will set `Partitioned` attribute on the cookie|`"true"` or `"false"` (defaults to false)|
2021
|nginx.ingress.kubernetes.io/session-cookie-max-age|Time until the cookie expires, corresponds to the `Max-Age` cookie directive|number of seconds|
2122
|nginx.ingress.kubernetes.io/session-cookie-expires|Legacy version of the previous annotation for compatibility with older browsers, generates an `Expires` cookie directive by adding the seconds to the current date|number of seconds|
2223
|nginx.ingress.kubernetes.io/session-cookie-change-on-failure|When set to `false` nginx ingress will send request to upstream pointed by sticky cookie even if previous attempt failed. When set to `true` and previous attempt failed, sticky cookie will be changed to point to another upstream.|`true` or `false` (defaults to `false`)|
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
apiVersion: networking.k8s.io/v1
2+
kind: Ingress
3+
metadata:
4+
name: cookie-partitioned
5+
annotations:
6+
nginx.ingress.kubernetes.io/affinity: "cookie"
7+
nginx.ingress.kubernetes.io/session-cookie-name: "PARTITIONEDCOOKIENAME"
8+
nginx.ingress.kubernetes.io/session-cookie-secure: "true"
9+
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
10+
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
11+
nginx.ingress.kubernetes.io/session-cookie-partitioned: "true"
12+
spec:
13+
ingressClassName: nginx
14+
rules:
15+
- host: stickyingress-partitioned.example.com
16+
http:
17+
paths:
18+
- path: /
19+
pathType: Prefix
20+
backend:
21+
service:
22+
name: http-svc
23+
port:
24+
number: 80
25+
---
26+
apiVersion: networking.k8s.io/v1
27+
kind: Ingress
28+
metadata:
29+
name: cookie-partitioned-false
30+
annotations:
31+
nginx.ingress.kubernetes.io/affinity: "cookie"
32+
nginx.ingress.kubernetes.io/session-cookie-name: "PARTITIONEDFALSECOOKIENAME"
33+
nginx.ingress.kubernetes.io/session-cookie-secure: "true"
34+
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
35+
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
36+
nginx.ingress.kubernetes.io/session-cookie-partitioned: "false"
37+
spec:
38+
ingressClassName: nginx
39+
rules:
40+
- host: stickyingress-partitioned-false.example.com
41+
http:
42+
paths:
43+
- path: /
44+
pathType: Prefix
45+
backend:
46+
service:
47+
name: http-svc
48+
port:
49+
number: 80

docs/user-guide/nginx-configuration/annotations.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz
104104
|[nginx.ingress.kubernetes.io/session-cookie-path](#cookie-affinity)|string|
105105
|[nginx.ingress.kubernetes.io/session-cookie-samesite](#cookie-affinity)|string|"None", "Lax" or "Strict"|
106106
|[nginx.ingress.kubernetes.io/session-cookie-secure](#cookie-affinity)|string|
107+
|[nginx.ingress.kubernetes.io/session-cookie-partitioned](#cookie-affinity)|"true" or "false"|
107108
|[nginx.ingress.kubernetes.io/ssl-redirect](#server-side-https-enforcement-through-redirect)|"true" or "false"|
108109
|[nginx.ingress.kubernetes.io/ssl-passthrough](#ssl-passthrough)|"true" or "false"|
109110
|[nginx.ingress.kubernetes.io/stream-snippet](#stream-snippet)|string|
@@ -192,6 +193,8 @@ Use `nginx.ingress.kubernetes.io/session-cookie-domain` to set the `Domain` attr
192193

193194
Use `nginx.ingress.kubernetes.io/session-cookie-samesite` to apply a `SameSite` attribute to the sticky cookie. Browser accepted values are `None`, `Lax`, and `Strict`. Some browsers reject cookies with `SameSite=None`, including those created before the `SameSite=None` specification (e.g. Chrome 5X). Other browsers mistakenly treat `SameSite=None` cookies as `SameSite=Strict` (e.g. Safari running on OSX 14). To omit `SameSite=None` from browsers with these incompatibilities, add the annotation `nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none: "true"`.
194195

196+
Use `nginx.ingress.kubernetes.io/session-cookie-partitioned` to apply a `Partitioned` attribute to the sticky cookie.
197+
195198
Use `nginx.ingress.kubernetes.io/session-cookie-expires` to control the cookie expires, its value is a number of seconds until the cookie expires.
196199

197200
Use `nginx.ingress.kubernetes.io/session-cookie-path` to control the cookie path when use-regex is set to true.

images/nginx/rootfs/build.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ export LUA_RESTY_CACHE=99e7578465b40f36f596d099b82eab404f2b42ed
7171
# Check for recent changes: https://github.com/openresty/lua-resty-core/compare/v0.1.27...master
7272
export LUA_RESTY_CORE=v0.1.28
7373

74-
# Check for recent changes: https://github.com/cloudflare/lua-resty-cookie/compare/f418d77082eaef48331302e84330488fdc810ef4...master
75-
export LUA_RESTY_COOKIE_VERSION=f418d77082eaef48331302e84330488fdc810ef4
74+
# Check for recent changes: https://github.com/utix/lua-resty-cookie/compare/9533f47...master
75+
export LUA_RESTY_COOKIE_VERSION=9533f479371663107b515590fc9daf00d61ebf11
7676

7777
# Check for recent changes: https://github.com/openresty/lua-resty-dns/compare/8bb53516e2933e61c317db740a9b7c2048847c2f...master
7878
export LUA_RESTY_DNS=8bb53516e2933e61c317db740a9b7c2048847c2f
@@ -249,8 +249,8 @@ get_src 39baab9e2b31cc48cecf896cea40ef6e80559054fd8a6e440cc804a858ea84d4 \
249249
get_src a77b9de160d81712f2f442e1de8b78a5a7ef0d08f13430ff619f79235db974d4 \
250250
"https://github.com/openresty/lua-cjson/archive/$LUA_CJSON_VERSION.tar.gz" "lua-cjson"
251251

252-
get_src 5ed48c36231e2622b001308622d46a0077525ac2f751e8cc0c9905914254baa4 \
253-
"https://github.com/cloudflare/lua-resty-cookie/archive/$LUA_RESTY_COOKIE_VERSION.tar.gz" "lua-resty-cookie"
252+
get_src a404c790553617424d743b82a9f01feccd0d2930b306b370c665ca3b7c09ccb6 \
253+
"https://github.com/utix/lua-resty-cookie/archive/$LUA_RESTY_COOKIE_VERSION.tar.gz" "lua-resty-cookie"
254254

255255
get_src 573184006b98ccee2594b0d134fa4d05e5d2afd5141cbad315051ccf7e9b6403 \
256256
"https://github.com/openresty/lua-resty-lrucache/archive/$LUA_RESTY_CACHE.tar.gz" "lua-resty-lrucache"

internal/ingress/annotations/sessionaffinity/main.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ const (
6161
// This is used to control whether SameSite=None should be conditionally applied based on the User-Agent
6262
annotationAffinityCookieConditionalSameSiteNone = "session-cookie-conditional-samesite-none"
6363

64+
// This is used to set the Partitioned flag on the cookie
65+
annotationAffinityCookiePartitioned = "session-cookie-partitioned"
66+
6467
// This is used to control the cookie change after request failure
6568
annotationAffinityCookieChangeOnFailure = "session-cookie-change-on-failure"
6669

@@ -141,6 +144,12 @@ var sessionAffinityAnnotations = parser.Annotation{
141144
Risk: parser.AnnotationRiskLow,
142145
Documentation: `This annotation is used to omit SameSite=None from browsers with SameSite attribute incompatibilities`,
143146
},
147+
annotationAffinityCookiePartitioned: {
148+
Validator: parser.ValidateBool,
149+
Scope: parser.AnnotationScopeIngress,
150+
Risk: parser.AnnotationRiskLow,
151+
Documentation: `This annotation sets the cookie as Partitioned`,
152+
},
144153
annotationAffinityCookieChangeOnFailure: {
145154
Validator: parser.ValidateBool,
146155
Scope: parser.AnnotationScopeIngress,
@@ -184,6 +193,8 @@ type Cookie struct {
184193
SameSite string `json:"samesite"`
185194
// Flag that conditionally applies SameSite=None attribute on cookie if user agent accepts it.
186195
ConditionalSameSiteNone bool `json:"conditional-samesite-none"`
196+
// Partitioned flag to be set
197+
Partitioned bool `json:"partitioned"`
187198
}
188199

189200
type affinity struct {
@@ -241,6 +252,11 @@ func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie {
241252
klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieConditionalSameSiteNone)
242253
}
243254

255+
cookie.Partitioned, err = parser.GetBoolAnnotation(annotationAffinityCookiePartitioned, ing, a.annotationConfig.Annotations)
256+
if err != nil {
257+
klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookiePartitioned)
258+
}
259+
244260
cookie.ChangeOnFailure, err = parser.GetBoolAnnotation(annotationAffinityCookieChangeOnFailure, ing, a.annotationConfig.Annotations)
245261
if err != nil {
246262
klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieChangeOnFailure)

internal/ingress/annotations/sessionaffinity/main_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
8282
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieSameSite)] = "Strict"
8383
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieChangeOnFailure)] = "true"
8484
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieSecure)] = "true"
85+
data[parser.GetAnnotationWithPrefix(annotationAffinityCookiePartitioned)] = "true"
8586
ing.SetAnnotations(data)
8687

8788
affin, err := NewParser(&resolver.Mock{}).Parse(ing)
@@ -133,4 +134,8 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
133134
if !nginxAffinity.Cookie.Secure {
134135
t.Errorf("expected secure parameter set to true but returned %v", nginxAffinity.Cookie.Secure)
135136
}
137+
138+
if !nginxAffinity.Cookie.Partitioned {
139+
t.Errorf("expected partitioned parameter set to true but returned %v", nginxAffinity.Cookie.Partitioned)
140+
}
136141
}

internal/ingress/controller/controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,7 @@ func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*in
868868
ups.SessionAffinity.CookieSessionAffinity.Domain = anns.SessionAffinity.Cookie.Domain
869869
ups.SessionAffinity.CookieSessionAffinity.SameSite = anns.SessionAffinity.Cookie.SameSite
870870
ups.SessionAffinity.CookieSessionAffinity.ConditionalSameSiteNone = anns.SessionAffinity.Cookie.ConditionalSameSiteNone
871+
ups.SessionAffinity.CookieSessionAffinity.Partitioned = anns.SessionAffinity.Cookie.Partitioned
871872
ups.SessionAffinity.CookieSessionAffinity.ChangeOnFailure = anns.SessionAffinity.Cookie.ChangeOnFailure
872873

873874
locs := ups.SessionAffinity.CookieSessionAffinity.Locations

pkg/apis/ingress/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ type CookieSessionAffinity struct {
162162
Domain string `json:"domain,omitempty"`
163163
SameSite string `json:"samesite,omitempty"`
164164
ConditionalSameSiteNone bool `json:"conditional_samesite_none,omitempty"`
165+
Partitioned bool `json:"partitioned,omitempty"`
165166
ChangeOnFailure bool `json:"change_on_failure,omitempty"`
166167
}
167168

pkg/apis/ingress/types_equals.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ func (csa1 *CookieSessionAffinity) Equal(csa2 *CookieSessionAffinity) bool {
187187
if csa1.ConditionalSameSiteNone != csa2.ConditionalSameSiteNone {
188188
return false
189189
}
190+
if csa1.Partitioned != csa2.Partitioned {
191+
return false
192+
}
190193

191194
return true
192195
}

rootfs/etc/nginx/lua/balancer/sticky.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ function _M.set_cookie(self, value)
9999
httponly = true,
100100
samesite = cookie_samesite,
101101
secure = cookie_secure,
102+
partitioned = self.cookie_session_affinity.partitioned,
102103
}
103104

104105
if self.cookie_session_affinity.expires and self.cookie_session_affinity.expires ~= "" then

0 commit comments

Comments
 (0)