diff --git a/docs/ingressroute.md b/docs/ingressroute.md index 8901859da9d..b74e5fb9030 100644 --- a/docs/ingressroute.md +++ b/docs/ingressroute.md @@ -776,6 +776,10 @@ spec: The `spec.tcpproxy` key indicates that this _root_ IngressRoute will forward all de-encrypted TCP traffic to the backend service. +In case `spec.virtualhost.tls` is not present, TLS is not going to be terminated +on Envoy and will be forwarded to the specified services, where the termination +would happen. This is called SSL/TLS Passthrough. + ### Limitations The current limitations are present in Contour 0.8. These will be addressed in later Contour versions. @@ -783,7 +787,6 @@ The current limitations are present in Contour 0.8. These will be addressed in l - `spec.routes` must be present in the `IngressRoute` document to pass validation, however they are ignored when `spec.tcpproxy` is present. - TCP Proxy IngressRoutes must be roots and can not delegate to other IngressRoutes. - TCP Proxying is not available on Kubernetes Ingress objects. -- `spec.virtualhost.tls` is required for TCP proxying. If not present, `spec.tcpproxy` will be ignored. ## Status Reporting diff --git a/internal/contour/listener.go b/internal/contour/listener.go index a13caca5316..c8fec6595c3 100644 --- a/internal/contour/listener.go +++ b/internal/contour/listener.go @@ -16,12 +16,13 @@ package contour import ( "sync" - "github.com/envoyproxy/go-control-plane/envoy/api/v2" + v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2" + "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth" "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener" "github.com/gogo/protobuf/proto" "github.com/heptio/contour/internal/dag" "github.com/heptio/contour/internal/envoy" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" ) const ( @@ -230,11 +231,6 @@ func (v *listenerVisitor) visit(vertex dag.Vertex) { // the listener properly. v.http = true case *dag.SecureVirtualHost: - data := vh.Data() - if data == nil { - // no secret for this vhost, skip it - return - } filters := []listener.Filter{ envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, v.httpsAccessLog()), } @@ -246,11 +242,16 @@ func (v *listenerVisitor) visit(vertex dag.Vertex) { alpnProtos = nil // do not offer ALPN } + var tlsContext *auth.DownstreamTlsContext + if vh.Secret != nil && vh.Secret.Data() != nil { + data := vh.Secret.Data() + tlsContext = envoy.DownstreamTLSContext(data[v1.TLSCertKey], data[v1.TLSPrivateKeyKey], vh.MinProtoVersion, alpnProtos...) + } fc := listener.FilterChain{ FilterChainMatch: &listener.FilterChainMatch{ ServerNames: []string{vh.Host}, }, - TlsContext: envoy.DownstreamTLSContext(data[v1.TLSCertKey], data[v1.TLSPrivateKeyKey], vh.MinProtoVersion, alpnProtos...), + TlsContext: tlsContext, Filters: filters, UseProxyProto: bv(v.UseProxyProto), } diff --git a/internal/dag/builder.go b/internal/dag/builder.go index 12461bd437b..40fc9197237 100644 --- a/internal/dag/builder.go +++ b/internal/dag/builder.go @@ -23,7 +23,7 @@ import ( "sync" "time" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/tools/cache" @@ -540,8 +540,8 @@ func (b *builder) DAG() *DAG { } } for _, svh := range b.svhosts { - // suppress secure virtual hosts without secrets. - if svh.Secret != nil { + // suppress secure virtual hosts without secrets or tcpproxy. + if svh.Secret != nil || svh.TCPProxy != nil { dag.roots = append(dag.roots, svh) } } @@ -586,7 +586,7 @@ func (b *builder) processIngressRoute(ir *ingressroutev1.IngressRoute, prefixMat visited = append(visited, ir) proxy := ir.Spec.TCPProxy - if proxy != nil && enforceTLS { + if proxy != nil { var ts []*TCPService for _, service := range proxy.Services { m := meta{name: service.Name, namespace: ir.Namespace} diff --git a/internal/dag/builder_test.go b/internal/dag/builder_test.go index 69ac4cf4a5f..6515a3c0e43 100644 --- a/internal/dag/builder_test.go +++ b/internal/dag/builder_test.go @@ -22,7 +22,7 @@ import ( "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth" "github.com/google/go-cmp/cmp" ingressroutev1 "github.com/heptio/contour/apis/contour/v1beta1" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -615,7 +615,8 @@ func TestDAGInsert(t *testing.T) { }, } - // ir1a tcp forwards traffic to default/kuard:8080 + // ir1a tcp forwards traffic to default/kuard:8080 by TLS terminating it + // first. ir1a := &ingressroutev1.IngressRoute{ ObjectMeta: metav1.ObjectMeta{ Name: "kuard-tcp", @@ -644,6 +645,26 @@ func TestDAGInsert(t *testing.T) { }, } + // ir1b tcp forwards traffic to default/kuard:8080 by TLS pass-throughing + // it. + ir1b := &ingressroutev1.IngressRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kuard-tcp", + Namespace: "default", + }, + Spec: ingressroutev1.IngressRouteSpec{ + VirtualHost: &ingressroutev1.VirtualHost{ + Fqdn: "kuard.example.com", + }, + TCPProxy: &ingressroutev1.TCPProxy{ + Services: []ingressroutev1.Service{{ + Name: "kuard", + Port: 8080, + }}, + }, + }, + } + // ir2 is like ir1 but refers to two backend services ir2 := &ingressroutev1.IngressRoute{ ObjectMeta: metav1.ObjectMeta{ @@ -1706,7 +1727,7 @@ func TestDAGInsert(t *testing.T) { ), }}, }, - "insert ingressroute with tcp forward": { + "insert ingressroute with tcp forward with TLS termination": { objs: []interface{}{ ir1a, s1, sec1, }, @@ -1726,6 +1747,24 @@ func TestDAGInsert(t *testing.T) { }, }, }, + "insert ingressroute with tcp forward without TLS termination": { + objs: []interface{}{ + ir1b, s1, + }, + want: []Vertex{ + &SecureVirtualHost{ + VirtualHost: VirtualHost{ + Host: "kuard.example.com", + Port: 443, + TCPProxy: &TCPProxy{ + Services: []*TCPService{ + tcpService(s1), + }, + }, + }, + }, + }, + }, "insert ingressroute with prefix rewrite route": { objs: []interface{}{ ir10, s1,