dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/credentials/cert_manager.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package credentials
    19  
    20  import (
    21  	"bytes"
    22  	"context"
    23  	"crypto/tls"
    24  	"crypto/x509"
    25  	"encoding/pem"
    26  	"fmt"
    27  	"os"
    28  	"strconv"
    29  	"strings"
    30  	"time"
    31  
    32  	"dubbo.apache.org/dubbo-go/v3/xds/client/bootstrap"
    33  	"dubbo.apache.org/dubbo-go/v3/xds/credentials/certgenerate"
    34  	"dubbo.apache.org/dubbo-go/v3/xds/credentials/certprovider"
    35  	"dubbo.apache.org/dubbo-go/v3/xds/credentials/certprovider/pemfile"
    36  	"dubbo.apache.org/dubbo-go/v3/xds/credentials/certprovider/remote"
    37  	"dubbo.apache.org/dubbo-go/v3/xds/internal"
    38  	"dubbo.apache.org/dubbo-go/v3/xds/utils/envconfig"
    39  	"github.com/dubbogo/gost/log/logger"
    40  )
    41  
    42  func init() {
    43  	//init file_watcher builder
    44  	certprovider.Register(&pemfile.PluginBuilder{})
    45  	//init builder func
    46  	internal.GetCertificateProviderBuilder = certprovider.GetBuilder
    47  }
    48  
    49  // CertManager manage agent or no agent cert
    50  type CertManager interface {
    51  	GetCertificate() ([]tls.Certificate, error)
    52  	GetRootCertificate() (*x509.CertPool, error)
    53  }
    54  
    55  // NewCertManager return a manager
    56  func NewCertManager() (CertManager, error) {
    57  	bootstrapPath := os.Getenv(envconfig.XDSBootstrapFileNameEnv)
    58  	if bootstrapPath != "" {
    59  		manager := &AgentCertManager{}
    60  		config, err := bootstrap.NewConfig()
    61  		if err != nil {
    62  			logger.Errorf("build bootstrap config error :%s", err.Error())
    63  			return nil, err
    64  		}
    65  		certProvider, err := buildProvider(config.CertProviderConfigs, "default")
    66  
    67  		if err != nil {
    68  			logger.Errorf("get cert provider error :%s", err.Error())
    69  			return nil, err
    70  		}
    71  		manager.provider = certProvider
    72  		return manager, nil
    73  	} else {
    74  		manager := &CACertManager{}
    75  		manager.rootPath = RootCertPath
    76  		return manager, nil
    77  	}
    78  
    79  }
    80  
    81  // AgentCertManager work in istio agent mode
    82  type AgentCertManager struct {
    83  	provider certprovider.Provider
    84  }
    85  
    86  // GetRootCertificate return certificate of ca
    87  func (c *AgentCertManager) GetRootCertificate() (*x509.CertPool, error) {
    88  	material, err := c.provider.KeyMaterial(context.Background())
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	return material.Roots, nil
    93  }
    94  
    95  // GetCertificate return certificate of application
    96  func (c *AgentCertManager) GetCertificate() ([]tls.Certificate, error) {
    97  	material, err := c.provider.KeyMaterial(context.Background())
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	return material.Certs, nil
   102  }
   103  
   104  // buildProvider build cert provider from config
   105  func buildProvider(configs map[string]*certprovider.BuildableConfig, instanceName string) (certprovider.Provider, error) {
   106  	cfg, ok := configs[instanceName]
   107  	if !ok {
   108  		return nil, fmt.Errorf("certificate provider instance %q not found in bootstrap file", instanceName)
   109  	}
   110  	provider, err := cfg.Build(certprovider.BuildOptions{
   111  		CertName:     "ca",
   112  		WantIdentity: true,
   113  		WantRoot:     true,
   114  	})
   115  	if err != nil {
   116  		return nil, fmt.Errorf("xds: failed to get security plugin instance (%+v): %v", cfg, err)
   117  	}
   118  	return provider, nil
   119  }
   120  
   121  // CACertManager work in no agent mode, fetch cert form CA
   122  type CACertManager struct {
   123  	// Certs contains a slice of cert/key pairs used to prove local identity.
   124  	Certs []tls.Certificate
   125  	// Roots contains the set of trusted roots to validate the peer's identity.
   126  	Roots *x509.CertPool
   127  
   128  	NoAfter time.Time
   129  
   130  	RootNoAfter time.Time
   131  
   132  	rootPath string
   133  }
   134  
   135  // GetCertificate return certificate of application
   136  func (c *CACertManager) GetCertificate() ([]tls.Certificate, error) {
   137  	//cert expired
   138  	if time.Now().After(c.NoAfter) {
   139  		if err := c.UpdateCert(); err != nil {
   140  			return nil, err
   141  		}
   142  	}
   143  	return c.Certs, nil
   144  }
   145  
   146  // GetRootCertificate return certificate of ca
   147  func (c *CACertManager) GetRootCertificate() (*x509.CertPool, error) {
   148  	//root expired
   149  	if time.Now().After(c.RootNoAfter) {
   150  		if err := c.UpdateRoot(); err != nil {
   151  			return nil, err
   152  		}
   153  	}
   154  	return c.Roots, nil
   155  }
   156  
   157  // UpdateRoot update root cert
   158  func (c *CACertManager) UpdateRoot() error {
   159  	rootFileContents, err := os.ReadFile(c.rootPath)
   160  	if err != nil {
   161  		return err
   162  	}
   163  	trustPool := x509.NewCertPool()
   164  	if !trustPool.AppendCertsFromPEM(rootFileContents) {
   165  		logger.Warn("failed to parse root certificate")
   166  	}
   167  	c.Roots = trustPool
   168  	block, _ := pem.Decode(rootFileContents)
   169  	if block == nil {
   170  		return fmt.Errorf("failed to decode certificate")
   171  	}
   172  	cert, err := x509.ParseCertificate(block.Bytes)
   173  	if err != nil {
   174  		return err
   175  	}
   176  	c.RootNoAfter = cert.NotAfter
   177  	return nil
   178  }
   179  
   180  // UpdateCert update cert
   181  func (c *CACertManager) UpdateCert() error {
   182  	tokenProvider, err := NewSaTokenProvider(ServiceAccountPath)
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	trustRoot, err := c.GetRootCertificate()
   188  	if err != nil {
   189  		return err
   190  	}
   191  	citadelClient, err := remote.NewCitadelClient(&remote.Options{
   192  		CAEndpoint:    IstioCAEndpoint,
   193  		TrustedRoots:  trustRoot,
   194  		TokenProvider: tokenProvider,
   195  		CertSigner:    certSigner,
   196  		ClusterID:     clusterID,
   197  	})
   198  	if err != nil {
   199  		return err
   200  	}
   201  	host := URIPrefix + Domain + "/ns/" + PodNamespace + "/sa/" + ServiceAccountName
   202  	ttl, err := strconv.ParseInt(CertTTL, 10, 64)
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	options := certgenerate.CertOptions{
   208  		Host:       host,
   209  		RSAKeySize: 2048,
   210  		PKCS8Key:   true,
   211  		TTL:        time.Duration(ttl),
   212  	}
   213  
   214  	// Generate the cert/key, send CSR to CA.
   215  	csrPEM, keyPEM, err := certgenerate.GenCSR(options)
   216  	if err != nil {
   217  		logger.Errorf("failed to generate key and certificate for CSR: %v", err)
   218  		return err
   219  	}
   220  	sign, err := citadelClient.CSRSign(csrPEM, ttl)
   221  
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	cert, err := c.parseCert(concatCerts(sign), keyPEM)
   227  	if err != nil {
   228  		return err
   229  	}
   230  	c.Certs = []tls.Certificate{*cert}
   231  	return nil
   232  }
   233  
   234  func (c *CACertManager) parseCert(certByte []byte, keyByte []byte) (*tls.Certificate, error) {
   235  	block, _ := pem.Decode(certByte)
   236  	if block == nil {
   237  		return nil, fmt.Errorf("failed to decode certificate")
   238  	}
   239  	cert, err := x509.ParseCertificate(block.Bytes)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  	expired := cert.NotAfter
   244  	logger.Infof("cert expired after:" + expired.String())
   245  	c.NoAfter = expired
   246  	pair, err := tls.X509KeyPair(certByte, keyByte)
   247  	if err != nil {
   248  		return nil, fmt.Errorf("failed to parse certificate: %v", err)
   249  	}
   250  	return &pair, nil
   251  }
   252  
   253  func concatCerts(certsPEM []string) []byte {
   254  	if len(certsPEM) == 0 {
   255  		return []byte{}
   256  	}
   257  	var certChain bytes.Buffer
   258  	for i, c := range certsPEM {
   259  		certChain.WriteString(c)
   260  		if i < len(certsPEM)-1 && !strings.HasSuffix(c, "\n") {
   261  			certChain.WriteString("\n")
   262  		}
   263  	}
   264  	return certChain.Bytes()
   265  }