...

Source file src/k8s.io/kubernetes/cmd/kubeadm/app/util/certs/util.go

Documentation: k8s.io/kubernetes/cmd/kubeadm/app/util/certs

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package certs
    18  
    19  import (
    20  	"crypto"
    21  	"crypto/rsa"
    22  	"crypto/x509"
    23  	"net"
    24  	"path/filepath"
    25  	"testing"
    26  
    27  	certutil "k8s.io/client-go/util/cert"
    28  	"k8s.io/client-go/util/keyutil"
    29  
    30  	"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
    31  )
    32  
    33  // SetupCertificateAuthority is a utility function for kubeadm testing that creates a
    34  // CertificateAuthority cert/key pair
    35  func SetupCertificateAuthority(t *testing.T) (*x509.Certificate, crypto.Signer) {
    36  	caCert, caKey, err := pkiutil.NewCertificateAuthority(&pkiutil.CertConfig{
    37  		Config: certutil.Config{CommonName: "kubernetes"},
    38  	})
    39  	if err != nil {
    40  		t.Fatalf("failure while generating CA certificate and key: %v", err)
    41  	}
    42  
    43  	return caCert, caKey
    44  }
    45  
    46  // AssertCertificateIsSignedByCa is a utility function for kubeadm testing that asserts if a given certificate is signed
    47  // by the expected CA
    48  func AssertCertificateIsSignedByCa(t *testing.T, cert *x509.Certificate, signingCa *x509.Certificate) {
    49  	if err := cert.CheckSignatureFrom(signingCa); err != nil {
    50  		t.Error("cert is not signed by signing CA as expected")
    51  	}
    52  }
    53  
    54  // AssertCertificateHasCommonName is a utility function for kubeadm testing that asserts if a given certificate has
    55  // the expected SubjectCommonName
    56  func AssertCertificateHasCommonName(t *testing.T, cert *x509.Certificate, commonName string) {
    57  	if cert.Subject.CommonName != commonName {
    58  		t.Errorf("cert has Subject.CommonName %s, expected %s", cert.Subject.CommonName, commonName)
    59  	}
    60  }
    61  
    62  // AssertCertificateHasOrganizations is a utility function for kubeadm testing that asserts if a given certificate has
    63  // and only has the expected Subject.Organization
    64  func AssertCertificateHasOrganizations(t *testing.T, cert *x509.Certificate, organizations ...string) {
    65  	if len(cert.Subject.Organization) != len(organizations) {
    66  		t.Fatalf("cert contains a different number of Subject.Organization, expected %v, got %v", organizations, cert.Subject.Organization)
    67  	}
    68  	for _, organization := range organizations {
    69  		found := false
    70  		for i := range cert.Subject.Organization {
    71  			if cert.Subject.Organization[i] == organization {
    72  				found = true
    73  			}
    74  		}
    75  		if !found {
    76  			t.Errorf("cert does not contain Subject.Organization %s as expected", organization)
    77  		}
    78  	}
    79  }
    80  
    81  // AssertCertificateHasClientAuthUsage is a utility function for kubeadm testing that asserts if a given certificate has
    82  // the expected ExtKeyUsageClientAuth
    83  func AssertCertificateHasClientAuthUsage(t *testing.T, cert *x509.Certificate) {
    84  	for i := range cert.ExtKeyUsage {
    85  		if cert.ExtKeyUsage[i] == x509.ExtKeyUsageClientAuth {
    86  			return
    87  		}
    88  	}
    89  	t.Error("cert has not ClientAuth usage as expected")
    90  }
    91  
    92  // AssertCertificateHasServerAuthUsage is a utility function for kubeadm testing that asserts if a given certificate has
    93  // the expected ExtKeyUsageServerAuth
    94  func AssertCertificateHasServerAuthUsage(t *testing.T, cert *x509.Certificate) {
    95  	for i := range cert.ExtKeyUsage {
    96  		if cert.ExtKeyUsage[i] == x509.ExtKeyUsageServerAuth {
    97  			return
    98  		}
    99  	}
   100  	t.Error("cert is not a ServerAuth")
   101  }
   102  
   103  // AssertCertificateHasDNSNames is a utility function for kubeadm testing that asserts if a given certificate has
   104  // the expected DNSNames
   105  func AssertCertificateHasDNSNames(t *testing.T, cert *x509.Certificate, DNSNames ...string) {
   106  	for _, DNSName := range DNSNames {
   107  		found := false
   108  		for _, val := range cert.DNSNames {
   109  			if val == DNSName {
   110  				found = true
   111  				break
   112  			}
   113  		}
   114  
   115  		if !found {
   116  			t.Errorf("cert does not contain DNSName %s", DNSName)
   117  		}
   118  	}
   119  }
   120  
   121  // AssertCertificateHasIPAddresses is a utility function for kubeadm testing that asserts if a given certificate has
   122  // the expected IPAddresses
   123  func AssertCertificateHasIPAddresses(t *testing.T, cert *x509.Certificate, IPAddresses ...net.IP) {
   124  	for _, IPAddress := range IPAddresses {
   125  		found := false
   126  		for _, val := range cert.IPAddresses {
   127  			if val.Equal(IPAddress) {
   128  				found = true
   129  				break
   130  			}
   131  		}
   132  
   133  		if !found {
   134  			t.Errorf("cert does not contain IPAddress %s", IPAddress)
   135  		}
   136  	}
   137  }
   138  
   139  // CreateCACert creates a generic CA cert.
   140  func CreateCACert(t *testing.T) (*x509.Certificate, crypto.Signer) {
   141  	certCfg := &pkiutil.CertConfig{Config: certutil.Config{CommonName: "kubernetes"}}
   142  	cert, key, err := pkiutil.NewCertificateAuthority(certCfg)
   143  	if err != nil {
   144  		t.Fatalf("couldn't create CA: %v", err)
   145  	}
   146  	return cert, key
   147  }
   148  
   149  // CreateTestCert makes a generic certificate with the given CA and alternative names.
   150  func CreateTestCert(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer, altNames certutil.AltNames) (*x509.Certificate, crypto.Signer, *pkiutil.CertConfig) {
   151  	config := &pkiutil.CertConfig{
   152  		Config: certutil.Config{
   153  			CommonName: "testCert",
   154  			Usages:     []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
   155  			AltNames:   altNames,
   156  		},
   157  	}
   158  	cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, config)
   159  	if err != nil {
   160  		t.Fatalf("couldn't create test cert: %v", err)
   161  	}
   162  	return cert, key, config
   163  }
   164  
   165  // CertTestCase is a configuration of certificates and whether it's expected to work.
   166  type CertTestCase struct {
   167  	Name        string
   168  	Files       PKIFiles
   169  	ExpectError bool
   170  }
   171  
   172  // GetSparseCertTestCases produces a series of cert configurations and their intended outcomes.
   173  func GetSparseCertTestCases(t *testing.T) []CertTestCase {
   174  
   175  	caCert, caKey := CreateCACert(t)
   176  	fpCACert, fpCAKey := CreateCACert(t)
   177  	etcdCACert, etcdCAKey := CreateCACert(t)
   178  
   179  	fpCert, fpKey, _ := CreateTestCert(t, fpCACert, fpCAKey, certutil.AltNames{})
   180  
   181  	return []CertTestCase{
   182  		{
   183  			Name: "nothing present",
   184  		},
   185  		{
   186  			Name: "CAs already exist",
   187  			Files: PKIFiles{
   188  				"ca.crt":             caCert,
   189  				"ca.key":             caKey,
   190  				"front-proxy-ca.crt": fpCACert,
   191  				"front-proxy-ca.key": fpCAKey,
   192  				"etcd/ca.crt":        etcdCACert,
   193  				"etcd/ca.key":        etcdCAKey,
   194  			},
   195  		},
   196  		{
   197  			Name: "CA certs only",
   198  			Files: PKIFiles{
   199  				"ca.crt":             caCert,
   200  				"front-proxy-ca.crt": fpCACert,
   201  				"etcd/ca.crt":        etcdCACert,
   202  			},
   203  			ExpectError: true,
   204  		},
   205  		{
   206  			Name: "FrontProxyCA with certs",
   207  			Files: PKIFiles{
   208  				"ca.crt":                 caCert,
   209  				"ca.key":                 caKey,
   210  				"front-proxy-ca.crt":     fpCACert,
   211  				"front-proxy-client.crt": fpCert,
   212  				"front-proxy-client.key": fpKey,
   213  				"etcd/ca.crt":            etcdCACert,
   214  				"etcd/ca.key":            etcdCAKey,
   215  			},
   216  		},
   217  		{
   218  			Name: "FrontProxy certs missing CA",
   219  			Files: PKIFiles{
   220  				"front-proxy-client.crt": fpCert,
   221  				"front-proxy-client.key": fpKey,
   222  			},
   223  			ExpectError: true,
   224  		},
   225  	}
   226  }
   227  
   228  // PKIFiles are a list of files that should be created for a test case
   229  type PKIFiles map[string]interface{}
   230  
   231  // WritePKIFiles writes the given files out to the given directory
   232  func WritePKIFiles(t *testing.T, dir string, files PKIFiles) {
   233  	for filename, body := range files {
   234  		switch body := body.(type) {
   235  		case *x509.Certificate:
   236  			if err := certutil.WriteCert(filepath.Join(dir, filename), pkiutil.EncodeCertPEM(body)); err != nil {
   237  				t.Errorf("unable to write certificate to file %q: [%v]", dir, err)
   238  			}
   239  		case *rsa.PublicKey:
   240  			publicKeyBytes, err := pkiutil.EncodePublicKeyPEM(body)
   241  			if err != nil {
   242  				t.Errorf("unable to write public key to file %q: [%v]", filename, err)
   243  			}
   244  			if err := keyutil.WriteKey(filepath.Join(dir, filename), publicKeyBytes); err != nil {
   245  				t.Errorf("unable to write public key to file %q: [%v]", filename, err)
   246  			}
   247  		case *rsa.PrivateKey:
   248  			privateKey, err := keyutil.MarshalPrivateKeyToPEM(body)
   249  			if err != nil {
   250  				t.Errorf("unable to write private key to file %q: [%v]", filename, err)
   251  			}
   252  			if err := keyutil.WriteKey(filepath.Join(dir, filename), privateKey); err != nil {
   253  				t.Errorf("unable to write private key to file %q: [%v]", filename, err)
   254  			}
   255  		}
   256  	}
   257  }
   258  

View as plain text