feat: always set auth cookie (#8213)

* feat: always set auth cookie

Signed-off-by: m.nabokikh <maksim.nabokikh@flant.com>

* feat: Add annotation to always set auth cookie

* Add annotation
* Add global configmap key
* Provide unit tests and e2e tests
* Fix e2e documentation autogen script

Signed-off-by: m.nabokikh <maksim.nabokikh@flant.com>

* Regenerate e2e tests

Signed-off-by: m.nabokikh <maksim.nabokikh@flant.com>
This commit is contained in:
Maksim Nabokikh 2022-05-20 02:27:53 +04:00 committed by GitHub
parent 93af9f726a
commit 2c27e66cc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 501 additions and 537 deletions

View file

@ -313,9 +313,12 @@ var _ = framework.DescribeAnnotation("auth-*", func() {
})
})
ginkgo.It("retains cookie set by external authentication server", func() {
ginkgo.Context("cookie set by external authentication server", func() {
host := "auth-check-cookies"
var annotations map[string]string
var ing1, ing2 *networking.Ingress
cfg := `#
events {
worker_connections 1024;
@ -342,40 +345,81 @@ http {
location / {
return 200;
}
location /error {
return 503;
}
}
}
`
ginkgo.BeforeEach(func() {
f.NGINXWithConfigDeployment("http-cookie-with-error", cfg)
f.NGINXWithConfigDeployment(framework.HTTPBinService, cfg)
e, err := f.KubeClientSet.CoreV1().Endpoints(f.Namespace).Get(context.TODO(), "http-cookie-with-error", metav1.GetOptions{})
assert.Nil(ginkgo.GinkgoT(), err)
e, err := f.KubeClientSet.CoreV1().Endpoints(f.Namespace).Get(context.TODO(), framework.HTTPBinService, metav1.GetOptions{})
assert.Nil(ginkgo.GinkgoT(), err)
assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets), 1, "expected at least one endpoint")
assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets[0].Addresses), 1, "expected at least one address ready in the endpoint")
assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets), 1, "expected at least one endpoint")
assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets[0].Addresses), 1, "expected at least one address ready in the endpoint")
httpbinIP := e.Subsets[0].Addresses[0].IP
httpbinIP := e.Subsets[0].Addresses[0].IP
annotations = map[string]string{
"nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/cookies/set/alma/armud", httpbinIP),
"nginx.ingress.kubernetes.io/auth-signin": "http://$host/auth/start",
}
annotations := map[string]string{
"nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/cookies/set/alma/armud", httpbinIP),
"nginx.ingress.kubernetes.io/auth-signin": "http://$host/auth/start",
}
ing1 = framework.NewSingleIngress(host, "/", host, f.Namespace, "http-cookie-with-error", 80, annotations)
f.EnsureIngress(ing1)
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
f.EnsureIngress(ing)
ing2 = framework.NewSingleIngress(host+"-error", "/error", host, f.Namespace, "http-cookie-with-error", 80, annotations)
f.EnsureIngress(ing2)
f.WaitForNginxServer(host, func(server string) bool {
return strings.Contains(server, "server_name "+host)
})
f.WaitForNginxServer(host, func(server string) bool {
return strings.Contains(server, "server_name auth")
})
f.HTTPTestClient().
GET("/").
WithHeader("Host", host).
WithQuery("a", "b").
WithQuery("c", "d").
Expect().
Status(http.StatusOK).
Header("Set-Cookie").Contains("alma=armud")
ginkgo.It("user retains cookie by default", func() {
f.HTTPTestClient().
GET("/").
WithHeader("Host", host).
WithQuery("a", "b").
WithQuery("c", "d").
Expect().
Status(http.StatusOK).
Header("Set-Cookie").Contains("alma=armud")
})
ginkgo.It("user does not retain cookie if upstream returns error status code", func() {
f.HTTPTestClient().
GET("/error").
WithHeader("Host", host).
WithQuery("a", "b").
WithQuery("c", "d").
Expect().
Status(http.StatusServiceUnavailable).
Header("Set-Cookie").Contains("")
})
ginkgo.It("user with annotated ingress retains cookie if upstream returns error status code", func() {
annotations["nginx.ingress.kubernetes.io/auth-always-set-cookie"] = "true"
f.UpdateIngress(ing1)
f.UpdateIngress(ing2)
f.WaitForNginxServer(host, func(server string) bool {
return strings.Contains(server, "server_name "+host)
})
f.HTTPTestClient().
GET("/error").
WithHeader("Host", host).
WithQuery("a", "b").
WithQuery("c", "d").
Expect().
Status(http.StatusServiceUnavailable).
Header("Set-Cookie").Contains("alma=armud")
})
})
ginkgo.Context("when external authentication is configured", func() {

View file

@ -17,6 +17,7 @@ limitations under the License.
package settings
import (
"context"
"fmt"
"net/http"
"regexp"
@ -24,6 +25,7 @@ import (
"github.com/onsi/ginkgo"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
networking "k8s.io/api/networking/v1"
"k8s.io/ingress-nginx/test/e2e/framework"
@ -150,7 +152,7 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() {
Status(http.StatusOK)
})
ginkgo.It("should still return status code 200 after auth backend is deleted using cache ", func() {
ginkgo.It("should still return status code 200 after auth backend is deleted using cache", func() {
globalExternalAuthCacheKeySetting := "global-auth-cache-key"
globalExternalAuthCacheKey := "foo"
@ -259,4 +261,101 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() {
})
ginkgo.Context("cookie set by external authentication server", func() {
host := "global-external-auth-check-cookies"
var ing1, ing2 *networking.Ingress
cfg := `#
events {
worker_connections 1024;
multi_accept on;
}
http {
default_type 'text/plain';
client_max_body_size 0;
server {
access_log on;
access_log /dev/stdout;
listen 80;
location ~ ^/cookies/set/(?<key>.*)/(?<value>.*) {
content_by_lua_block {
ngx.header['Set-Cookie'] = {ngx.var.key.."="..ngx.var.value}
ngx.say("OK")
}
}
location / {
return 200;
}
location /error {
return 503;
}
}
}
`
ginkgo.BeforeEach(func() {
f.NGINXWithConfigDeployment("http-cookie-with-error", cfg)
e, err := f.KubeClientSet.CoreV1().Endpoints(f.Namespace).Get(context.TODO(), "http-cookie-with-error", metav1.GetOptions{})
assert.Nil(ginkgo.GinkgoT(), err)
assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets), 1, "expected at least one endpoint")
assert.GreaterOrEqual(ginkgo.GinkgoT(), len(e.Subsets[0].Addresses), 1, "expected at least one address ready in the endpoint")
httpbinIP := e.Subsets[0].Addresses[0].IP
f.UpdateNginxConfigMapData(globalExternalAuthURLSetting, fmt.Sprintf("http://%s/cookies/set/alma/armud", httpbinIP))
ing1 = framework.NewSingleIngress(host, "/", host, f.Namespace, "http-cookie-with-error", 80, nil)
f.EnsureIngress(ing1)
ing2 = framework.NewSingleIngress(host+"-error", "/error", host, f.Namespace, "http-cookie-with-error", 80, nil)
f.EnsureIngress(ing2)
f.WaitForNginxServer(host, func(server string) bool {
return strings.Contains(server, "server_name "+host)
})
})
ginkgo.It("user retains cookie by default", func() {
f.HTTPTestClient().
GET("/").
WithHeader("Host", host).
WithQuery("a", "b").
WithQuery("c", "d").
Expect().
Status(http.StatusOK).
Header("Set-Cookie").Contains("alma=armud")
})
ginkgo.It("user does not retain cookie if upstream returns error status code", func() {
f.HTTPTestClient().
GET("/error").
WithHeader("Host", host).
WithQuery("a", "b").
WithQuery("c", "d").
Expect().
Status(http.StatusServiceUnavailable).
Header("Set-Cookie").Contains("")
})
ginkgo.It("user with global-auth-always-set-cookie key in configmap retains cookie if upstream returns error status code", func() {
f.UpdateNginxConfigMapData("global-auth-always-set-cookie", "true")
f.HTTPTestClient().
GET("/error").
WithHeader("Host", host).
WithQuery("a", "b").
WithQuery("c", "d").
Expect().
Status(http.StatusServiceUnavailable).
Header("Set-Cookie").Contains("alma=armud")
})
})
})