github.com/verrazzano/verrazzano@v1.7.0/platform-operator/internal/k8s/certificate/certificate.go (about)

     1  // Copyright (c) 2020, 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package certificate
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	cryptorand "crypto/rand"
    10  	"crypto/rsa"
    11  	"crypto/x509"
    12  	"crypto/x509/pkix"
    13  	"encoding/pem"
    14  	"fmt"
    15  	"math/big"
    16  	"os"
    17  	"time"
    18  
    19  	"go.uber.org/zap"
    20  	v1 "k8s.io/api/core/v1"
    21  	"k8s.io/apimachinery/pkg/api/errors"
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/client-go/kubernetes"
    24  	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
    25  )
    26  
    27  const (
    28  	// OperatorName is the resource name for the Verrazzano platform operator
    29  	OperatorName    = "verrazzano-platform-operator-webhook"
    30  	OldOperatorName = "verrazzano-platform-operator"
    31  	OperatorCA      = "verrazzano-platform-operator-ca"
    32  	OperatorTLS     = "verrazzano-platform-operator-tls"
    33  
    34  	// OperatorNamespace is the resource namespace for the Verrazzano platform operator
    35  	OperatorNamespace = "verrazzano-install"
    36  	CRDName           = "verrazzanos.install.verrazzano.io"
    37  
    38  	CertKey = "tls.crt"
    39  	PrivKey = "tls.key"
    40  
    41  	certYearsValid = 3
    42  )
    43  
    44  // CreateWebhookCertificates creates the needed certificates for the validating webhook
    45  func CreateWebhookCertificates(log *zap.SugaredLogger, kubeClient kubernetes.Interface, certDir string) error {
    46  
    47  	commonName := fmt.Sprintf("%s.%s.svc", OperatorName, OperatorNamespace)
    48  	ca, caKey, err := createCACert(log, kubeClient, commonName)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	serverPEM, serverKeyPEM, err := createTLSCert(log, kubeClient, commonName, ca, caKey)
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	log.Debugf("Creating certs dir %s", certDir)
    59  	if err := os.MkdirAll(certDir, 0666); err != nil {
    60  		log.Errorf("Mkdir error %v", err)
    61  		return err
    62  	}
    63  
    64  	if err := writeFile(log, fmt.Sprintf("%s/%s", certDir, CertKey), serverPEM); err != nil {
    65  		log.Errorf("Error writing cert file: %v", err)
    66  		return err
    67  	}
    68  
    69  	if err := writeFile(log, fmt.Sprintf("%s/%s", certDir, PrivKey), serverKeyPEM); err != nil {
    70  		log.Errorf("Error 3 %v", err)
    71  		return err
    72  	}
    73  
    74  	return nil
    75  }
    76  
    77  func createTLSCert(log *zap.SugaredLogger, kubeClient kubernetes.Interface, commonName string, ca *x509.Certificate, caKey *rsa.PrivateKey) ([]byte, []byte, error) {
    78  	secretsClient := kubeClient.CoreV1().Secrets(OperatorNamespace)
    79  	existingSecret, err := secretsClient.Get(context.TODO(), OperatorTLS, metav1.GetOptions{})
    80  	if err == nil {
    81  		log.Infof("Secret %s exists, using...", OperatorTLS)
    82  		return existingSecret.Data[CertKey], existingSecret.Data[PrivKey], nil
    83  	}
    84  	if !errors.IsNotFound(err) {
    85  		return []byte{}, []byte{}, err
    86  	}
    87  
    88  	serialNumber, err := newSerialNumber()
    89  	if err != nil {
    90  		return []byte{}, []byte{}, err
    91  	}
    92  
    93  	// server cert config
    94  	cert := &x509.Certificate{
    95  		DNSNames:     []string{commonName},
    96  		SerialNumber: serialNumber,
    97  		Subject: pkix.Name{
    98  			CommonName: commonName,
    99  		},
   100  		NotBefore:    time.Now(),
   101  		NotAfter:     time.Now().AddDate(certYearsValid, 0, 0),
   102  		IsCA:         false,
   103  		SubjectKeyId: []byte{1, 2, 3, 4, 6},
   104  		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
   105  		KeyUsage:     x509.KeyUsageDigitalSignature,
   106  	}
   107  
   108  	// server private key
   109  	serverPrivKey, err := rsa.GenerateKey(cryptorand.Reader, 4096)
   110  	if err != nil {
   111  		return []byte{}, []byte{}, err
   112  	}
   113  
   114  	// sign the server cert
   115  	serverCertBytes, err := x509.CreateCertificate(cryptorand.Reader, cert, ca, &serverPrivKey.PublicKey, caKey)
   116  	if err != nil {
   117  		return []byte{}, []byte{}, err
   118  	}
   119  
   120  	return createTLSCertSecretIfNecesary(log, secretsClient, serverCertBytes, serverPrivKey)
   121  }
   122  
   123  func createTLSCertSecretIfNecesary(log *zap.SugaredLogger, secretsClient corev1.SecretInterface,
   124  	serverCertBytes []byte, serverPrivKey *rsa.PrivateKey) ([]byte, []byte, error) {
   125  	// PEM encode Server cert
   126  	serverPEM := new(bytes.Buffer)
   127  	_ = pem.Encode(serverPEM, &pem.Block{
   128  		Type:  "CERTIFICATE",
   129  		Bytes: serverCertBytes,
   130  	})
   131  
   132  	// PEM encode Server cert
   133  	serverKeyPEM := new(bytes.Buffer)
   134  	_ = pem.Encode(serverKeyPEM, &pem.Block{
   135  		Type:  "RSA PRIVATE KEY",
   136  		Bytes: x509.MarshalPKCS1PrivateKey(serverPrivKey),
   137  	})
   138  
   139  	serverPEMBytes := serverPEM.Bytes()
   140  
   141  	serverKeyPEMBytes := serverKeyPEM.Bytes()
   142  
   143  	var webhookCrt v1.Secret
   144  	webhookCrt.Namespace = OperatorNamespace
   145  	webhookCrt.Name = OperatorTLS
   146  	webhookCrt.Type = v1.SecretTypeTLS
   147  	webhookCrt.Data = make(map[string][]byte)
   148  	webhookCrt.Data[CertKey] = serverPEMBytes
   149  	webhookCrt.Data[PrivKey] = serverKeyPEMBytes
   150  
   151  	_, createError := secretsClient.Create(context.TODO(), &webhookCrt, metav1.CreateOptions{})
   152  	if createError != nil {
   153  		if errors.IsAlreadyExists(createError) {
   154  			log.Infof("Operator CA secret %s already exists, skipping", OperatorCA)
   155  			existingSecret, err := secretsClient.Get(context.TODO(), OperatorTLS, metav1.GetOptions{})
   156  			if err != nil {
   157  				return []byte{}, []byte{}, err
   158  			}
   159  			log.Infof("Secret %s exists, using...", OperatorTLS)
   160  			return existingSecret.Data[CertKey], existingSecret.Data[PrivKey], nil
   161  		}
   162  		return []byte{}, []byte{}, createError
   163  	}
   164  
   165  	return serverPEMBytes, serverKeyPEMBytes, nil
   166  }
   167  
   168  func createCACert(log *zap.SugaredLogger, kubeClient kubernetes.Interface, commonName string) (*x509.Certificate, *rsa.PrivateKey, error) {
   169  	secretsClient := kubeClient.CoreV1().Secrets(OperatorNamespace)
   170  	existingSecret, err := secretsClient.Get(context.TODO(), OperatorCA, metav1.GetOptions{})
   171  	if err == nil {
   172  		log.Infof("CA secret %s exists, using...", OperatorCA)
   173  		return decodeExistingSecretData(existingSecret)
   174  	}
   175  	if !errors.IsNotFound(err) {
   176  		return nil, nil, err
   177  	}
   178  
   179  	log.Infof("Creating CA secret %s", OperatorCA)
   180  	serialNumber, err := newSerialNumber()
   181  	if err != nil {
   182  		return nil, nil, err
   183  	}
   184  
   185  	// CA config
   186  	ca := &x509.Certificate{
   187  		DNSNames:     []string{commonName},
   188  		SerialNumber: serialNumber,
   189  		Subject: pkix.Name{
   190  			CommonName: commonName,
   191  		},
   192  		NotBefore:             time.Now(),
   193  		NotAfter:              time.Now().AddDate(certYearsValid, 0, 0),
   194  		IsCA:                  true,
   195  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
   196  		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
   197  		BasicConstraintsValid: true,
   198  	}
   199  
   200  	// CA private key
   201  	caPrivKey, err := rsa.GenerateKey(cryptorand.Reader, 4096)
   202  	if err != nil {
   203  		return nil, nil, err
   204  	}
   205  
   206  	// Self signed CA certificate
   207  	caBytes, err := x509.CreateCertificate(cryptorand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
   208  	if err != nil {
   209  		return nil, nil, err
   210  	}
   211  
   212  	return createCACertSecretIfNecessary(log, secretsClient, ca, caPrivKey, caBytes)
   213  }
   214  
   215  func createCACertSecretIfNecessary(log *zap.SugaredLogger, secretsClient corev1.SecretInterface, ca *x509.Certificate,
   216  	caPrivKey *rsa.PrivateKey, caBytes []byte) (*x509.Certificate, *rsa.PrivateKey, error) {
   217  	caPEMBytes, caKeyPEMBytes := encodeCABytes(caBytes, caPrivKey)
   218  
   219  	webhookCA := v1.Secret{}
   220  	webhookCA.Namespace = OperatorNamespace
   221  	webhookCA.Name = OperatorCA
   222  	webhookCA.Type = v1.SecretTypeTLS
   223  
   224  	webhookCA.Data = make(map[string][]byte)
   225  	webhookCA.Data[CertKey] = caPEMBytes
   226  	webhookCA.Data[PrivKey] = caKeyPEMBytes
   227  
   228  	_, createError := secretsClient.Create(context.TODO(), &webhookCA, metav1.CreateOptions{})
   229  	if createError != nil {
   230  		if errors.IsAlreadyExists(createError) {
   231  			log.Infof("Operator CA secret %s already exists, using existing secret", OperatorCA)
   232  			existingSecret, err := secretsClient.Get(context.TODO(), OperatorCA, metav1.GetOptions{})
   233  			if err != nil {
   234  				return nil, nil, err
   235  			}
   236  			return decodeExistingSecretData(existingSecret)
   237  		}
   238  		return nil, nil, createError
   239  	}
   240  	return ca, caPrivKey, nil
   241  }
   242  
   243  // encodeCABytes PEM-encode the certificate and Key data
   244  func encodeCABytes(caBytes []byte, caPrivKey *rsa.PrivateKey) ([]byte, []byte) {
   245  	// PEM encode CA cert
   246  	caPEM := new(bytes.Buffer)
   247  	_ = pem.Encode(caPEM, &pem.Block{
   248  		Type:  "CERTIFICATE",
   249  		Bytes: caBytes,
   250  	})
   251  
   252  	// PEM encode CA cert
   253  	caKeyPEM := new(bytes.Buffer)
   254  	_ = pem.Encode(caKeyPEM, &pem.Block{
   255  		Type:  "RSA PRIVATE KEY",
   256  		Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey),
   257  	})
   258  
   259  	return caPEM.Bytes(), caKeyPEM.Bytes()
   260  }
   261  
   262  // decodeExistingSecretData Decode existing secret data into their X509 and RSA objects
   263  func decodeExistingSecretData(secret *v1.Secret) (*x509.Certificate, *rsa.PrivateKey, error) {
   264  	cert, err := decodeCertificate(secret.Data[CertKey])
   265  	if err != nil {
   266  		return nil, nil, err
   267  	}
   268  	key, err := decodeKey(secret.Data[PrivKey])
   269  	if err != nil {
   270  		return nil, nil, err
   271  	}
   272  	return cert, key, err
   273  }
   274  
   275  // decodeCertificate Decode certificate PEM data
   276  func decodeCertificate(certBytes []byte) (*x509.Certificate, error) {
   277  	p, _ := pem.Decode(certBytes)
   278  	if p == nil {
   279  		return nil, fmt.Errorf("Unable to decode certificate")
   280  	}
   281  	certificate, err := x509.ParseCertificate(p.Bytes)
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  	return certificate, nil
   286  }
   287  
   288  // decodeKey - Decode private Key PEM data
   289  func decodeKey(keyBytes []byte) (*rsa.PrivateKey, error) {
   290  	p, _ := pem.Decode(keyBytes)
   291  	if p == nil {
   292  		return nil, fmt.Errorf("Unable to decode certificate")
   293  	}
   294  	key, err := x509.ParsePKCS1PrivateKey(p.Bytes)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  	return key, nil
   299  }
   300  
   301  // newSerialNumber returns a new random serial number suitable for use in a certificate.
   302  func newSerialNumber() (*big.Int, error) {
   303  	// A serial number can be up to 20 octets in size.
   304  	return cryptorand.Int(cryptorand.Reader, new(big.Int).Lsh(big.NewInt(1), 8*20))
   305  }
   306  
   307  // writeFile writes data in the file at the given path
   308  func writeFile(log *zap.SugaredLogger, filepath string, pemData []byte) error {
   309  	log.Debugf("Writing file %s", filepath)
   310  	f, err := os.Create(filepath)
   311  	if err != nil {
   312  		return err
   313  	}
   314  	defer f.Close()
   315  
   316  	_, err = f.Write(pemData)
   317  	if err != nil {
   318  		return err
   319  	}
   320  
   321  	return nil
   322  }