How to approve a Certificate Signing Request using Kubernetes go-client

1.6k Views Asked by At

I am currently trying to create a Certificate Signing Request resource on my Kubernetes cluster and then approve it. I am doing this with the go-client, but I am facing issues with the approval process.

Here is the function responsible for defining the csr struct:

func PopulateCSR(UserName string, csrPemBlock []byte) corev1.CertificateSigningRequest {
    var seconds int32 = 315569260
    usages := []corev1.KeyUsage{
        "digital signature", "key encipherment", "client auth",
    }

    csr := corev1.CertificateSigningRequest{
        TypeMeta: v1.TypeMeta{
            Kind:       "CertificateSigningRequest",
            APIVersion: "certificates.k8s.io/v1",
        },
        ObjectMeta: v1.ObjectMeta{
            Name: UserName + "-csr",
        },
        Spec: corev1.CertificateSigningRequestSpec{
            Request:           csrPemBlock,
            SignerName:        "kubernetes.io/kube-apiserver-client",
            ExpirationSeconds: &seconds,
            Usages:            usages,
        },
        Status: corev1.CertificateSigningRequestStatus{
            Conditions: []corev1.CertificateSigningRequestCondition{
                {
                    Type:   corev1.CertificateApproved,
                    Status: "True",
                },
            },
        },
    }
    return csr
}

Here is the function responsible for creating the CSR resource on the cluster:

func CreateCSR(csr *corev1.CertificateSigningRequest, clientSet *kubernetes.Clientset) {
    _, err := clientSet.CertificatesV1().CertificateSigningRequests().Create(context.Background(), csr, v1.CreateOptions{})
    if err != nil {
        fmt.Printf("error while trying to create CSR: %v\n", err.Error())
        os.Exit(1)
    }
}

Then I try to approve the csr using this function:

func approveCSR(csr *corev1.CertificateSigningRequest, clientSet *kubernetes.Clientset) {
    csr.Status.Conditions = append(csr.Status.Conditions, corev1.CertificateSigningRequestCondition{
        Type:           corev1.CertificateApproved,
        Reason:         "User activation",
        Message:        "This CSR was approved",
        LastUpdateTime: v1.Now(),
    })

    _, err := clientSet.CertificatesV1().CertificateSigningRequests().UpdateApproval(context.Background(), csr.ObjectMeta.Name, csr, v1.UpdateOptions{})
    if err != nil {
        fmt.Printf("error while trying to approve CSR: %v\n", err.Error())
        os.Exit(1)
    }
    fmt.Println("CSR was successfully approved")
}

When doing this I get the following mistake: error while trying to approve CSR: CertificateSigningRequest.certificates.k8s.io "user-csr" is invalid: status.conditions[0].status: Required value exit status 1

So instead of using this function I tried to adjust the CSR struct itself by adding the CertificateSigningRequestStatus field like this:

csr := corev1.CertificateSigningRequest{
        TypeMeta: v1.TypeMeta{
            Kind:       "CertificateSigningRequest",
            APIVersion: "certificates.k8s.io/v1",
        },
        ObjectMeta: v1.ObjectMeta{
            Name: UserName + "-csr",
        },
        Spec: corev1.CertificateSigningRequestSpec{
            Request:           csrPemBlock,
            SignerName:        "kubernetes.io/kube-apiserver-client",
            ExpirationSeconds: &seconds,
            Usages:            usages,
        },
        Status: corev1.CertificateSigningRequestStatus{
            Conditions: []corev1.CertificateSigningRequestCondition{
                {
                    Type:   corev1.CertificateApproved,
                    Status: "True",
                },
            },
            Certificate: nil,
        },
    }

However after this the csr status on the cluster is still pending. I tried adding the same csrPemBlock as in the Spec subresource to Certificate instead of nil, but no positive result. Does anyone know how to approve the Certificate upon creation or after?

2

There are 2 best solutions below

0
GProssliner On

I don't have the concrete answer to your question, but this helps me in such cases:

Can you successfully approve the CSR by kubectl certificate approve csr-name? If yes, I would execute kubectl with log-level 8 (kubectl certificate approve csr-name -v=8).

This forces kubectl to log every api request incl. request and response data. If you have this information, you may be able to see what your go code is missing.

0
Dimsss On

Add Status and set it to corev1.ConditionTrue

import (
    ...
    certsv1 "k8s.io/api/certificates/v1"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    ...
)
csr.Status.Conditions = append(csr.Status.Conditions, 
        certsv1.CertificateSigningRequestCondition{
            Status:             corev1.ConditionTrue,
            Type:               certsv1.CertificateApproved,
            Reason:             "foo-bar,
            Message:            "foo-bar",
            LastTransitionTime: metav1.Now(),
        })