From 9ca9baef4dd697d1bc7cd0e8bdc939459cf19bec Mon Sep 17 00:00:00 2001 From: ZLBer <1098294815@qq.com> Date: Tue, 8 Nov 2022 13:43:22 +0800 Subject: [PATCH] tls support: dubbo/dubbo3/grpc protocol (#2073) * tls support for grpc protocol * tls support for grpc protocol --- common/constant/key.go | 8 ++++ config/protocol_config.go | 9 ++-- config/reference_config.go | 19 ++++++-- config/service_config.go | 8 ++++ config/tls_config.go | 93 ++++++++++++++++++++++++++++++++++++++ protocol/grpc/client.go | 17 ++++++- protocol/grpc/server.go | 24 +++++++++- 7 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 config/tls_config.go diff --git a/common/constant/key.go b/common/constant/key.go index 4fd53cdb71..12d41c794c 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -64,6 +64,14 @@ const ( MaxServerRecvMsgSize = "max-server-recv-msg-size" ) +//tls constant +const ( + TLSKey = "tls_key" + TLSCert = "tls_cert" + CACert = "ca_cert" + TLSServerNAME = "tls_server_name" +) + const ( ServiceFilterKey = "service.filter" ReferenceFilterKey = "reference.filter" diff --git a/config/protocol_config.go b/config/protocol_config.go index 8c4f8ecb84..9a8016ad9c 100644 --- a/config/protocol_config.go +++ b/config/protocol_config.go @@ -27,10 +27,11 @@ import ( // ProtocolConfig is protocol configuration type ProtocolConfig struct { - Name string `default:"dubbo" validate:"required" yaml:"name" json:"name,omitempty" property:"name"` - Ip string `yaml:"ip" json:"ip,omitempty" property:"ip"` - Port string `default:"20000" yaml:"port" json:"port,omitempty" property:"port"` - Params interface{} `yaml:"params" json:"params,omitempty" property:"params"` + Name string `default:"dubbo" validate:"required" yaml:"name" json:"name,omitempty" property:"name"` + Ip string `yaml:"ip" json:"ip,omitempty" property:"ip"` + Port string `default:"20000" yaml:"port" json:"port,omitempty" property:"port"` + Params interface{} `yaml:"params" json:"params,omitempty" property:"params"` + TLSConfig *TLSConfig `yaml:"tls_config" json:"tls_config,omitempty" property:"tls_config"` } // Prefix dubbo.config-center diff --git a/config/reference_config.go b/config/reference_config.go index d4dfe876ec..bbc992706c 100644 --- a/config/reference_config.go +++ b/config/reference_config.go @@ -63,11 +63,12 @@ type ReferenceConfig struct { Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` invoker protocol.Invoker urls []*common.URL - Generic string `yaml:"generic" json:"generic,omitempty" property:"generic"` - Sticky bool `yaml:"sticky" json:"sticky,omitempty" property:"sticky"` - RequestTimeout string `yaml:"timeout" json:"timeout,omitempty" property:"timeout"` - ForceTag bool `yaml:"force.tag" json:"force.tag,omitempty" property:"force.tag"` - TracingKey string `yaml:"tracing-key" json:"tracing-key,omitempty" propertiy:"tracing-key"` + Generic string `yaml:"generic" json:"generic,omitempty" property:"generic"` + Sticky bool `yaml:"sticky" json:"sticky,omitempty" property:"sticky"` + RequestTimeout string `yaml:"timeout" json:"timeout,omitempty" property:"timeout"` + ForceTag bool `yaml:"force.tag" json:"force.tag,omitempty" property:"force.tag"` + TracingKey string `yaml:"tracing-key" json:"tracing-key,omitempty" propertiy:"tracing-key"` + TLSConfig *TLSConfig `yaml:"tls_config" json:"tls_config,omitempty" property:"tls_config"` rootConfig *RootConfig metaDataType string @@ -137,6 +138,14 @@ func (rc *ReferenceConfig) Refer(srv interface{}) { common.WithParamsValue(constant.BeanNameKey, rc.id), common.WithParamsValue(constant.MetadataTypeKey, rc.metaDataType), ) + //client tls client + if rc.TLSConfig != nil { + cfgURL.AddParam(constant.SslEnabledKey, "true") + cfgURL.AddParam(constant.TLSCert, rc.TLSConfig.TLSCertFile) + cfgURL.AddParam(constant.TLSKey, rc.TLSConfig.TLSKeyFile) + cfgURL.AddParam(constant.CACert, rc.TLSConfig.CACertFile) + cfgURL.AddParam(constant.TLSServerNAME, rc.TLSConfig.TLSServerName) + } SetConsumerServiceByInterfaceName(rc.InterfaceName, srv) if rc.ForceTag { diff --git a/config/service_config.go b/config/service_config.go index d669c8c5c3..2c8093ec73 100644 --- a/config/service_config.go +++ b/config/service_config.go @@ -279,6 +279,14 @@ func (s *ServiceConfig) Export() error { common.WithToken(s.Token), common.WithParamsValue(constant.MetadataTypeKey, s.metadataType), ) + //server tls config + if proto.TLSConfig != nil { + ivkURL.AddParam(constant.SslEnabledKey, "true") + ivkURL.AddParam(constant.TLSCert, proto.TLSConfig.TLSCertFile) + ivkURL.AddParam(constant.TLSKey, proto.TLSConfig.TLSKeyFile) + ivkURL.AddParam(constant.CACert, proto.TLSConfig.CACertFile) + ivkURL.AddParam(constant.TLSServerNAME, proto.TLSConfig.TLSServerName) + } if len(s.Tag) > 0 { ivkURL.AddParam(constant.Tagkey, s.Tag) } diff --git a/config/tls_config.go b/config/tls_config.go new file mode 100644 index 0000000000..c285a074b8 --- /dev/null +++ b/config/tls_config.go @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 config + +import ( + "crypto/tls" + "crypto/x509" + "io/ioutil" +) + +// TLSConfig tls config +type TLSConfig struct { + CACertFile string `yaml:"ca_cert_file" json:"ca_cert_file" property:"ca_cert_file"` + TLSCertFile string `yaml:"tls_cert_file" json:"tls_cert_file" property:"tls_cert_file"` + TLSKeyFile string `yaml:"tls_key_file" json:"tls_key_file" property:"tls_key_file"` + TLSServerName string `yaml:"tls_server_name" json:"tls_server_name" property:"tls_server_name"` +} + +// GetServerTlsConfig build server tls config from TLSConfig +func GetServerTlsConfig(opt *TLSConfig) (*tls.Config, error) { + //no TLS + if opt.TLSCertFile == "" && opt.TLSKeyFile == "" { + return nil, nil + } + var ca *x509.CertPool + cfg := &tls.Config{} + //need mTLS + if opt.CACertFile != "" { + ca = x509.NewCertPool() + caBytes, err := ioutil.ReadFile(opt.CACertFile) + if err != nil { + return nil, err + } + if ok := ca.AppendCertsFromPEM(caBytes); !ok { + return nil, err + } + cfg.ClientAuth = tls.RequireAndVerifyClientCert + cfg.ClientCAs = ca + } + cert, err := tls.LoadX509KeyPair(opt.TLSCertFile, opt.TLSKeyFile) + if err != nil { + return nil, err + } + cfg.Certificates = []tls.Certificate{cert} + cfg.ServerName = opt.TLSServerName + + return cfg, nil +} + +// GetClientTlsConfig build client tls config from TLSConfig +func GetClientTlsConfig(opt *TLSConfig) (*tls.Config, error) { + //no TLS + if opt.CACertFile == "" { + return nil, nil + } + cfg := &tls.Config{ + ServerName: opt.TLSServerName, + } + ca := x509.NewCertPool() + caBytes, err := ioutil.ReadFile(opt.CACertFile) + if err != nil { + return nil, err + } + if ok := ca.AppendCertsFromPEM(caBytes); !ok { + return nil, err + } + cfg.RootCAs = ca + //need mTls + if opt.TLSCertFile != "" { + var cert tls.Certificate + cert, err = tls.LoadX509KeyPair(opt.TLSCertFile, opt.TLSKeyFile) + if err != nil { + return nil, err + } + cfg.Certificates = []tls.Certificate{cert} + } + return cfg, err +} diff --git a/protocol/grpc/client.go b/protocol/grpc/client.go index f316d931c1..ead433c036 100644 --- a/protocol/grpc/client.go +++ b/protocol/grpc/client.go @@ -32,6 +32,8 @@ import ( "github.com/opentracing/opentracing-go" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" "gopkg.in/yaml.v2" ) @@ -65,7 +67,6 @@ func NewClient(url *common.URL) (*Client, error) { //connectTimeout := config.GetConsumerConfig().ConnectTimeout dialOpts = append(dialOpts, - grpc.WithInsecure(), grpc.WithBlock(), // todo config network timeout grpc.WithTimeout(time.Second*3), @@ -77,6 +78,20 @@ func NewClient(url *common.URL) (*Client, error) { grpc.MaxCallSendMsgSize(1024*1024*maxMessageSize), ), ) + if url.GetParam(constant.SslEnabledKey, "false") == "true" { + cfg, err := config.GetClientTlsConfig(&config.TLSConfig{ + CACertFile: url.GetParam(constant.CACert, ""), + TLSCertFile: url.GetParam(constant.TLSCert, ""), + TLSKeyFile: url.GetParam(constant.TLSKey, ""), + TLSServerName: url.GetParam(constant.TLSServerNAME, ""), + }) + if err != nil { + return nil, err + } + dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(cfg))) + } else { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } conn, err := grpc.Dial(url.Location, dialOpts...) if err != nil { diff --git a/protocol/grpc/server.go b/protocol/grpc/server.go index d938af857f..901ab7b879 100644 --- a/protocol/grpc/server.go +++ b/protocol/grpc/server.go @@ -18,6 +18,7 @@ package grpc import ( + "crypto/tls" "fmt" "net" "sync" @@ -32,11 +33,14 @@ import ( "github.com/opentracing/opentracing-go" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/reflection" ) import ( "dubbo.apache.org/dubbo-go/v3/common" + "dubbo.apache.org/dubbo-go/v3/common/constant" "dubbo.apache.org/dubbo-go/v3/config" "dubbo.apache.org/dubbo-go/v3/protocol" ) @@ -81,12 +85,30 @@ func (s *Server) Start(url *common.URL) { // If global trace instance was set, then server tracer instance // can be get. If not, will return NoopTracer. tracer := opentracing.GlobalTracer() - server := grpc.NewServer( + var serverOpts []grpc.ServerOption + serverOpts = append(serverOpts, grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer)), grpc.StreamInterceptor(otgrpc.OpenTracingStreamServerInterceptor(tracer)), grpc.MaxRecvMsgSize(1024*1024*s.bufferSize), grpc.MaxSendMsgSize(1024*1024*s.bufferSize), ) + + if url.GetParam(constant.SslEnabledKey, "false") == "true" { + var cfg *tls.Config + cfg, err = config.GetServerTlsConfig(&config.TLSConfig{ + CACertFile: url.GetParam(constant.CACert, ""), + TLSCertFile: url.GetParam(constant.TLSCert, ""), + TLSKeyFile: url.GetParam(constant.TLSKey, ""), + TLSServerName: url.GetParam(constant.TLSServerNAME, ""), + }) + if err != nil { + return + } + serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(cfg))) + } else { + serverOpts = append(serverOpts, grpc.Creds(insecure.NewCredentials())) + } + server := grpc.NewServer(serverOpts...) s.grpcServer = server go func() {