Skip to content

Commit

Permalink
SRIOV provider, csrcreator: switch to self-sign certificates (#472)
Browse files Browse the repository at this point in the history
* sriov provider, cert generator: switch to self-sign certificates

Currently we use Kubernetes CertificateSigningRequest (CSR)
for creating certificats for sriov-network-operaotor webhhoks.

We encounter flakiness on during CSR negotiation causing
cluster-up to fail often.

This commit presents new code for generating self-signed
certificats for sriov-network-operator webhooks.

Signed-off-by: Or Mergi <[email protected]>

* update .gitigone

Signed-off-by: Or Mergi <[email protected]>
  • Loading branch information
ormergi authored Nov 12, 2020
1 parent 73a1177 commit c2cf403
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 259 deletions.
2 changes: 1 addition & 1 deletion cluster-up/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cluster/kind-k8s-sriov*/csrcreator/*.cert
cluster/kind-k8s-sriov*/certcreator/*.cert
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package certlib

import (
"bytes"
cryptorand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"math/rand"
"time"
)

type SelfSignedCertificate struct {
DNSNames []string
CommonName string
Certificate *bytes.Buffer
PrivateKey *bytes.Buffer
}

func (s *SelfSignedCertificate) Generate() error {
var caPEM *bytes.Buffer

randomSource := rand.New(rand.NewSource(time.Now().Unix()))
caCertificateConfig := &x509.Certificate{
SerialNumber: big.NewInt(randomSource.Int63()),
Subject: pkix.Name{
Organization: []string{"kubvirt.io"},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1, 0, 0),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}

caPrivateKey, err := rsa.GenerateKey(cryptorand.Reader, 4096)
if err != nil {
return fmt.Errorf("failed to generate CA private key: %v", err)
}

caSelfSignedCertificateBytes, err := x509.CreateCertificate(
cryptorand.Reader,
caCertificateConfig,
caCertificateConfig,
&caPrivateKey.PublicKey,
caPrivateKey)
if err != nil {
return fmt.Errorf("failed to generate CA certificate: %v", err)
}

// PEM encode CA cert
caPEM = new(bytes.Buffer)
err = pem.Encode(caPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: caSelfSignedCertificateBytes,
})
if err != nil {
return fmt.Errorf("failed to encode CA certificate bytes to PEM: %v", err)
}

serverCertificateConfig := &x509.Certificate{
DNSNames: s.DNSNames,
SerialNumber: big.NewInt(randomSource.Int63()),
Subject: pkix.Name{
CommonName: s.CommonName,
Organization: []string{"kubevirt.io"},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1, 0, 0),
SubjectKeyId: []byte{1, 2, 3, 4, 6},
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}

serverPrivateKey, err := rsa.GenerateKey(cryptorand.Reader, 4096)
if err != nil {
return fmt.Errorf("failed to generate server private key: %v", err)
}

// Signing server certificate
serverCertificateBytes, err := x509.CreateCertificate(
cryptorand.Reader,
serverCertificateConfig,
caCertificateConfig,
&serverPrivateKey.PublicKey,
caPrivateKey)
if err != nil {
return fmt.Errorf("failed to sign server certificate: %v", err)
}

// PEM encode the server cert and key
s.Certificate = new(bytes.Buffer)
err = pem.Encode(s.Certificate, &pem.Block{
Type: "CERTIFICATE",
Bytes: serverCertificateBytes,
})
if err != nil {
return fmt.Errorf("failed to encode server certificate bytes to PEM: %v", err)
}

s.PrivateKey = new(bytes.Buffer)
err = pem.Encode(s.PrivateKey, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(serverPrivateKey),
})
if err != nil {
return fmt.Errorf("failed to encode server private key bytes to PEM: %v", err)
}

return nil
}
140 changes: 140 additions & 0 deletions cluster-up/cluster/kind-k8s-sriov-1.17.0/certcreator/certsecret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package main

import (
"encoding/base64"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"time"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"

"kubevirt.io/kubevirtci/cluster-up/cluster/kind-k8s-sriov-1.17.0/certcreator/certlib"
)

func handleKubeClientConfig(kubeconfig string) (*rest.Config, error) {
if kubeconfig == "" {
log.Printf("Using env kubeconfig %s", kubeconfig)
kubeconfig = os.Getenv("KUBECONFIG")
}

var config *rest.Config
var err error
if kubeconfig != "" {
log.Printf("Loading kube client config from path %q", kubeconfig)
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
} else {
log.Printf("Using in-cluster kube client config")
config, err = rest.InClusterConfig()
}
if err != nil {
return nil, fmt.Errorf("could not get the client: %v", err)
}

return config, nil
}

func generate(hookName, namespace string) ([]byte, []byte, error) {
serviceName := strings.Join([]string{hookName, "service"}, "-")

certConfig := certlib.SelfSignedCertificate{
CommonName: strings.Join([]string{serviceName, namespace, "svc"}, "."),
DNSNames: []string{
serviceName,
strings.Join([]string{serviceName, namespace}, "."),
strings.Join([]string{serviceName, namespace, "svc"}, ".")},
}
err := certConfig.Generate()
if err != nil {
return nil, nil, fmt.Errorf("failed to generate self-signed certificate: %v", err)
}
log.Printf("Self-Signed certificate created sucessfully for CN %s", certConfig.CommonName)

return certConfig.Certificate.Bytes(), certConfig.PrivateKey.Bytes(), nil
}

func exportCertificateFile(data []byte, filePath string) error {
certificateFileName := fmt.Sprintf("%s.cert", filePath)
encodedData := []byte(base64.StdEncoding.EncodeToString(data))
if err := ioutil.WriteFile(certificateFileName, encodedData, 0644); err != nil {
return fmt.Errorf("failed to write content to file %s: %v", filePath, err)
}
log.Printf("certificate exported successfully to: %s", filePath)

return nil
}

func createSecret(clusterApi kubernetes.Interface, namespace, secretName string, certificate, key []byte) error {
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
Data: map[string][]byte{
"tls.crt": certificate,
"tls.key": key,
},
}

err := wait.Poll(time.Second*5, time.Minute*3, func() (bool, error) {
_, err := clusterApi.CoreV1().Secrets(namespace).Create(secret)
if err != nil {
log.Printf("failed to create secret '%s': %v", secret.Name, err)
return false, nil
}
return true, nil
})
if err != nil {
return fmt.Errorf("timeout waiting for secret '%s' to create secret: %v", secret.Name, err)
}
log.Printf("Secret '%s' at '%s' created sucessfully", secret.Name, namespace)

return nil
}

func main() {
namespace := flag.String("namespace", "", "The namespace of the webhook")
kubeconfig := flag.String("kubeconfig", "", "The path of kubeconfig")
hookName := flag.String("hook", "", "The name of the hook")
secretName := flag.String("secret", "", "The name of the secret")
flag.Parse()

if *namespace == "" || *hookName == "" || *secretName == "" {
flag.Usage()
log.Fatal("Not enough arguments")
}

var err error
config, err := handleKubeClientConfig(*kubeconfig)
if err != nil {
log.Fatalf("Failed to set kubernetes client config: %v", err)
}

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatalf("Failed to set up Kubernetes client: %v", err)
}

certificate, key, err := generate(*hookName, *namespace)
if err != nil {
log.Fatalf("Failed to generate certificate: %v", err)
}

err = exportCertificateFile(certificate, *hookName)
if err != nil {
log.Fatalf("Failed to export certificate to file: %v", err)
}

err = createSecret(clientset, *namespace, *secretName, certificate, key)
if err != nil {
log.Fatalf("Failed to create Secret: %v", err)
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
module github.com/kubevirt/csrcreate
module kubevirt.io/kubevirtci/cluster-up/cluster/kind-k8s-sriov-1.17.0/certcreator

go 1.13

require (
github.com/cloudflare/cfssl v1.4.1
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/pkg/errors v0.9.1
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
k8s.io/api v0.17.3
Expand Down
Loading

0 comments on commit c2cf403

Please sign in to comment.