Add annotation for setting sticky cookie domain (#9088)
This adds the new annotation `nginx.ingress.kubernetes.io/session-cookie-domain` for setting the cookie `Domain` attribute of the sticky cookie. Signed-off-by: Matthias Neugebauer <mtneug@mailbox.org> Signed-off-by: Matthias Neugebauer <mtneug@mailbox.org>
This commit is contained in:
parent
077c0414aa
commit
26fe69cb47
9 changed files with 74 additions and 1 deletions
|
@ -14,6 +14,7 @@ Session affinity can be configured using the following annotations:
|
|||
|nginx.ingress.kubernetes.io/session-cookie-name|Name of the cookie that will be created|string (defaults to `INGRESSCOOKIE`)|
|
||||
|nginx.ingress.kubernetes.io/session-cookie-secure|Set the cookie as secure regardless the protocol of the incoming request|`"true"` or `"false"`|
|
||||
|nginx.ingress.kubernetes.io/session-cookie-path|Path that will be set on the cookie (required if your [Ingress paths][ingress-paths] use regular expressions)|string (defaults to the currently [matched path][ingress-paths])|
|
||||
|nginx.ingress.kubernetes.io/session-cookie-domain|Domain that will be set on the cookie|string|
|
||||
|nginx.ingress.kubernetes.io/session-cookie-samesite|`SameSite` attribute to apply to the cookie|Browser accepted values are `None`, `Lax`, and `Strict`|
|
||||
|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"`
|
||||
|nginx.ingress.kubernetes.io/session-cookie-max-age|Time until the cookie expires, corresponds to the `Max-Age` cookie directive|number of seconds|
|
||||
|
|
|
@ -98,6 +98,7 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz
|
|||
|[nginx.ingress.kubernetes.io/service-upstream](#service-upstream)|"true" or "false"|
|
||||
|[nginx.ingress.kubernetes.io/session-cookie-name](#cookie-affinity)|string|
|
||||
|[nginx.ingress.kubernetes.io/session-cookie-path](#cookie-affinity)|string|
|
||||
|[nginx.ingress.kubernetes.io/session-cookie-domain](#cookie-affinity)|string|
|
||||
|[nginx.ingress.kubernetes.io/session-cookie-change-on-failure](#cookie-affinity)|"true" or "false"|
|
||||
|[nginx.ingress.kubernetes.io/session-cookie-samesite](#cookie-affinity)|string|
|
||||
|[nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none](#cookie-affinity)|"true" or "false"|
|
||||
|
@ -189,6 +190,8 @@ If you use the ``cookie`` affinity type you can also specify the name of the coo
|
|||
|
||||
The NGINX annotation `nginx.ingress.kubernetes.io/session-cookie-path` defines the path that will be set on the cookie. This is optional unless the annotation `nginx.ingress.kubernetes.io/use-regex` is set to true; Session cookie paths do not support regex.
|
||||
|
||||
Use `nginx.ingress.kubernetes.io/session-cookie-domain` to set the `Domain` attribute of the sticky cookie.
|
||||
|
||||
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"`.
|
||||
|
||||
### Authentication
|
||||
|
|
|
@ -52,6 +52,9 @@ const (
|
|||
// This is used to control the cookie path when use-regex is set to true
|
||||
annotationAffinityCookiePath = "session-cookie-path"
|
||||
|
||||
// This is used to control the cookie Domain
|
||||
annotationAffinityCookieDomain = "session-cookie-domain"
|
||||
|
||||
// This is used to control the SameSite attribute of the cookie
|
||||
annotationAffinityCookieSameSite = "session-cookie-samesite"
|
||||
|
||||
|
@ -87,6 +90,8 @@ type Cookie struct {
|
|||
MaxAge string `json:"maxage"`
|
||||
// The path that a cookie will be set on
|
||||
Path string `json:"path"`
|
||||
// The domain that a cookie will be set on
|
||||
Domain string `json:"domain"`
|
||||
// Flag that allows cookie regeneration on request failure
|
||||
ChangeOnFailure bool `json:"changeonfailure"`
|
||||
// Secure flag to be set
|
||||
|
@ -127,6 +132,11 @@ func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie {
|
|||
klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookiePath)
|
||||
}
|
||||
|
||||
cookie.Domain, err = parser.GetStringAnnotation(annotationAffinityCookieDomain, ing)
|
||||
if err != nil {
|
||||
klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieDomain)
|
||||
}
|
||||
|
||||
cookie.SameSite, err = parser.GetStringAnnotation(annotationAffinityCookieSameSite, ing)
|
||||
if err != nil {
|
||||
klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieSameSite)
|
||||
|
|
|
@ -78,6 +78,7 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
|
|||
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieExpires)] = "4500"
|
||||
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieMaxAge)] = "3000"
|
||||
data[parser.GetAnnotationWithPrefix(annotationAffinityCookiePath)] = "/foo"
|
||||
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieDomain)] = "foo.bar"
|
||||
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieChangeOnFailure)] = "true"
|
||||
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieSecure)] = "true"
|
||||
ing.SetAnnotations(data)
|
||||
|
@ -112,6 +113,10 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
|
|||
t.Errorf("expected /foo as session-cookie-path but returned %v", nginxAffinity.Cookie.Path)
|
||||
}
|
||||
|
||||
if nginxAffinity.Cookie.Domain != "foo.bar" {
|
||||
t.Errorf("expected foo.bar as session-cookie-domain but returned %v", nginxAffinity.Cookie.Domain)
|
||||
}
|
||||
|
||||
if !nginxAffinity.Cookie.ChangeOnFailure {
|
||||
t.Errorf("expected change of failure parameter set to true but returned %v", nginxAffinity.Cookie.ChangeOnFailure)
|
||||
}
|
||||
|
|
|
@ -767,6 +767,7 @@ func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*in
|
|||
ups.SessionAffinity.CookieSessionAffinity.MaxAge = anns.SessionAffinity.Cookie.MaxAge
|
||||
ups.SessionAffinity.CookieSessionAffinity.Secure = anns.SessionAffinity.Cookie.Secure
|
||||
ups.SessionAffinity.CookieSessionAffinity.Path = cookiePath
|
||||
ups.SessionAffinity.CookieSessionAffinity.Domain = anns.SessionAffinity.Cookie.Domain
|
||||
ups.SessionAffinity.CookieSessionAffinity.SameSite = anns.SessionAffinity.Cookie.SameSite
|
||||
ups.SessionAffinity.CookieSessionAffinity.ConditionalSameSiteNone = anns.SessionAffinity.Cookie.ConditionalSameSiteNone
|
||||
ups.SessionAffinity.CookieSessionAffinity.ChangeOnFailure = anns.SessionAffinity.Cookie.ChangeOnFailure
|
||||
|
|
|
@ -159,6 +159,7 @@ type CookieSessionAffinity struct {
|
|||
Locations map[string][]string `json:"locations,omitempty"`
|
||||
Secure bool `json:"secure,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Domain string `json:"domain,omitempty"`
|
||||
SameSite string `json:"samesite,omitempty"`
|
||||
ConditionalSameSiteNone bool `json:"conditional_samesite_none,omitempty"`
|
||||
ChangeOnFailure bool `json:"change_on_failure,omitempty"`
|
||||
|
|
|
@ -173,6 +173,9 @@ func (csa1 *CookieSessionAffinity) Equal(csa2 *CookieSessionAffinity) bool {
|
|||
if csa1.Path != csa2.Path {
|
||||
return false
|
||||
}
|
||||
if csa1.Domain != csa2.Domain {
|
||||
return false
|
||||
}
|
||||
if csa1.Expires != csa2.Expires {
|
||||
return false
|
||||
}
|
||||
|
@ -192,7 +195,7 @@ func (csa1 *CookieSessionAffinity) Equal(csa2 *CookieSessionAffinity) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
//Equal checks the equality between UpstreamByConfig types
|
||||
// Equal checks the equality between UpstreamByConfig types
|
||||
func (u1 *UpstreamHashByConfig) Equal(u2 *UpstreamHashByConfig) bool {
|
||||
if u1 == u2 {
|
||||
return true
|
||||
|
|
|
@ -110,6 +110,10 @@ function _M.set_cookie(self, value)
|
|||
cookie_data.max_age = tonumber(self.cookie_session_affinity.maxage)
|
||||
end
|
||||
|
||||
if self.cookie_session_affinity.domain and self.cookie_session_affinity.domain ~= "" then
|
||||
cookie_data.domain = self.cookie_session_affinity.domain
|
||||
end
|
||||
|
||||
local ok
|
||||
ok, err = cookie:set(cookie_data)
|
||||
if not ok then
|
||||
|
|
|
@ -222,6 +222,51 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() {
|
|||
Header("Set-Cookie").Contains(fmt.Sprintf("Expires=%s", expected)).Contains("Max-Age=259200")
|
||||
})
|
||||
|
||||
ginkgo.It("should set cookie with domain", func() {
|
||||
host := "cookiedomain.foo.com"
|
||||
annotations := make(map[string]string)
|
||||
annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie"
|
||||
annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "DomainCookie"
|
||||
annotations["nginx.ingress.kubernetes.io/session-cookie-domain"] = "foo.bar"
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Header("Set-Cookie").Contains("Domain=foo.bar")
|
||||
})
|
||||
|
||||
ginkgo.It("should not set cookie without domain annotation", func() {
|
||||
host := "cookienodomain.foo.com"
|
||||
annotations := make(map[string]string)
|
||||
annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie"
|
||||
annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "NoDomainCookie"
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
|
||||
})
|
||||
|
||||
f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Header("Set-Cookie").NotContains("; Domain")
|
||||
})
|
||||
|
||||
ginkgo.It("should work with use-regex annotation and session-cookie-path", func() {
|
||||
host := "useregex.foo.com"
|
||||
annotations := make(map[string]string)
|
||||
|
|
Loading…
Reference in a new issue