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 }