Skip to content

Commit

Permalink
Fix forwarding of auth-response-headers to gRPC backends (kubernetes#…
Browse files Browse the repository at this point in the history
…7331)

* add e2e test for auth-response-headers annotation

* add e2e test for grpc with auth-response-headers

* fix forwarding of auth header to GRPC backends

* add test case for proxySetHeader(nil)
  • Loading branch information
kd7lxl committed Aug 9, 2021
1 parent f3d3ea5 commit 461e50d
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 22 deletions.
6 changes: 3 additions & 3 deletions internal/ingress/controller/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ func shouldApplyGlobalAuth(input interface{}, globalExternalAuthURL string) bool
return false
}

func buildAuthResponseHeaders(headers []string) []string {
func buildAuthResponseHeaders(proxySetHeader string, headers []string) []string {
res := []string{}

if len(headers) == 0 {
Expand All @@ -574,7 +574,7 @@ func buildAuthResponseHeaders(headers []string) []string {
hvar := strings.ToLower(h)
hvar = strings.NewReplacer("-", "_").Replace(hvar)
res = append(res, fmt.Sprintf("auth_request_set $authHeader%v $upstream_http_%v;", i, hvar))
res = append(res, fmt.Sprintf("proxy_set_header '%v' $authHeader%v;", h, i))
res = append(res, fmt.Sprintf("%s '%v' $authHeader%v;", proxySetHeader, h, i))
}
return res
}
Expand Down Expand Up @@ -668,7 +668,7 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string {
var xForwardedPrefix string

if len(location.XForwardedPrefix) > 0 {
xForwardedPrefix = fmt.Sprintf("proxy_set_header X-Forwarded-Prefix \"%s\";\n", location.XForwardedPrefix)
xForwardedPrefix = fmt.Sprintf("%s X-Forwarded-Prefix \"%s\";\n", proxySetHeader(location), location.XForwardedPrefix)
}

return fmt.Sprintf(`
Expand Down
51 changes: 34 additions & 17 deletions internal/ingress/controller/template/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ func TestBuildAuthResponseHeaders(t *testing.T) {
"proxy_set_header 'H-With-Caps-And-Dashes' $authHeader1;",
}

headers := buildAuthResponseHeaders(externalAuthResponseHeaders)
headers := buildAuthResponseHeaders(proxySetHeader(nil), externalAuthResponseHeaders)

if !reflect.DeepEqual(expected, headers) {
t.Errorf("Expected \n'%v'\nbut returned \n'%v'", expected, headers)
Expand Down Expand Up @@ -1182,23 +1182,40 @@ func TestBuildCustomErrorLocationsPerServer(t *testing.T) {
}

func TestProxySetHeader(t *testing.T) {
invalidType := &ingress.Ingress{}
expected := "proxy_set_header"
actual := proxySetHeader(invalidType)

if expected != actual {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
}

grpcBackend := &ingress.Location{
BackendProtocol: "GRPC",
tests := []struct {
name string
loc interface{}
expected string
}{
{
name: "nil",
loc: nil,
expected: "proxy_set_header",
},
{
name: "invalid type",
loc: &ingress.Ingress{},
expected: "proxy_set_header",
},
{
name: "http backend",
loc: &ingress.Location{},
expected: "proxy_set_header",
},
{
name: "gRPC backend",
loc: &ingress.Location{
BackendProtocol: "GRPC",
},
expected: "grpc_set_header",
},
}

expected = "grpc_set_header"
actual = proxySetHeader(grpcBackend)

if expected != actual {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := proxySetHeader(tt.loc); got != tt.expected {
t.Errorf("proxySetHeader() = %v, expected %v", got, tt.expected)
}
})
}
}

Expand Down
4 changes: 2 additions & 2 deletions rootfs/etc/nginx/template/nginx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -1178,7 +1178,7 @@ stream {
auth_request {{ $authPath }};
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
{{- range $line := buildAuthResponseHeaders $externalAuth.ResponseHeaders }}
{{- range $line := buildAuthResponseHeaders $proxySetHeader $externalAuth.ResponseHeaders }}
{{ $line }}
{{- end }}
{{ end }}
Expand All @@ -1196,7 +1196,7 @@ stream {
auth_digest {{ $location.BasicDigestAuth.Realm | quote }};
auth_digest_user_file {{ $location.BasicDigestAuth.File }};
{{ end }}
proxy_set_header Authorization "";
{{ $proxySetHeader }} Authorization "";
{{ end }}
{{ end }}

Expand Down
23 changes: 23 additions & 0 deletions test/e2e/annotations/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,29 @@ http {
Expect().
Status(http.StatusOK)
})

ginkgo.It("should overwrite Foo header with auth response", func() {
var (
rewriteHeader = "Foo"
rewriteVal = "bar"
)
annotations["nginx.ingress.kubernetes.io/auth-response-headers"] = rewriteHeader
f.UpdateIngress(ing)

f.WaitForNginxServer(host, func(server string) bool {
return strings.Contains(server, fmt.Sprintf("proxy_set_header '%s' $authHeader0;", rewriteHeader))
})

f.HTTPTestClient().
GET("/").
WithHeader("Host", host).
WithHeader(rewriteHeader, rewriteVal).
WithBasicAuth("user", "password").
Expect().
Status(http.StatusOK).
Body().
NotContainsFold(fmt.Sprintf("%s=%s", rewriteHeader, rewriteVal))
})
})

ginkgo.Context("when external authentication is configured with a custom redirect param", func() {
Expand Down
74 changes: 74 additions & 0 deletions test/e2e/annotations/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
core "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -121,6 +122,79 @@ var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() {
assert.Equal(ginkgo.GinkgoT(), metadata["content-type"].Values[0], "application/grpc")
})

ginkgo.It("authorization metadata should be overwritten by external auth response headers", func() {
f.NewGRPCBinDeployment()
f.NewHttpbinDeployment()

host := "echo"

svc := &core.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "grpcbin-test",
Namespace: f.Namespace,
},
Spec: corev1.ServiceSpec{
ExternalName: fmt.Sprintf("grpcbin.%v.svc.cluster.local", f.Namespace),
Type: corev1.ServiceTypeExternalName,
Ports: []corev1.ServicePort{
{
Name: host,
Port: 9000,
TargetPort: intstr.FromInt(9000),
Protocol: "TCP",
},
},
},
}
f.EnsureService(svc)

err := framework.WaitForEndpoints(f.KubeClientSet, framework.DefaultTimeout, framework.HTTPBinService, f.Namespace, 1)
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")

httpbinIP := e.Subsets[0].Addresses[0].IP

annotations := map[string]string{
"nginx.ingress.kubernetes.io/auth-url": fmt.Sprintf("http://%s/response-headers?authorization=foo", httpbinIP),
"nginx.ingress.kubernetes.io/auth-response-headers": "Authorization",
"nginx.ingress.kubernetes.io/backend-protocol": "GRPC",
}

ing := framework.NewSingleIngressWithTLS(host, "/", host, []string{host}, f.Namespace, "grpcbin-test", 9000, annotations)

f.EnsureIngress(ing)

f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, "grpc_pass grpc://upstream_balancer;")
})

conn, _ := grpc.Dial(f.GetNginxIP()+":443",
grpc.WithTransportCredentials(
credentials.NewTLS(&tls.Config{
ServerName: "echo",
InsecureSkipVerify: true,
}),
),
)
defer conn.Close()

client := pb.NewGRPCBinClient(conn)
ctx := metadata.AppendToOutgoingContext(context.Background(),
"authorization", "bar")

res, err := client.HeadersUnary(ctx, &pb.EmptyMessage{})
assert.Nil(ginkgo.GinkgoT(), err)

metadata := res.GetMetadata()
assert.Equal(ginkgo.GinkgoT(), "foo", metadata["authorization"].Values[0])
})

ginkgo.It("should return OK for service with backend protocol GRPCS", func() {
f.NewGRPCBinDeployment()

Expand Down

0 comments on commit 461e50d

Please sign in to comment.