istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/csrctrl/signer/ca_provider.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package signer 16 17 import ( 18 "bytes" 19 "crypto" 20 "fmt" 21 "os" 22 "sync/atomic" 23 "time" 24 25 "k8s.io/client-go/util/cert" 26 "k8s.io/client-go/util/keyutil" 27 28 "istio.io/istio/pkg/test/cert/ca" 29 "istio.io/istio/pkg/test/csrctrl/authority" 30 ) 31 32 func newCAProvider(signerRoot, signerName string) (*caProvider, error) { 33 strRoot := signerRoot + "/" + signerName + "/" 34 // create the folder if it does not exist 35 if _, err := os.Stat(strRoot); os.IsNotExist(err) { 36 dErr := os.MkdirAll(strRoot, os.ModePerm) 37 if dErr != nil { 38 return nil, fmt.Errorf("error creating CA cert folder %s: %v", strRoot, dErr) 39 } 40 cErr := os.Chmod(strRoot, os.ModePerm) 41 if cErr != nil { 42 return nil, fmt.Errorf("error change mode of CA cert folder %s: %v", strRoot, cErr) 43 } 44 } 45 caLoader, err := ca.NewRoot(strRoot) 46 if err != nil { 47 return nil, fmt.Errorf("error reading CA cert file %s: %v", strRoot, err) 48 } 49 // Create the new extensions config for the CA 50 caConfig, err := ca.NewIstioConfig("istio-system") 51 if err != nil { 52 return nil, err 53 } 54 intermediateCA, err := ca.NewIntermediate(strRoot, caConfig, caLoader) 55 if err != nil { 56 return nil, err 57 } 58 ret := &caProvider{ 59 caLoader: caLoader, 60 caIntermediate: intermediateCA, 61 } 62 if err := ret.setCA(); err != nil { 63 return nil, err 64 } 65 66 return ret, nil 67 } 68 69 type caProvider struct { 70 caValue atomic.Value 71 caLoader ca.Root 72 caIntermediate ca.Intermediate 73 } 74 75 // currentCertContent retrieve current certificate content from cert file 76 func (p *caProvider) currentCertContent() ([]byte, error) { 77 certBytes, err := os.ReadFile(p.caIntermediate.CertFile) 78 if err != nil { 79 return []byte(""), fmt.Errorf("error reading CA from cert file %s: %v", p.caLoader.CertFile, err) 80 } 81 return certBytes, nil 82 } 83 84 // currentKeyContent retrieve current private key content from key file 85 func (p *caProvider) currentKeyContent() ([]byte, error) { 86 keyBytes, err := os.ReadFile(p.caIntermediate.KeyFile) 87 if err != nil { 88 return []byte(""), fmt.Errorf("error reading private key from key file %s: %v", p.caLoader.KeyFile, err) 89 } 90 return keyBytes, nil 91 } 92 93 // setCA unconditionally stores the current cert/key content 94 func (p *caProvider) setCA() error { 95 certPEM, cerr := p.currentCertContent() 96 if cerr != nil { 97 return cerr 98 } 99 100 keyPEM, kerr := p.currentKeyContent() 101 if kerr != nil { 102 return kerr 103 } 104 105 certs, err := cert.ParseCertsPEM(certPEM) 106 if err != nil { 107 return fmt.Errorf("error reading CA cert file %q: %v", p.caLoader.CertFile, err) 108 } 109 if len(certs) != 1 { 110 return fmt.Errorf("error reading CA cert file %q: expected 1 certificate, found %d", p.caLoader.CertFile, len(certs)) 111 } 112 113 key, err := keyutil.ParsePrivateKeyPEM(keyPEM) 114 if err != nil { 115 return fmt.Errorf("error reading CA key file %q: %v", p.caLoader.KeyFile, err) 116 } 117 priv, ok := key.(crypto.Signer) 118 if !ok { 119 return fmt.Errorf("error reading CA key file %q: key did not implement crypto.Signer", p.caLoader.KeyFile) 120 } 121 122 ca := &authority.CertificateAuthority{ 123 RawCert: certPEM, 124 RawKey: keyPEM, 125 126 Certificate: certs[0], 127 PrivateKey: priv, 128 Backdate: 5 * time.Minute, 129 } 130 p.caValue.Store(ca) 131 132 return nil 133 } 134 135 // currentCA provides the current value of the CA. 136 // It always check for a stale value. This is cheap because it's all an in memory cache of small slices. 137 func (p *caProvider) currentCA() (*authority.CertificateAuthority, error) { 138 certPEM, cerr := p.currentCertContent() 139 if cerr != nil { 140 return nil, cerr 141 } 142 143 keyPEM, kerr := p.currentKeyContent() 144 if kerr != nil { 145 return nil, kerr 146 } 147 currCA := p.caValue.Load().(*authority.CertificateAuthority) 148 if bytes.Equal(currCA.RawCert, certPEM) && bytes.Equal(currCA.RawKey, keyPEM) { 149 return currCA, nil 150 } 151 152 // the bytes weren't equal, so we have to set and then load 153 if err := p.setCA(); err != nil { 154 return currCA, err 155 } 156 return p.caValue.Load().(*authority.CertificateAuthority), nil 157 }