istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/cert/ca/intermediate.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 ca 16 17 import ( 18 "os" 19 "path/filepath" 20 21 corev1 "k8s.io/api/core/v1" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 24 "istio.io/istio/pkg/test/cert" 25 "istio.io/istio/pkg/test/util/file" 26 "istio.io/istio/pkg/test/util/tmpl" 27 ) 28 29 const ( 30 istioConfTemplate = ` 31 [ req ] 32 encrypt_key = no 33 prompt = no 34 utf8 = yes 35 default_md = sha256 36 default_bits = 4096 37 req_extensions = req_ext 38 x509_extensions = req_ext 39 distinguished_name = req_dn 40 [ req_ext ] 41 subjectKeyIdentifier = hash 42 basicConstraints = critical, CA:true, pathlen:0 43 keyUsage = critical, digitalSignature, nonRepudiation, keyEncipherment, keyCertSign 44 subjectAltName=@san 45 [ san ] 46 DNS.1 = istiod.{{ .SystemNamespace }} 47 DNS.2 = istiod.{{ .SystemNamespace }}.svc 48 DNS.3 = istio-pilot.{{ .SystemNamespace }} 49 DNS.4 = istio-pilot.{{ .SystemNamespace }}.svc 50 [ req_dn ] 51 O = Istio 52 CN = Intermediate CA 53 ` 54 ) 55 56 // NewIstioConfig creates an extensions configuration for Istio, using the given system namespace in 57 // the DNS SANs. 58 func NewIstioConfig(systemNamespace string) (string, error) { 59 return tmpl.Evaluate(istioConfTemplate, map[string]any{ 60 "SystemNamespace": systemNamespace, 61 }) 62 } 63 64 // IntermediateCA is an intermediate CA for a single cluster. 65 type Intermediate struct { 66 KeyFile string 67 ConfFile string 68 CSRFile string 69 CertFile string 70 Root Root 71 } 72 73 // NewIntermediate creates a new intermediate CA for the given cluster. 74 func NewIntermediate(workDir, config string, root Root) (Intermediate, error) { 75 ca := Intermediate{ 76 KeyFile: filepath.Join(workDir, "ca-key.pem"), 77 ConfFile: filepath.Join(workDir, "ca.conf"), 78 CSRFile: filepath.Join(workDir, "ca.csr"), 79 CertFile: filepath.Join(workDir, "ca-cert.pem"), 80 Root: root, 81 } 82 83 // Write out the CA config file. 84 if err := os.WriteFile(ca.ConfFile, []byte(config), os.ModePerm); err != nil { 85 return Intermediate{}, err 86 } 87 88 // Create the key for the intermediate CA. 89 if err := cert.GenerateKey(ca.KeyFile); err != nil { 90 return Intermediate{}, err 91 } 92 93 // Create the CSR for the intermediate CA. 94 if err := cert.GenerateCSR(ca.ConfFile, ca.KeyFile, ca.CSRFile); err != nil { 95 return Intermediate{}, err 96 } 97 98 // Create the intermediate cert, signed by the root. 99 if err := cert.GenerateIntermediateCert(ca.ConfFile, ca.CSRFile, root.CertFile, 100 root.KeyFile, ca.CertFile); err != nil { 101 return Intermediate{}, err 102 } 103 104 return ca, nil 105 } 106 107 // NewIstioCASecret creates a secret (named "cacerts") containing the intermediate certificate and cert chain. 108 // If available when Istio starts, this will be used instead of Istio's autogenerated self-signed root 109 // (istio-ca-secret). This can be used in a multicluster environment in order to establish a common root of 110 // trust between the clusters. 111 func (ca Intermediate) NewIstioCASecret() (*corev1.Secret, error) { 112 caCert, err := file.AsString(ca.CertFile) 113 if err != nil { 114 return nil, err 115 } 116 caKey, err := file.AsString(ca.KeyFile) 117 if err != nil { 118 return nil, err 119 } 120 rootCert, err := file.AsString(ca.Root.CertFile) 121 if err != nil { 122 return nil, err 123 } 124 125 // Create the cert chain by concatenating the intermediate and root certs. 126 certChain := caCert + rootCert 127 128 return &corev1.Secret{ 129 ObjectMeta: metav1.ObjectMeta{ 130 Name: "cacerts", 131 }, 132 Data: map[string][]byte{ 133 "ca-cert.pem": []byte(caCert), 134 "ca-key.pem": []byte(caKey), 135 "cert-chain.pem": []byte(certChain), 136 "root-cert.pem": []byte(rootCert), 137 }, 138 }, nil 139 }