Skip to content

Commit

Permalink
Add missing signature to CSR requests
Browse files Browse the repository at this point in the history
The cluster can be configured to automatically approve certificate
sign requests that are issued when refeshing cluster certificates.

However, k8sd rejects the CSR requests since it expects them to
include a signature that is currently missing.

We'll address the problem by adding the missing CSR signature.

Note that the CSR signature is passed through k8s annotations and
thus needs to be base64 encoded. We're updating the unit tests
accordingly.
  • Loading branch information
petrutlucian94 committed Oct 30, 2024
1 parent 9c6083c commit 2cd7f6b
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 5 deletions.
34 changes: 32 additions & 2 deletions src/k8s/pkg/k8sd/api/certificates_refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package api

import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509/pkix"
"encoding/base64"
"fmt"
"math"
"math/rand"
"math/big"
"net"
"net/http"
"path/filepath"
Expand All @@ -29,7 +33,11 @@ import (
)

func (e *Endpoints) postRefreshCertsPlan(s state.State, r *http.Request) response.Response {
seed := rand.Intn(math.MaxInt)
seedBigInt, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt))
if err != nil {
return response.InternalError(fmt.Errorf("failed to generate seed: %w", err))
}
seed := int(seedBigInt.Int64())

snap := e.provider.Snap()
isWorker, err := snaputil.IsWorker(snap)
Expand Down Expand Up @@ -216,6 +224,11 @@ func refreshCertsRunWorker(s state.State, r *http.Request, snap snap.Snap) respo
certificates.CACert = clusterConfig.Certificates.GetCACert()
certificates.ClientCACert = clusterConfig.Certificates.GetClientCACert()

k8sdPublicKey, err := pkiutil.LoadRSAPublicKey(clusterConfig.Certificates.GetK8sdPublicKey())
if err != nil {
return response.InternalError(fmt.Errorf("failed to load k8sd public key, error: %w", err))
}

g, ctx := errgroup.WithContext(r.Context())

for _, csr := range []struct {
Expand Down Expand Up @@ -272,9 +285,26 @@ func refreshCertsRunWorker(s state.State, r *http.Request, snap snap.Snap) respo
return fmt.Errorf("failed to generate CSR for %s: %w", csr.name, err)
}

// Obtain the SHA256 sum of the CSR request.
hash := sha256.New()
_, err = hash.Write([]byte(csrPEM))
if err != nil {
return fmt.Errorf("failed to checksum CSR %s, err: %w", csr.name, err)
}

signature, err := rsa.EncryptPKCS1v15(rand.Reader, k8sdPublicKey, hash.Sum(nil))
if err != nil {
return fmt.Errorf("failed to sign CSR %s, err: %w", csr.name, err)
}
signatureB64 := base64.StdEncoding.EncodeToString(signature)

if _, err = client.CertificatesV1().CertificateSigningRequests().Create(ctx, &certv1.CertificateSigningRequest{
ObjectMeta: metav1.ObjectMeta{
Name: csr.name,
Annotations: map[string]string{
"k8sd.io/signature": signatureB64,
"k8sd.io/node": snap.Hostname(),
},
},
Spec: certv1.CertificateSigningRequestSpec{
Request: []byte(csrPEM),
Expand Down
8 changes: 7 additions & 1 deletion src/k8s/pkg/k8sd/controllers/csrsigning/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"fmt"

"github.com/canonical/k8s/pkg/utils"
Expand All @@ -21,7 +22,12 @@ func validateCSR(obj *certv1.CertificateSigningRequest, priv *rsa.PrivateKey) er
return fmt.Errorf("failed to parse x509 certificate request: %w", err)
}

encryptedSignature := obj.Annotations["k8sd.io/signature"]
encryptedSignatureB64 := obj.Annotations["k8sd.io/signature"]
encryptedSignature, err := base64.StdEncoding.DecodeString(encryptedSignatureB64)
if err != nil {
return fmt.Errorf("failed to decode b64 signature: %w", err)
}

signature, err := rsa.DecryptPKCS1v15(nil, priv, []byte(encryptedSignature))
if err != nil {
return fmt.Errorf("failed to decrypt signature: %w", err)
Expand Down
5 changes: 3 additions & 2 deletions src/k8s/pkg/k8sd/controllers/csrsigning/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"crypto/x509/pkix"
"encoding/base64"
"testing"

pkiutil "github.com/canonical/k8s/pkg/utils/pki"
Expand Down Expand Up @@ -93,7 +94,7 @@ func TestValidateCSREncryption(t *testing.T) {
},
},
expectErr: true,
expectErrMessage: "failed to decrypt signature",
expectErrMessage: "failed to decode b64 signature",
},
{
name: "Missing Signature",
Expand Down Expand Up @@ -219,5 +220,5 @@ func mustCreateEncryptedSignature(g Gomega, pub *rsa.PublicKey, csrPEM string) s
signature, err := rsa.EncryptPKCS1v15(rand.Reader, pub, hash.Sum(nil))
g.Expect(err).NotTo(HaveOccurred())

return string(signature)
return base64.StdEncoding.EncodeToString(signature)
}

0 comments on commit 2cd7f6b

Please sign in to comment.