Add support for grpc_set_header

This commit is contained in:
Manuel de Brito Fontes 2018-05-17 08:35:11 -04:00
parent 7ef8a0a775
commit ff3e182350
13 changed files with 248 additions and 46 deletions

View file

@ -426,7 +426,6 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
glog.V(3).Infof("replacing ingress rule %v/%v location %v upstream %v (%v)", ing.Namespace, ing.Name, loc.Path, ups.Name, loc.Backend)
loc.Backend = ups.Name
loc.IsDefBackend = false
loc.Backend = ups.Name
loc.Port = ups.Port
loc.Service = ups.Service
loc.Ingress = ing
@ -521,7 +520,6 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
if upstream.Name == location.Backend {
if len(upstream.Endpoints) == 0 {
glog.V(3).Infof("upstream %v does not have any active endpoints.", upstream.Name)
location.Backend = ""
// check if the location contains endpoints and a custom default backend
if location.DefaultBackend != nil {

View file

@ -152,6 +152,7 @@ var (
"buildAuthSignURL": buildAuthSignURL,
"buildOpentracingLoad": buildOpentracingLoad,
"buildOpentracing": buildOpentracing,
"proxySetHeader": proxySetHeader,
}
)
@ -895,3 +896,17 @@ func buildOpentracing(input interface{}) string {
buf.WriteString("\r\n")
return buf.String()
}
func proxySetHeader(loc interface{}) string {
location, ok := loc.(*ingress.Location)
if !ok {
glog.Errorf("expected a '*ingress.Location' type but %T was returned", loc)
return "proxy_set_header"
}
if location.GRPC {
return "grpc_set_header"
}
return "proxy_set_header"
}

View file

@ -763,6 +763,7 @@ stream {
{{ range $location := $server.Locations }}
{{ $path := buildLocation $location }}
{{ $proxySetHeader := proxySetHeader $location }}
{{ $authPath := buildAuthLocation $location }}
{{ if not (empty $location.Rewrite.AppRoot)}}
@ -992,54 +993,54 @@ stream {
{{/* By default use vhost as Host to upstream, but allow overrides */}}
{{ if not (empty $location.UpstreamVhost) }}
proxy_set_header Host "{{ $location.UpstreamVhost }}";
{{ $proxySetHeader }} Host "{{ $location.UpstreamVhost }}";
{{ else }}
proxy_set_header Host $best_http_host;
{{ $proxySetHeader }} Host $best_http_host;
{{ end }}
# Pass the extracted client certificate to the backend
{{ if not (empty $server.CertificateAuth.CAFileName) }}
{{ if $server.CertificateAuth.PassCertToUpstream }}
proxy_set_header ssl-client-cert $ssl_client_escaped_cert;
{{ $proxySetHeader }} ssl-client-cert $ssl_client_escaped_cert;
{{ end }}
proxy_set_header ssl-client-verify $ssl_client_verify;
proxy_set_header ssl-client-subject-dn $ssl_client_s_dn;
proxy_set_header ssl-client-issuer-dn $ssl_client_i_dn;
{{ $proxySetHeader }} ssl-client-verify $ssl_client_verify;
{{ $proxySetHeader }} ssl-client-subject-dn $ssl_client_s_dn;
{{ $proxySetHeader }} ssl-client-issuer-dn $ssl_client_i_dn;
{{ end }}
# Allow websocket connections
proxy_set_header Upgrade $http_upgrade;
{{ $proxySetHeader }} Upgrade $http_upgrade;
{{ if $location.Connection.Enabled}}
proxy_set_header Connection {{ $location.Connection.Header }};
{{ $proxySetHeader }} Connection {{ $location.Connection.Header }};
{{ else }}
proxy_set_header Connection $connection_upgrade;
{{ $proxySetHeader }} Connection $connection_upgrade;
{{ end }}
proxy_set_header X-Request-ID $req_id;
proxy_set_header X-Real-IP $the_real_ip;
{{ $proxySetHeader }} X-Request-ID $req_id;
{{ $proxySetHeader }} X-Real-IP $the_real_ip;
{{ if $all.Cfg.ComputeFullForwardedFor }}
proxy_set_header X-Forwarded-For $full_x_forwarded_for;
{{ $proxySetHeader }} X-Forwarded-For $full_x_forwarded_for;
{{ else }}
proxy_set_header X-Forwarded-For $the_real_ip;
{{ $proxySetHeader }} X-Forwarded-For $the_real_ip;
{{ end }}
proxy_set_header X-Forwarded-Host $best_http_host;
proxy_set_header X-Forwarded-Port $pass_port;
proxy_set_header X-Forwarded-Proto $pass_access_scheme;
{{ $proxySetHeader }} X-Forwarded-Host $best_http_host;
{{ $proxySetHeader }} X-Forwarded-Port $pass_port;
{{ $proxySetHeader }} X-Forwarded-Proto $pass_access_scheme;
{{ if $all.Cfg.ProxyAddOriginalUriHeader }}
proxy_set_header X-Original-URI $request_uri;
{{ $proxySetHeader }} X-Original-URI $request_uri;
{{ end }}
proxy_set_header X-Scheme $pass_access_scheme;
{{ $proxySetHeader }} X-Scheme $pass_access_scheme;
# Pass the original X-Forwarded-For
proxy_set_header X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }};
{{ $proxySetHeader }} X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }};
# mitigate HTTPoxy Vulnerability
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
proxy_set_header Proxy "";
{{ $proxySetHeader }} Proxy "";
# Custom headers to proxied server
{{ range $k, $v := $all.ProxySetHeaders }}
proxy_set_header {{ $k }} "{{ $v }}";
{{ $proxySetHeader }} {{ $k }} "{{ $v }}";
{{ end }}
proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s;
@ -1062,7 +1063,7 @@ stream {
{{/* rewrite only works if the content is not compressed */}}
{{ if $location.Rewrite.AddBaseURL }}
proxy_set_header Accept-Encoding "";
{{ $proxySetHeader }} Accept-Encoding "";
{{ end }}
{{/* Add any additional configuration defined */}}

View file

@ -45,7 +45,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Alias", func() {
It("should return status code 200 when no authentication is configured", func() {
host := "auth"
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, nil))
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, nil))
Expect(err).NotTo(HaveOccurred())
Expect(bi).NotTo(BeNil())
@ -69,7 +69,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Alias", func() {
It("should return status code 503 when authentication is configured with an invalid secret", func() {
host := "auth"
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, nil))
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, nil))
Expect(err).NotTo(HaveOccurred())
Expect(bi).NotTo(BeNil())
@ -106,7 +106,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Alias", func() {
Expect(s).NotTo(BeNil())
Expect(s.ObjectMeta).NotTo(BeNil())
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, nil))
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, nil))
Expect(err).NotTo(HaveOccurred())
Expect(bi).NotTo(BeNil())
@ -143,7 +143,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Alias", func() {
Expect(s).NotTo(BeNil())
Expect(s.ObjectMeta).NotTo(BeNil())
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, nil))
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, nil))
Expect(err).NotTo(HaveOccurred())
Expect(bi).NotTo(BeNil())
@ -181,7 +181,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Alias", func() {
Expect(s).NotTo(BeNil())
Expect(s.ObjectMeta).NotTo(BeNil())
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, nil))
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, nil))
Expect(err).NotTo(HaveOccurred())
Expect(bi).NotTo(BeNil())
@ -230,7 +230,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Alias", func() {
Expect(s).NotTo(BeNil())
Expect(s.ObjectMeta).NotTo(BeNil())
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, nil))
bi, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, nil))
Expect(err).NotTo(HaveOccurred())
Expect(bi).NotTo(BeNil())

View file

@ -0,0 +1,63 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package annotations
import (
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/ingress-nginx/test/e2e/framework"
)
var _ = framework.IngressNginxDescribe("Annotations - grpc", func() {
f := framework.NewDefaultFramework("grpc")
BeforeEach(func() {
err := f.NewGRPCFortuneTellerDeployment()
Expect(err).NotTo(HaveOccurred())
})
Context("when grpc is enabled", func() {
It("should use grpc_pass in the configuration file", func() {
host := "grpc"
annotations := map[string]string{
"nginx.ingress.kubernetes.io/grpc-backend": "true",
}
ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "fortune-teller", 50051, &annotations))
Expect(err).NotTo(HaveOccurred())
Expect(ing).NotTo(BeNil())
err = f.WaitForNginxServer(host,
func(server string) bool {
return Expect(server).Should(ContainSubstring(fmt.Sprintf("server_name %v", host))) &&
Expect(server).ShouldNot(ContainSubstring("return 503"))
})
Expect(err).NotTo(HaveOccurred())
err = f.WaitForNginxServer(host,
func(server string) bool {
return Expect(server).Should(ContainSubstring("grpc_pass")) &&
Expect(server).Should(ContainSubstring("grpc_set_header")) &&
Expect(server).ShouldNot(ContainSubstring("proxy_pass"))
})
Expect(err).NotTo(HaveOccurred())
})
})
})

View file

@ -39,7 +39,7 @@ var _ = framework.IngressNginxDescribe("Annotations - lua-resty-waf", func() {
Context("when lua-resty-waf is enabled", func() {
It("should return 403 for a malicious request that matches a default WAF rule and 200 for other requests", func() {
host := "foo"
createIngress(f, host, map[string]string{"nginx.ingress.kubernetes.io/lua-resty-waf": "active"})
createIngress(f, host, "http-svc", 80, map[string]string{"nginx.ingress.kubernetes.io/lua-resty-waf": "active"})
url := fmt.Sprintf("%s?msg=<A href=\"http://mysite.com/\">XSS</A>", f.IngressController.HTTPURL)
resp, _, errs := gorequest.New().
@ -52,7 +52,7 @@ var _ = framework.IngressNginxDescribe("Annotations - lua-resty-waf", func() {
})
It("should not apply ignored rulesets", func() {
host := "foo"
createIngress(f, host, map[string]string{
createIngress(f, host, "http-svc", 80, map[string]string{
"nginx.ingress.kubernetes.io/lua-resty-waf": "active",
"nginx.ingress.kubernetes.io/lua-resty-waf-ignore-rulesets": "41000_sqli, 42000_xss"})
@ -67,7 +67,7 @@ var _ = framework.IngressNginxDescribe("Annotations - lua-resty-waf", func() {
})
It("should apply configured extra rules", func() {
host := "foo"
createIngress(f, host, map[string]string{
createIngress(f, host, "http-svc", 80, map[string]string{
"nginx.ingress.kubernetes.io/lua-resty-waf": "active",
"nginx.ingress.kubernetes.io/lua-resty-waf-extra-rules": `[=[
{ "access": [
@ -106,7 +106,7 @@ var _ = framework.IngressNginxDescribe("Annotations - lua-resty-waf", func() {
Context("when lua-resty-waf is not enabled", func() {
It("should return 200 even for a malicious request", func() {
host := "foo"
createIngress(f, host, map[string]string{})
createIngress(f, host, "http-svc", 80, map[string]string{})
url := fmt.Sprintf("%s?msg=<A href=\"http://mysite.com/\">XSS</A>", f.IngressController.HTTPURL)
resp, _, errs := gorequest.New().
@ -119,7 +119,7 @@ var _ = framework.IngressNginxDescribe("Annotations - lua-resty-waf", func() {
})
It("should run in simulate mode", func() {
host := "foo"
createIngress(f, host, map[string]string{"nginx.ingress.kubernetes.io/lua-resty-waf": "simulate"})
createIngress(f, host, "http-svc", 80, map[string]string{"nginx.ingress.kubernetes.io/lua-resty-waf": "simulate"})
url := fmt.Sprintf("%s?msg=<A href=\"http://mysite.com/\">XSS</A>", f.IngressController.HTTPURL)
resp, _, errs := gorequest.New().
@ -138,14 +138,14 @@ var _ = framework.IngressNginxDescribe("Annotations - lua-resty-waf", func() {
})
})
func createIngress(f *framework.Framework, host string, annotations map[string]string) {
ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, &annotations))
func createIngress(f *framework.Framework, host, service string, port int, annotations map[string]string) {
ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, service, port, &annotations))
Expect(err).NotTo(HaveOccurred())
Expect(ing).NotTo(BeNil())
err = f.WaitForNginxServer(host,
func(server string) bool {
return Expect(server).Should(ContainSubstring("server_name foo")) &&
return Expect(server).Should(ContainSubstring(fmt.Sprintf("server_name %v", host))) &&
Expect(server).ShouldNot(ContainSubstring("return 503"))
})
Expect(err).NotTo(HaveOccurred())

View file

@ -385,7 +385,7 @@ func UpdateDeployment(kubeClientSet kubernetes.Interface, namespace string, name
}
// NewSingleIngress creates a simple ingress rule
func NewSingleIngress(name, path, host, ns string, annotations *map[string]string) *extensions.Ingress {
func NewSingleIngress(name, path, host, ns, service string, port int, annotations *map[string]string) *extensions.Ingress {
if annotations == nil {
annotations = &map[string]string{}
}
@ -412,8 +412,8 @@ func NewSingleIngress(name, path, host, ns string, annotations *map[string]strin
{
Path: path,
Backend: extensions.IngressBackend{
ServiceName: "http-svc",
ServicePort: intstr.FromInt(80),
ServiceName: service,
ServicePort: intstr.FromInt(port),
},
},
},

View file

@ -0,0 +1,125 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package framework
import (
"fmt"
"time"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/intstr"
)
// NewGRPCFortuneTellerDeployment creates a new single replica
// deployment of the fortune teller image in a particular namespace
func (f *Framework) NewGRPCFortuneTellerDeployment() error {
return f.NewNewGRPCFortuneTellerDeploymentWithReplicas(1)
}
// NewNewGRPCFortuneTellerDeploymentWithReplicas creates a new deployment of the
// fortune teller image in a particular namespace. Number of replicas is configurable
func (f *Framework) NewNewGRPCFortuneTellerDeploymentWithReplicas(replicas int32) error {
deployment := &extensions.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "fortune-teller",
Namespace: f.IngressController.Namespace,
},
Spec: extensions.DeploymentSpec{
Replicas: NewInt32(replicas),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "fortune-teller",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "fortune-teller",
},
},
Spec: corev1.PodSpec{
TerminationGracePeriodSeconds: NewInt64(0),
Containers: []corev1.Container{
{
Name: "fortune-teller",
Image: "quay.io/kubernetes-ingress-controller/grpc-fortune-teller:0.1",
Env: []corev1.EnvVar{},
Ports: []corev1.ContainerPort{
{
Name: "grpc",
ContainerPort: 50051,
},
},
},
},
},
},
},
}
d, err := f.EnsureDeployment(deployment)
if err != nil {
return err
}
if d == nil {
return fmt.Errorf("unexpected error creating deployement for fortune-teller")
}
err = WaitForPodsReady(f.KubeClientSet, 5*time.Minute, int(replicas), f.IngressController.Namespace, metav1.ListOptions{
LabelSelector: fields.SelectorFromSet(fields.Set(d.Spec.Template.ObjectMeta.Labels)).String(),
})
if err != nil {
return errors.Wrap(err, "failed to wait for to become ready")
}
service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "fortune-teller",
Namespace: f.IngressController.Namespace,
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "grpc",
Port: 50051,
TargetPort: intstr.FromInt(50051),
Protocol: "TCP",
},
},
Selector: map[string]string{
"app": "fortune-teller",
},
},
}
s, err := f.EnsureService(service)
if err != nil {
return err
}
if s == nil {
return fmt.Errorf("unexpected error creating service for fortune-teller deployment")
}
return nil
}

View file

@ -364,7 +364,7 @@ func enableDynamicConfiguration(namespace string, kubeClientSet kubernetes.Inter
}
func ensureIngress(f *framework.Framework, host string) (*extensions.Ingress, error) {
return f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, &map[string]string{
return f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, &map[string]string{
"nginx.ingress.kubernetes.io/load-balance": "ewma",
}))
}

View file

@ -50,7 +50,7 @@ var _ = framework.IngressNginxDescribe("Proxy Protocol", func() {
err := f.UpdateNginxConfigMapData(setting, "true")
Expect(err).NotTo(HaveOccurred())
ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, nil))
ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, nil))
Expect(err).NotTo(HaveOccurred())
Expect(ing).NotTo(BeNil())

View file

@ -44,7 +44,7 @@ var _ = framework.IngressNginxDescribe("Server Tokens", func() {
err := f.UpdateNginxConfigMapData(serverTokens, "false")
Expect(err).NotTo(HaveOccurred())
ing, err := f.EnsureIngress(framework.NewSingleIngress(serverTokens, "/", serverTokens, f.IngressController.Namespace, nil))
ing, err := f.EnsureIngress(framework.NewSingleIngress(serverTokens, "/", serverTokens, f.IngressController.Namespace, "http-svc", 80, nil))
Expect(err).NotTo(HaveOccurred())
Expect(ing).NotTo(BeNil())

View file

@ -177,7 +177,7 @@ var _ = framework.IngressNginxDescribe("Settings - TLS)", func() {
})
func tlsEndpoint(f *framework.Framework, host string) (*tls.Config, error) {
ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, nil))
ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, nil))
if err != nil {
return nil, err
}

View file

@ -54,7 +54,7 @@ var _ = framework.IngressNginxDescribe("SSL", func() {
})
Expect(err).NotTo(HaveOccurred())
ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, nil))
ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, nil))
Expect(err).ToNot(HaveOccurred())
Expect(ing).ToNot(BeNil())