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  }