Skip to content

Commit

Permalink
Migrate CSR package to v1
Browse files Browse the repository at this point in the history
  • Loading branch information
palexster committed Jun 10, 2021
1 parent fb58bab commit 62a9e38
Show file tree
Hide file tree
Showing 14 changed files with 206 additions and 155 deletions.
8 changes: 5 additions & 3 deletions build/liqo-test/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
FROM golang:1.16 as builder
ENV PATH /go/bin:/usr/local/go/bin:/usr/local/kubebuilder/bin:$PATH
ENV PATH /go/bin:/usr/local/go/bin:/opt/kubebuilder/testbin:$PATH
ENV GOPATH /go
ENV K8S_VERSION=1.19.2
WORKDIR /go/src/github.com/liqotech/liqo
COPY go.mod ./go.mod
COPY go.sum ./go.sum
RUN go mod download

# Install kubebuilder
RUN curl -sL https://go.kubebuilder.io/dl/2.3.0/$(go env GOOS)/$(go env GOARCH) | tar -xz -C /tmp/
RUN mv /tmp/kubebuilder_2.3.0_$(go env GOOS)_$(go env GOARCH) /usr/local/kubebuilder
RUN curl --fail -sSLo envtest-bins.tar.gz "https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-${K8S_VERSION}-$(go env GOOS)-$(go env GOARCH).tar.gz"
RUN mkdir /usr/local/kubebuilder/
RUN tar -C /usr/local/kubebuilder/ --strip-components=1 -zvxf envtest-bins.tar.gz

# Install goimports
RUN GO111MODULE="on" go get -u github.com/ory/go-acc
Expand Down
36 changes: 6 additions & 30 deletions cmd/init-virtual-kubelet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package main

import (
"context"
"flag"
"os"
"path/filepath"

"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
Expand All @@ -18,7 +17,10 @@ import (

func main() {
var config *rest.Config
var distribution string
klog.Info("Loading client config")
flag.StringVar(&distribution, "k8s-distribution", "kubernetes", "determine the provider to adapt csr generation")
ctx := context.Background()

kubeconfigPath, ok := os.LookupEnv("KUBECONFIG")
if !ok {
Expand All @@ -42,11 +44,11 @@ func main() {
}

// Generate Key and CSR files in PEM format
if err := createCSRResource(name, client, vk.CsrLocation, vk.KeyLocation); err != nil {
if err := csr.CreateCSRResource(ctx, name, client, vk.CsrLocation, vk.KeyLocation, distribution); err != nil {
klog.Fatalf("Unable to create CSR: %s", err)
}

cert, err := csr.WaitForApproval(client, name)
cert, err := csr.WaitForApproval(ctx, client, name)
if err != nil {
klog.Fatalf("Unable to get certificate: %s", err)
}
Expand All @@ -55,29 +57,3 @@ func main() {
os.Exit(1)
}
}

func createCSRResource(name string, client kubernetes.Interface, CsrLocation string, KeyLocation string) error {
csrPem, keyPem, err := csr.GenerateVKCertificateBundle(name)
if err != nil {
return err
}

if err := utils.WriteFile(CsrLocation, csrPem); err != nil {
return err
}

if err := utils.WriteFile(KeyLocation, keyPem); err != nil {
return err
}

// Generate and create CSR resource
csrResource := csr.GenerateVKCSR(name, csrPem)
_, err = client.CertificatesV1beta1().CertificateSigningRequests().Create(context.TODO(), csrResource, metav1.CreateOptions{})
if errors.IsAlreadyExists(err) {
klog.Infof("CSR already exists: %s", err)
} else if err != nil {
klog.Errorf("Unable to create CSR: %s", err)
return err
}
return nil
}
4 changes: 2 additions & 2 deletions pkg/identityManager/identityManager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,14 @@ var _ = Describe("IdentityManager", func() {
certificate, err := identityManager.ApproveSigningRequest(remoteClusterID, base64.StdEncoding.EncodeToString(csrBytes))
Expect(err).To(BeNil())
Expect(certificate).NotTo(BeNil())
Expect(certificate).To(Equal([]byte("test")))
Expect(certificate).To(Equal([]byte(idManTest.FakeCRT)))
})

It("Retrieve Remote Certificate", func() {
certificate, err := identityManager.GetRemoteCertificate(remoteClusterID, base64.StdEncoding.EncodeToString(csrBytes))
Expect(err).To(BeNil())
Expect(certificate).NotTo(BeNil())
Expect(certificate).To(Equal([]byte("test")))
Expect(certificate).To(Equal([]byte(idManTest.FakeCRT)))
})

It("Retrieve Remote Certificate wrong clusterid", func() {
Expand Down
43 changes: 21 additions & 22 deletions pkg/identityManager/signingRequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"strings"
"time"

certv1beta1 "k8s.io/api/certificates/v1beta1"
certv1 "k8s.io/api/certificates/v1"
v1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -18,9 +18,8 @@ import (
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
"k8s.io/utils/pointer"

certificateSigningRequest2 "github.com/liqotech/liqo/pkg/utils/certificateSigningRequest"
certificateSigningRequest "github.com/liqotech/liqo/pkg/vkMachinery/csr"
)

// random package initialization.
Expand All @@ -30,7 +29,7 @@ func init() {

// GetRemoteCertificate retrieves a certificate issued in the past,
// given the clusterid and the signingRequest.
func (certManager *certificateIdentityManager) GetRemoteCertificate(clusterID string, signingRequest string) (certificate []byte, err error) {
func (certManager *certificateIdentityManager) GetRemoteCertificate(clusterID, signingRequest string) (certificate []byte, err error) {
namespace, err := certManager.namespaceManager.GetNamespace(clusterID)
if err != nil {
klog.Error(err)
Expand Down Expand Up @@ -80,7 +79,7 @@ func (certManager *certificateIdentityManager) GetRemoteCertificate(clusterID st
// ApproveSigningRequest approves a remote CertificateSigningRequest.
// It creates a CertificateSigningRequest CR to be issued by the local cluster, and approves it.
// This function will wait (with a timeout) for an available certificate before returning.
func (certManager *certificateIdentityManager) ApproveSigningRequest(clusterID string, signingRequest string) (certificate []byte, err error) {
func (certManager *certificateIdentityManager) ApproveSigningRequest(clusterID, signingRequest string) (certificate []byte, err error) {
rnd := fmt.Sprintf("%v", rand.Int63())

signingBytes, err := base64.StdEncoding.DecodeString(signingRequest)
Expand All @@ -89,37 +88,37 @@ func (certManager *certificateIdentityManager) ApproveSigningRequest(clusterID s
return nil, err
}

// TODO: move client-go to a newer version to use certificates/v1
cert := &certv1beta1.CertificateSigningRequest{
cert := &certv1.CertificateSigningRequest{
ObjectMeta: metav1.ObjectMeta{
GenerateName: strings.Join([]string{identitySecretRoot, ""}, "-"),
Labels: map[string]string{
// the informer needs to select it by label, this is a temporal ID for this request
randomIDLabel: rnd,
},
},
Spec: certv1beta1.CertificateSigningRequestSpec{
Spec: certv1.CertificateSigningRequestSpec{
Groups: []string{
"system:authenticated",
},
SignerName: pointer.StringPtr(certv1beta1.KubeAPIServerClientSignerName),
SignerName: certv1.KubeAPIServerClientSignerName,
Request: signingBytes,
Usages: []certv1beta1.KeyUsage{
certv1beta1.UsageDigitalSignature,
certv1beta1.UsageKeyEncipherment,
certv1beta1.UsageClientAuth,
Usages: []certv1.KeyUsage{
certv1.UsageDigitalSignature,
certv1.UsageKeyEncipherment,
certv1.UsageClientAuth,
},
},
}

cert, err = certManager.client.CertificatesV1beta1().CertificateSigningRequests().Create(context.TODO(), cert, metav1.CreateOptions{})
cert, err = certManager.client.CertificatesV1().CertificateSigningRequests().Create(context.TODO(), cert, metav1.CreateOptions{})
if err != nil {
klog.Error(err)
return nil, err
}

// approve the CertificateSigningRequest
if err = certificateSigningRequest2.ApproveCSR(certManager.client, cert, "IdentityManagerApproval", "This CSR was approved by Liqo Identity Manager"); err != nil {
if err = certificateSigningRequest.ApproveCSR(certManager.client, cert, "IdentityManagerApproval",
"This CSR was approved by Liqo Identity Manager"); err != nil {
klog.Error(err)
return nil, err
}
Expand All @@ -141,18 +140,18 @@ func (certManager *certificateIdentityManager) ApproveSigningRequest(clusterID s

// getCertificate retrieves the certificate given the CertificateSigningRequest and its randomID.
// If the certificate is not ready yet, it will wait for it (with a timeout).
func (certManager *certificateIdentityManager) getCertificate(csr *certv1beta1.CertificateSigningRequest, randomID string) ([]byte, error) {
func (certManager *certificateIdentityManager) getCertificate(csr *certv1.CertificateSigningRequest, randomID string) ([]byte, error) {
var certificate []byte

// define a function that will check if a generic object is a CSR with a issued certificate
checkCertificate := func(obj interface{}) bool {
certificateSigningRequest, ok := obj.(*certv1beta1.CertificateSigningRequest)
certificateSigningRequest, ok := obj.(*certv1.CertificateSigningRequest)
if !ok {
klog.Errorf("this object is not a CertificateSigningRequest: %v", obj)
return false
}

res := (certificateSigningRequest.Status.Certificate != nil && len(certificateSigningRequest.Status.Certificate) > 0)
res := certificateSigningRequest.Status.Certificate != nil && len(certificateSigningRequest.Status.Certificate) > 0
if res {
certificate = certificateSigningRequest.Status.Certificate
}
Expand All @@ -173,13 +172,13 @@ func (certManager *certificateIdentityManager) getCertificate(csr *certv1beta1.C
informer := cache.NewSharedIndexInformer(&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
options.LabelSelector = labelSelector.String()
return certManager.client.CertificatesV1beta1().CertificateSigningRequests().List(context.TODO(), options)
return certManager.client.CertificatesV1().CertificateSigningRequests().List(context.TODO(), options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
options.LabelSelector = labelSelector.String()
return certManager.client.CertificatesV1beta1().CertificateSigningRequests().Watch(context.TODO(), options)
return certManager.client.CertificatesV1().CertificateSigningRequests().Watch(context.TODO(), options)
},
}, &certv1beta1.CertificateSigningRequest{}, 0, cache.Indexers{})
}, &certv1.CertificateSigningRequest{}, 0, cache.Indexers{})

stopChan := make(chan struct{})
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
Expand Down Expand Up @@ -211,7 +210,7 @@ func (certManager *certificateIdentityManager) getCertificate(csr *certv1beta1.C
}

// storeRemoteCertificate stores the issued certificate in a Secret in the TenantControlNamespace.
func (certManager *certificateIdentityManager) storeRemoteCertificate(clusterID string, signingRequest []byte, certificate []byte) (*v1.Secret, error) {
func (certManager *certificateIdentityManager) storeRemoteCertificate(clusterID string, signingRequest, certificate []byte) (*v1.Secret, error) {
namespace, err := certManager.namespaceManager.GetNamespace(clusterID)
if err != nil {
klog.Error(err)
Expand Down
35 changes: 28 additions & 7 deletions pkg/identityManager/testUtils/csrApprover.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"context"
"os"

certv1beta1 "k8s.io/api/certificates/v1beta1"
certv1 "k8s.io/api/certificates/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
Expand All @@ -13,29 +13,50 @@ import (
"k8s.io/klog"
)

// FakeCRT is the fake CRT returned by the TestApprover as valid CRT.
var FakeCRT = `
-----BEGIN CERTIFICATE-----
MIIBvzCCAWWgAwIBAgIRAMd7Mz3fPrLm1aFUn02lLHowCgYIKoZIzj0EAwIwIzEh
MB8GA1UEAwwYazNzLWNsaWVudC1jYUAxNjE2NDMxOTU2MB4XDTIxMDQxOTIxNTMz
MFoXDTIyMDQxOTIxNTMzMFowMjEVMBMGA1UEChMMc3lzdGVtOm5vZGVzMRkwFwYD
VQQDExBzeXN0ZW06bm9kZTp0ZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
Xd9aZm6nftepZpUwof9RSUZqZDgu7dplIiDt8nnhO5Bquy2jn7/AVx20xb0Xz0d2
XLn3nn5M+lR2p3NlZmqWHaNrMGkwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoG
CCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU/fZa5enijRDB25DF
NT1/vPUy/hMwEwYDVR0RBAwwCoIIRE5TOnRlc3QwCgYIKoZIzj0EAwIDSAAwRQIg
b3JL5+Q3zgwFrciwfdgtrKv8MudlA0nu6EDQO7eaJbwCIQDegFyC4tjGPp/5JKqQ
kovW9X7Ook/tTW0HyX6D6HRciA==
-----END CERTIFICATE-----
`

// StartTestApprover mocks the CSRApprover.
// When a CSR is approved, it injects a fake certificate to fill the .status.Certificate field.
func StartTestApprover(client kubernetes.Interface, stopChan <-chan struct{}) {
// we need an informer to fill the certificate field, since no api server is running
informer := cache.NewSharedIndexInformer(&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
return client.CertificatesV1beta1().CertificateSigningRequests().List(context.TODO(), options)
return client.CertificatesV1().CertificateSigningRequests().List(context.TODO(), options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
return client.CertificatesV1beta1().CertificateSigningRequests().Watch(context.TODO(), options)
return client.CertificatesV1().CertificateSigningRequests().Watch(context.TODO(), options)
},
}, &certv1beta1.CertificateSigningRequest{}, 0, cache.Indexers{})
}, &certv1.CertificateSigningRequest{}, 0, cache.Indexers{})

informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
csr, ok := newObj.(*certv1beta1.CertificateSigningRequest)
csr, ok := newObj.(*certv1.CertificateSigningRequest)
if !ok {
klog.Info("not a csr")
os.Exit(1)
}

if csr.Status.Certificate == nil {
csr.Status.Certificate = []byte("test")
_, _ = client.CertificatesV1beta1().CertificateSigningRequests().UpdateStatus(
csr.Status.Certificate = []byte(FakeCRT)
_, err := client.CertificatesV1().CertificateSigningRequests().UpdateStatus(
context.TODO(), csr, metav1.UpdateOptions{})
if err != nil {
klog.Error(err)
}
}
},
})
Expand Down
35 changes: 0 additions & 35 deletions pkg/utils/certificateSigningRequest/approve.go

This file was deleted.

8 changes: 8 additions & 0 deletions pkg/vkMachinery/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@ package vkMachinery

import "path/filepath"

// VKCertsRootPath defines the path where VK certificates are stored.
const VKCertsRootPath = "/etc/virtual-kubelet/certs"

// KeyLocation defines the path where the VK Key file is stored.
var KeyLocation = filepath.Join(VKCertsRootPath, "server-key.pem")

// CertLocation defines the path where the VK Certificate is stored.
var CertLocation = filepath.Join(VKCertsRootPath, "server.crt")

// CsrLocation defines the path where the VK CSR is stored.
var CsrLocation = filepath.Join(VKCertsRootPath, "server.csr")

// CsrLabels defines the labels attached to the CSR resource.
var CsrLabels = map[string]string{
"virtual-kubelet.liqo.io/csr": "true",
}
Expand Down
Loading

0 comments on commit 62a9e38

Please sign in to comment.