Skip to content

Commit

Permalink
tcpproxy: add tls passthrough
Browse files Browse the repository at this point in the history
Allow an IngressRoute for TCP forwarding but without a `tls` section
which implicitly configures it as a TLS Passthrough. TLS Passthrough
only reads the very first TLS packets and routes the traffic without
doing any kind of TLS termination. The chosen `virtualhost` is
discovered by reading the TLS SNI (Server Name Indication) extension.

Closes #15

Signed-off-by: Gorka Lerchundi Osa <glertxundi@gmail.com>
  • Loading branch information
glerchundi committed Jan 13, 2019
1 parent 57e217f commit a6996bb
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 16 deletions.
5 changes: 4 additions & 1 deletion docs/ingressroute.md
Expand Up @@ -776,14 +776,17 @@ 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.
- `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
Expand Down
17 changes: 9 additions & 8 deletions internal/contour/listener.go
Expand Up @@ -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 (
Expand Down Expand Up @@ -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()),
}
Expand All @@ -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),
}
Expand Down
8 changes: 4 additions & 4 deletions internal/dag/builder.go
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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}
Expand Down
45 changes: 42 additions & 3 deletions internal/dag/builder_test.go
Expand Up @@ -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"
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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,
},
Expand All @@ -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,
Expand Down

0 comments on commit a6996bb

Please sign in to comment.