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 }