From bbda851a69b8a934f2cdd5d3fca86740dc9a9058 Mon Sep 17 00:00:00 2001 From: mojiiba <91647529+mojiiba@users.noreply.github.com> Date: Thu, 20 Jul 2023 16:14:03 -0400 Subject: [PATCH] Add MTLS to Static Binding (#1919) --- go.mod | 2 +- go.sum | 2 + topologies/binding/options.go | 33 +++++++++++++ topologies/proto/binding.proto | 13 +++++ topologies/proto/binding/binding.pb.go | 66 +++++++++++++++++++++----- 5 files changed, 104 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index fc76846cc0a..eecb963a5e8 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( golang.org/x/text v0.10.0 google.golang.org/api v0.122.0 google.golang.org/grpc v1.56.0 - google.golang.org/protobuf v1.30.0 + google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 25b656f153c..f3708bedf1b 100644 --- a/go.sum +++ b/go.sum @@ -1703,6 +1703,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/topologies/binding/options.go b/topologies/binding/options.go index c1384d5d8c2..0c97e2c7856 100644 --- a/topologies/binding/options.go +++ b/topologies/binding/options.go @@ -17,6 +17,7 @@ package binding import ( "context" "crypto/tls" + "crypto/x509" "fmt" "net" "net/http" @@ -75,6 +76,27 @@ type dialer struct { *bindpb.Options } +// load trust bundle and client key and certificate +func (d *dialer) loadCertificates() (*x509.CertPool, tls.Certificate, error) { + if d.CertFile == "" || d.KeyFile == "" || d.TrustBundleFile == "" { + return nil, tls.Certificate{}, fmt.Errorf("cert_file, key_file, and trust_bundle_file need to be set when mutual tls is set") + } + caCertBytes, err := os.ReadFile(d.TrustBundleFile) + if err != nil { + return nil, tls.Certificate{}, err + } + trusBundle := x509.NewCertPool() + if !trusBundle.AppendCertsFromPEM(caCertBytes) { + return nil, tls.Certificate{}, fmt.Errorf("error in loading ca trust bundle") + } + keyPair, err := tls.LoadX509KeyPair(d.CertFile, d.KeyFile) + if err != nil { + return nil, tls.Certificate{}, fmt.Errorf("could load the client keys") + } + return trusBundle, keyPair, nil + +} + // dialGRPC dials a gRPC connection using the binding options. // //lint:ignore U1000 will be used by the binding. @@ -86,6 +108,17 @@ func (d *dialer) dialGRPC(ctx context.Context, opts ...grpc.DialOption) (*grpc.C case d.SkipVerify: tc := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}) opts = append(opts, grpc.WithTransportCredentials(tc)) + case d.MutualTls: + trusBundle, keyPair, err := d.loadCertificates() + if err != nil { + return nil, err + } + tls := &tls.Config{ + Certificates: []tls.Certificate{keyPair}, + RootCAs: trusBundle, + } + tlsConfig := credentials.NewTLS(tls) + opts = append(opts, grpc.WithTransportCredentials(tlsConfig)) } if d.Username != "" { c := &creds{d.Username, d.Password, !d.Insecure} diff --git a/topologies/proto/binding.proto b/topologies/proto/binding.proto index 3edc07a1c9e..74206855ce5 100644 --- a/topologies/proto/binding.proto +++ b/topologies/proto/binding.proto @@ -124,6 +124,19 @@ message Options { // gRPC dial option to set the maximum recv message size in bytes. int32 max_recv_msg_size = 8; + + // When using TLS, enable mutual certificate verification (gRPC) + bool mutual_tls = 9; + + // Trust bundle file: a *.pem file that contains one or more certificates (root and intermediate CAs) + string trust_bundle_file = 10; + + // Certificate file path : a *.pem file that is signed by root or intermediate CA + string cert_file = 11; + + // Key file Path: a *.pem file that contains a private key + string key_file = 12; + } // Port binding. diff --git a/topologies/proto/binding/binding.pb.go b/topologies/proto/binding/binding.pb.go index 5245c3330fc..3d475980995 100644 --- a/topologies/proto/binding/binding.pb.go +++ b/topologies/proto/binding/binding.pb.go @@ -14,8 +14,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 -// protoc v3.21.12 +// protoc-gen-go v1.31.0 +// protoc v3.19.1 // source: binding.proto package binding @@ -387,6 +387,14 @@ type Options struct { Timeout int32 `protobuf:"varint,7,opt,name=timeout,proto3" json:"timeout,omitempty"` // gRPC dial option to set the maximum recv message size in bytes. MaxRecvMsgSize int32 `protobuf:"varint,8,opt,name=max_recv_msg_size,json=maxRecvMsgSize,proto3" json:"max_recv_msg_size,omitempty"` + // When using TLS, enable mutual certificate verification (gRPC) + MutualTls bool `protobuf:"varint,9,opt,name=mutual_tls,json=mutualTls,proto3" json:"mutual_tls,omitempty"` + // Trust bundle file: a *.pem file that contains one or more certificates (root and intermediate CAs) + TrustBundleFile string `protobuf:"bytes,10,opt,name=trust_bundle_file,json=trustBundleFile,proto3" json:"trust_bundle_file,omitempty"` + // Certificate file path : a *.pem file that is signed by root or intermediate CA + CertFile string `protobuf:"bytes,11,opt,name=cert_file,json=certFile,proto3" json:"cert_file,omitempty"` + // Key file Path: a *.pem file that contains a private key + KeyFile string `protobuf:"bytes,12,opt,name=key_file,json=keyFile,proto3" json:"key_file,omitempty"` } func (x *Options) Reset() { @@ -477,6 +485,34 @@ func (x *Options) GetMaxRecvMsgSize() int32 { return 0 } +func (x *Options) GetMutualTls() bool { + if x != nil { + return x.MutualTls + } + return false +} + +func (x *Options) GetTrustBundleFile() string { + if x != nil { + return x.TrustBundleFile + } + return "" +} + +func (x *Options) GetCertFile() string { + if x != nil { + return x.CertFile + } + return "" +} + +func (x *Options) GetKeyFile() string { + if x != nil { + return x.KeyFile + } + return "" +} + // Port binding. type Port struct { state protoimpl.MessageState @@ -607,7 +643,7 @@ var file_binding_proto_rawDesc = []byte{ 0x61, 0x72, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x22, 0xfa, 0x01, 0x0a, 0x07, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x69, 0x6f, 0x6e, 0x22, 0xfd, 0x02, 0x0a, 0x07, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x65, 0x63, @@ -623,14 +659,22 @@ var file_binding_proto_rawDesc = []byte{ 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x29, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x63, 0x76, 0x5f, 0x6d, 0x73, 0x67, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x63, 0x76, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x7a, 0x65, - 0x22, 0x2a, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x40, 0x5a, 0x3e, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x70, 0x72, 0x6f, - 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x75, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x74, 0x6c, 0x73, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6d, 0x75, 0x74, 0x75, 0x61, 0x6c, 0x54, 0x6c, 0x73, 0x12, + 0x2a, 0x0a, 0x11, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5f, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, + 0x65, 0x72, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x63, 0x65, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x46, + 0x69, 0x6c, 0x65, 0x22, 0x2a, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, + 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, + 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, + 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var (