deploy/kubernetes/operator/pkg/utils/certs.go (185 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utils
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"math"
"math/big"
"net"
"strings"
"time"
"k8s.io/client-go/util/cert"
"k8s.io/client-go/util/keyutil"
"k8s.io/klog/v2"
)
const (
certificateBlockType = "CERTIFICATE"
rsaKeySize = 2048
duration365d = time.Hour * 24 * 365 * 100
blockPrivateType = "RSA PRIVATE KEY"
blockCertType = "CERTIFICATE"
)
// newPrivateKey generates a private key.
func newPrivateKey() (*rsa.PrivateKey, error) {
return rsa.GenerateKey(rand.Reader, rsaKeySize)
}
// SetUpCaKey sets up a new ca private key
func SetUpCaKey() ([]byte, error) {
signingKey, err := newPrivateKey()
if err != nil {
return nil, fmt.Errorf("failed to create CA private key %v", err)
}
privateSigningKeyPEM, err := keyutil.MarshalPrivateKeyToPEM(signingKey)
if err != nil {
klog.Errorf("marshall private key to pem error:%v", err)
return nil, err
}
return privateSigningKeyPEM, nil
}
// SetUpCaCert sets up a new ca certification.
func SetUpCaCert(commonName string, pemCAKey []byte) ([]byte, error) {
caKey, err := decodePemToRSA(pemCAKey)
if err != nil {
klog.Errorf("decode pem to rsa private error %v", err)
return nil, err
}
signingCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: commonName}, caKey)
if err != nil {
klog.Errorf("self sign error %v", err)
return nil, fmt.Errorf("failed to create CA cert for apiserver %v", err)
}
return encodeCertPEM(signingCert), nil
}
// SetUpSignedCertAndKey uses the ca certificate and ca private key to generate certificates.
func SetUpSignedCertAndKey(domains []string, ips []net.IP, commonName string,
pemCAKey, pemCACert []byte, usage []x509.ExtKeyUsage) (
[]byte, []byte, error) {
// decode ca private key in PEM format.
caKey, err := decodePemToRSA(pemCAKey)
if err != nil {
klog.Errorf("decode pem to rsa private error %v", err)
return nil, nil, err
}
// decode ca certificate in PEM format.
caCert, err := decodePemToCert(pemCACert)
if err != nil {
klog.Errorf("decode pem to cert error %v", err)
return nil, nil, err
}
// create a new private key.
signedKey, err := newPrivateKey()
if err != nil {
return nil, nil, fmt.Errorf("failed to create private key %v", err)
}
// generate a new certificate by new private key, ca certificate and ca private key.
signedCert, err := newSignedCert(
&cert.Config{
CommonName: commonName,
AltNames: cert.AltNames{DNSNames: domains, IPs: ips},
Usages: usage,
},
signedKey, caCert, caKey,
)
if err != nil {
return nil, nil, fmt.Errorf("failed to create cert %v", err)
}
privateSigningKeyPEM, err := keyutil.MarshalPrivateKeyToPEM(signedKey)
if err != nil {
klog.Errorf("marshall private key to pem error:%v", err)
return nil, nil, err
}
// return a new certificate and private key in PEM format.
return encodeCertPEM(signedCert), privateSigningKeyPEM, nil
}
// SetupServerCert sets up the server certificate and private key.
func SetupServerCert(domain, commonName string) ([]byte, []byte, []byte, error) {
signingKey, err := newPrivateKey()
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create CA private key %v", err)
}
signingCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: commonName}, signingKey)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create CA cert for apiserver %v", err)
}
key, err := newPrivateKey()
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create private key for %v", err)
}
signedCert, err := newSignedCert(
&cert.Config{
CommonName: commonName,
AltNames: cert.AltNames{DNSNames: strings.Split(domain, ",")},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
},
key, signingCert, signingKey,
)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create cert %v", err)
}
privateKeyPEM, err := keyutil.MarshalPrivateKeyToPEM(key)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to marshal key %v", err)
}
return encodeCertPEM(signedCert), privateKeyPEM, encodeCertPEM(signingCert), nil
}
// newSignedCert generates s self-signed cert.
func newSignedCert(cfg *cert.Config, key crypto.Signer, caCert *x509.Certificate,
caKey crypto.Signer) (*x509.Certificate, error) {
serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
if err != nil {
return nil, err
}
if len(cfg.CommonName) == 0 {
return nil, errors.New("must specify a CommonName")
}
if len(cfg.Usages) == 0 {
return nil, errors.New("must specify at least one ExtKeyUsage")
}
certTmpl := x509.Certificate{
Subject: pkix.Name{
CommonName: cfg.CommonName,
Organization: cfg.Organization,
},
DNSNames: cfg.AltNames.DNSNames,
IPAddresses: cfg.AltNames.IPs,
SerialNumber: serial,
NotBefore: caCert.NotBefore,
NotAfter: time.Now().Add(duration365d).UTC(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: cfg.Usages,
}
certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, key.Public(), caKey)
if err != nil {
return nil, err
}
return x509.ParseCertificate(certDERBytes)
}
// encodeCertPEM encodes a certificate.
func encodeCertPEM(cert *x509.Certificate) []byte {
block := pem.Block{
Type: certificateBlockType,
Bytes: cert.Raw,
}
return pem.EncodeToMemory(&block)
}
// decodePemToRSA decodes pem key to RSA private key.
func decodePemToRSA(pemKey []byte) (*rsa.PrivateKey, error) {
block, _ := pem.Decode(pemKey)
if block == nil || block.Type != blockPrivateType {
err := errors.New("block is nil or block type is wrong")
klog.Errorf("decode pem key error %v", err)
return nil, err
}
pri, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
klog.Errorf("parse pem key error %v", err)
return nil, err
}
return pri, nil
}
// decodePemToCert decodes pem cert to x509 certificate.
func decodePemToCert(pemCert []byte) (*x509.Certificate, error) {
block, _ := pem.Decode(pemCert)
if block == nil || block.Type != blockCertType {
err := errors.New("block is nil or block type is wrong")
klog.Errorf("decode pem cert error %v", err)
return nil, err
}
certificate, err := x509.ParseCertificate(block.Bytes)
if err != nil {
klog.Errorf("parse pem cert error %v", err)
return nil, err
}
return certificate, nil
}