Skip to content

Commit

Permalink
Merge pull request #16 from wuxs/feat/dynamic-stream-server-certificate
Browse files Browse the repository at this point in the history
feat: dynamic generate stream server certificate
  • Loading branch information
benjaminhuo authored Aug 7, 2023
2 parents 9416d65 + 74b515c commit 4a9834f
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 1 deletion.
123 changes: 123 additions & 0 deletions cloud/pkg/cloudstream/certutil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package cloudstream

import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"errors"
"fmt"
pkgutil "github.com/kubeedge/kubeedge/pkg/util"
"k8s.io/klog/v2"
"math"
"math/big"
"net"
"time"

certutil "k8s.io/client-go/util/cert"
)

// Copy from edgewize

func SignCloudCoreCert(cacrt, cakey []byte) ([]byte, []byte, error) {
podIP, err := pkgutil.GetLocalIP(pkgutil.GetHostname())
if err != nil {
klog.Errorf("Failed to get Local IP address: %v", err)
return nil, nil, err
}
klog.Infoln("pod ip", podIP)

cfg := &certutil.Config{
CommonName: "EdgeWize",
Organization: []string{"EdgeWize"},
Usages: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
},
AltNames: certutil.AltNames{
IPs: []net.IP{net.ParseIP(podIP)}, // TODO
},
}
var keyDER []byte
caCert, err := x509.ParseCertificate(cacrt)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse a caCert from the given ASN.1 DER data, err: %v", err)
}
serverKey, err := NewPrivateKey(caCert.SignatureAlgorithm)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate a privateKey, err: %v", err)
}
var caKey crypto.Signer
switch caCert.SignatureAlgorithm {
case x509.ECDSAWithSHA256:
caKey, err = x509.ParseECPrivateKey(cakey)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse ECPrivateKey, err: %v", err)
}
keyDER, err = x509.MarshalECPrivateKey(serverKey.(*ecdsa.PrivateKey))
if err != nil {
return nil, nil, fmt.Errorf("failed to convert an EC private key to SEC 1, ASN.1 DER form, err: %v", err)
}
case x509.SHA256WithRSA:
caKey, err = x509.ParsePKCS1PrivateKey(cakey)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse PKCS1PrivateKey, err: %v", err)
}
keyDER = x509.MarshalPKCS1PrivateKey(serverKey.(*rsa.PrivateKey))
default:
return nil, nil, fmt.Errorf("unsupport signature algorithm: %s", caCert.SignatureAlgorithm.String())
}

certDER, err := NewCertFromCa(cfg, caCert, serverKey.Public(), caKey, 365*100)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate a certificate using the given CA certificate and key, err: %v", err)
}
return certDER, keyDER, nil
}

// NewPrivateKey creates a private key
func NewPrivateKey(algorithm x509.SignatureAlgorithm) (crypto.Signer, error) {
switch algorithm {
case x509.ECDSAWithSHA256:
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
case x509.SHA256WithRSA:
return rsa.GenerateKey(rand.Reader, 2048)
default:
return nil, fmt.Errorf("unsepport signature algorithm: %s", algorithm.String())
}
}

// NewCertFromCa creates a signed certificate using the given CA certificate and key
func NewCertFromCa(cfg *certutil.Config, caCert *x509.Certificate, serverKey crypto.PublicKey, caKey crypto.Signer, validalityPeriod time.Duration) ([]byte, 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: time.Now().UTC(),
NotAfter: time.Now().Add(time.Hour * 24 * validalityPeriod),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: cfg.Usages,
}
certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, serverKey, caKey)
if err != nil {
return nil, err
}
return certDERBytes, nil
}
67 changes: 66 additions & 1 deletion cloud/pkg/cloudstream/streamserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
certutil "k8s.io/client-go/util/cert"
"net/http"
"os"
"reflect"
Expand Down Expand Up @@ -322,10 +324,73 @@ func (s *StreamServer) Start() {
MinVersion: tls.VersionTLS12,
},
}
err = s.initStreamCert()
if err != nil {
klog.Exitf("Init stream cert error %v", err)
return
}
klog.Infof("Prepare to start stream server ...")
err = streamServer.ListenAndServeTLS(config.Config.TLSStreamCertFile, config.Config.TLSStreamPrivateKeyFile)
err = streamServer.ListenAndServeTLS("/etc/kubeedge/stream.crt", "/etc/kubeedge/stream.key")
if err != nil {
klog.Exitf("Start stream server error %v\n", err)
return
}
}

// read config.Config.TLSStreamCertFile and parse it to tls.Certificate
// regen cert with current pod ip
func (s *StreamServer) initStreamCert() error {
// get ca crt and key from configmap casecret in kubeedge namespace
kubeClient := client.GetKubeClient()
if kubeClient == nil {
return fmt.Errorf("can not get kube client")
}
cloudhubSecret, err := kubeClient.CoreV1().Secrets("kubeedge").Get(context.Background(), "cloudhub", v1.GetOptions{})
if err != nil {
return fmt.Errorf("get ca secret error %v", err)
}
var caCrt []byte
var caKey []byte

var ok bool
caCrtPem, ok := cloudhubSecret.Data["rootCA.crt"]
if !ok {
klog.Errorf("secret root ca cert is invalided")
return fmt.Errorf("invalid rootca secret")
}
if caCrtBlock, _ := pem.Decode(caCrtPem); caCrtBlock == nil {
klog.Errorf("pem decode root ca cert error")
return fmt.Errorf("invalid rootca secret")
} else {
caCrt = caCrtBlock.Bytes
}

caKeyPem, ok := cloudhubSecret.Data["rootCA.key"]
if !ok {
klog.Errorf("secret root ca key is invalided")
return fmt.Errorf("invalid rootca secret")
}
if caKeyBlock, _ := pem.Decode(caKeyPem); caKeyBlock == nil {
klog.Errorf("pem decode root ca key error")
return fmt.Errorf("invalid rootca secret")
} else {
caKey = caKeyBlock.Bytes
}
serverCrt, serverKey, err := SignCloudCoreCert(caCrt, caKey)
if err != nil {
return err
}
serverCrtPem := pem.EncodeToMemory(&pem.Block{Type: certutil.CertificateBlockType, Bytes: serverCrt})
serverKeyPem := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: serverKey})
klog.V(3).Infof("server crt: %s", string(serverCrtPem))
klog.V(3).Infof("server key: %s", string(serverKeyPem))
err = os.WriteFile("/etc/kubeedge/stream.crt", serverCrtPem, 0644)
if err != nil {
return err
}
err = os.WriteFile("/etc/kubeedge/stream.key", serverKeyPem, 0644)
if err != nil {
return err
}
return nil
}

0 comments on commit 4a9834f

Please sign in to comment.