Skip to content

Commit

Permalink
internal/envoy: Enable xDS v3 Clusters
Browse files Browse the repository at this point in the history
Enables v3 xDS Clusters as well as supporting structs.

Updates projectcontour#1898

Signed-off-by: Steve Sloka <slokas@vmware.com>
  • Loading branch information
stevesloka committed Nov 10, 2020
1 parent 2885e38 commit 04acb5d
Show file tree
Hide file tree
Showing 5 changed files with 595 additions and 0 deletions.
110 changes: 110 additions & 0 deletions internal/envoy/v3/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright Project Contour 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 v3

import (
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
"github.com/projectcontour/contour/internal/dag"
"github.com/projectcontour/contour/internal/envoy"
"github.com/projectcontour/contour/internal/protobuf"
)

// UpstreamTLSContext creates an envoy_tls_v3.UpstreamTlsContext. By default
// UpstreamTLSContext returns a HTTP/1.1 TLS enabled context. A list of
// additional ALPN protocols can be provided.
func UpstreamTLSContext(peerValidationContext *dag.PeerValidationContext, sni string, clientSecret *dag.Secret, alpnProtocols ...string) *envoy_tls_v3.UpstreamTlsContext {
var clientSecretConfigs []*envoy_tls_v3.SdsSecretConfig
if clientSecret != nil {
clientSecretConfigs = []*envoy_tls_v3.SdsSecretConfig{{
Name: envoy.Secretname(clientSecret),
SdsConfig: ConfigSource("contour"),
}}
}

context := &envoy_tls_v3.UpstreamTlsContext{
CommonTlsContext: &envoy_tls_v3.CommonTlsContext{
AlpnProtocols: alpnProtocols,
TlsCertificateSdsSecretConfigs: clientSecretConfigs,
},
Sni: sni,
}

if peerValidationContext.GetCACertificate() != nil && len(peerValidationContext.GetSubjectName()) > 0 {
// We have to explicitly assign the value from validationContext
// to context.CommonTlsContext.ValidationContextType because the
// latter is an interface. Returning nil from validationContext
// directly into this field boxes the nil into the unexported
// type of this grpc OneOf field which causes proto marshaling
// to explode later on.
vc := validationContext(peerValidationContext.GetCACertificate(), peerValidationContext.GetSubjectName())
if vc != nil {
context.CommonTlsContext.ValidationContextType = vc
}
}

return context
}

func validationContext(ca []byte, subjectName string) *envoy_tls_v3.CommonTlsContext_ValidationContext {
vc := &envoy_tls_v3.CommonTlsContext_ValidationContext{
ValidationContext: &envoy_tls_v3.CertificateValidationContext{
TrustedCa: &envoy_core_v3.DataSource{
// TODO(dfc) update this for SDS
Specifier: &envoy_core_v3.DataSource_InlineBytes{
InlineBytes: ca,
},
},
},
}

if len(subjectName) > 0 {
vc.ValidationContext.MatchSubjectAltNames = []*matcher.StringMatcher{{
MatchPattern: &matcher.StringMatcher_Exact{
Exact: subjectName,
}},
}
}

return vc
}

// DownstreamTLSContext creates a new DownstreamTlsContext.
func DownstreamTLSContext(serverSecret *dag.Secret, tlsMinProtoVersion envoy_tls_v3.TlsParameters_TlsProtocol, peerValidationContext *dag.PeerValidationContext, alpnProtos ...string) *envoy_tls_v3.DownstreamTlsContext {
context := &envoy_tls_v3.DownstreamTlsContext{
CommonTlsContext: &envoy_tls_v3.CommonTlsContext{
TlsParams: &envoy_tls_v3.TlsParameters{
TlsMinimumProtocolVersion: tlsMinProtoVersion,
TlsMaximumProtocolVersion: envoy_tls_v3.TlsParameters_TLSv1_3,
CipherSuites: envoy.Ciphers,
},
TlsCertificateSdsSecretConfigs: []*envoy_tls_v3.SdsSecretConfig{{
Name: envoy.Secretname(serverSecret),
SdsConfig: ConfigSource("contour"),
}},
AlpnProtocols: alpnProtos,
},
}

if peerValidationContext.GetCACertificate() != nil {
vc := validationContext(peerValidationContext.GetCACertificate(), "")
if vc != nil {
context.CommonTlsContext.ValidationContextType = vc
context.RequireClientCertificate = protobuf.Bool(true)
}
}

return context
}
114 changes: 114 additions & 0 deletions internal/envoy/v3/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright Project Contour 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 v3

import (
"testing"

envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
"github.com/projectcontour/contour/internal/dag"
"github.com/projectcontour/contour/internal/protobuf"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestUpstreamTLSContext(t *testing.T) {
secret := &dag.Secret{
Object: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret",
Namespace: "default",
},
Type: v1.SecretTypeTLS,
Data: map[string][]byte{dag.CACertificateKey: []byte("ca")},
},
}

tests := map[string]struct {
validation *dag.PeerValidationContext
alpnProtocols []string
externalName string
want *envoy_tls_v3.UpstreamTlsContext
}{
"no alpn, no validation": {
want: &envoy_tls_v3.UpstreamTlsContext{
CommonTlsContext: &envoy_tls_v3.CommonTlsContext{},
},
},
"h2, no validation": {
alpnProtocols: []string{"h2c"},
want: &envoy_tls_v3.UpstreamTlsContext{
CommonTlsContext: &envoy_tls_v3.CommonTlsContext{
AlpnProtocols: []string{"h2c"},
},
},
},
"no alpn, missing altname": {
validation: &dag.PeerValidationContext{
CACertificate: secret,
},
want: &envoy_tls_v3.UpstreamTlsContext{
CommonTlsContext: &envoy_tls_v3.CommonTlsContext{},
},
},
"no alpn, missing ca": {
validation: &dag.PeerValidationContext{
SubjectName: "www.example.com",
},
want: &envoy_tls_v3.UpstreamTlsContext{
CommonTlsContext: &envoy_tls_v3.CommonTlsContext{},
},
},
"no alpn, ca and altname": {
validation: &dag.PeerValidationContext{
CACertificate: secret,
SubjectName: "www.example.com",
},
want: &envoy_tls_v3.UpstreamTlsContext{
CommonTlsContext: &envoy_tls_v3.CommonTlsContext{
ValidationContextType: &envoy_tls_v3.CommonTlsContext_ValidationContext{
ValidationContext: &envoy_tls_v3.CertificateValidationContext{
TrustedCa: &envoy_core_v3.DataSource{
Specifier: &envoy_core_v3.DataSource_InlineBytes{
InlineBytes: []byte("ca"),
},
},
MatchSubjectAltNames: []*matcher.StringMatcher{{
MatchPattern: &matcher.StringMatcher_Exact{
Exact: "www.example.com",
}},
},
},
},
},
},
},
"external name sni": {
externalName: "projectcontour.local",
want: &envoy_tls_v3.UpstreamTlsContext{
CommonTlsContext: &envoy_tls_v3.CommonTlsContext{},
Sni: "projectcontour.local",
},
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := UpstreamTLSContext(tc.validation, tc.externalName, nil, tc.alpnProtocols...)
protobuf.ExpectEqual(t, tc.want, got)
})
}
}
Loading

0 comments on commit 04acb5d

Please sign in to comment.