mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-02-03 20:40:26 -05:00
Pod Certificates: Add StubPKCS10Request; migrate in-tree usages to it
This commit is contained in:
parent
285222e92e
commit
a86f377950
7 changed files with 327 additions and 88 deletions
|
|
@ -340,8 +340,7 @@ type PodCertificateRequestSpec struct {
|
|||
// seconds (1 hour). This constraint is enforced by kube-apiserver.
|
||||
MaxExpirationSeconds *int32
|
||||
|
||||
// pkixPublicKey is the PKIX-serialized public key the signer will issue the
|
||||
// certificate to.
|
||||
// The PKIX-serialized public key the signer will issue the certificate to.
|
||||
//
|
||||
// The key must be one of RSA3072, RSA4096, ECDSAP256, ECDSAP384, ECDSAP521,
|
||||
// or ED25519. Note that this list may be expanded in the future.
|
||||
|
|
@ -352,13 +351,20 @@ type PodCertificateRequestSpec struct {
|
|||
// setting a status.conditions entry with a type of "Denied" and a reason of
|
||||
// "UnsupportedKeyType". It may also suggest a key type that it does support
|
||||
// in the message field.
|
||||
//
|
||||
// DEPRECATED: This field is replaced by StubPKCS10Request. If
|
||||
// StubPKCS10Request is set, this field must be empty. Signer
|
||||
// implementations should extract the public key from the StubPKCS10Request
|
||||
// field.
|
||||
//
|
||||
// +optional
|
||||
PKIXPublicKey []byte
|
||||
|
||||
// proofOfPossession proves that the requesting kubelet holds the private
|
||||
// key corresponding to pkixPublicKey.
|
||||
// A proof that the requesting kubelet holds the private key corresponding
|
||||
// to pkixPublicKey.
|
||||
//
|
||||
// It is contructed by signing the ASCII bytes of the pod's UID using
|
||||
// `PKIXPublicKey`.
|
||||
// `pkixPublicKey`.
|
||||
//
|
||||
// kube-apiserver validates the proof of possession during creation of the
|
||||
// PodCertificateRequest.
|
||||
|
|
@ -374,8 +380,40 @@ type PodCertificateRequestSpec struct {
|
|||
// If the key is an ED25519 key, the the signature is as described by the
|
||||
// [ED25519 Specification](https://ed25519.cr.yp.to/) (as implemented by the
|
||||
// golang library crypto/ed25519.Sign).
|
||||
//
|
||||
// DEPRECATED: This field is replaced by StubPKCS10Request. If
|
||||
// StubPKCS10Request is set, this field must be empty.
|
||||
//
|
||||
// +optional
|
||||
ProofOfPossession []byte
|
||||
|
||||
// A PKCS#10 certificate signing request (DER-serialized) generated by
|
||||
// Kubelet using the subject private key.
|
||||
//
|
||||
// Most signer implementations will ignore the contents of the CSR except to
|
||||
// extract the subject public key. The API server automatically verifies the
|
||||
// CSR signature during admission, so the signer does not need to repeat the
|
||||
// verification.
|
||||
//
|
||||
// The subject public key must be one of RSA3072, RSA4096, ECDSAP256,
|
||||
// ECDSAP384, ECDSAP521, or ED25519. Note that this list may be expanded in
|
||||
// the future.
|
||||
//
|
||||
// Signer implementations do not need to support all key types supported by
|
||||
// kube-apiserver and kubelet. If a signer does not support the key type
|
||||
// used for a given PodCertificateRequest, it must deny the request by
|
||||
// setting a status.conditions entry with a type of "Denied" and a reason of
|
||||
// "UnsupportedKeyType". It may also suggest a key type that it does support
|
||||
// in the message field.
|
||||
//
|
||||
// Some CA implementations require that the client (the signer
|
||||
// implementation, in this case) provide a PKCS#10 certificate signing
|
||||
// request, even if the CA only extracts the subject public key from the
|
||||
// request. To enable compatibility with these CAs, Kubelet will generate a
|
||||
// stub PKCS#10 request that the signer implementation can then pass on to
|
||||
// the CA.
|
||||
StubPKCS10Request []byte
|
||||
|
||||
// unverifiedUserAnnotations allow pod authors to pass additional information to
|
||||
// the signer implementation. Kubernetes does not restrict or validate this
|
||||
// metadata in any way.
|
||||
|
|
@ -485,6 +523,9 @@ const (
|
|||
// MaxProofOfPossessionSize is the maximum size permitted for the
|
||||
// ProofOfPossession field.
|
||||
MaxProofOfPossessionSize = 10 * 1024
|
||||
// MaxStubPKCS10RequestSize is the maximum size permitted for the
|
||||
// UnverifiedPKCS10Request field.
|
||||
MaxStubPKCS10RequestSize = 10 * 1024
|
||||
// MaxCertificateChainSize is the maximum size permitted for the
|
||||
// CertificateChain field.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -646,6 +646,26 @@ func ValidatePodCertificateRequestCreate(req *certificates.PodCertificateRequest
|
|||
}
|
||||
}
|
||||
|
||||
// Either (PKIXPublicKey, ProofOfPossession) xor (StubPKCS10Request)
|
||||
// must be set.
|
||||
if len(req.Spec.StubPKCS10Request) != 0 && len(req.Spec.PKIXPublicKey) == 0 && len(req.Spec.ProofOfPossession) == 0 {
|
||||
// Valid, using StubPKCS10Request
|
||||
allErrors = append(allErrors, validateStubPKCS10Request(req)...)
|
||||
return allErrors
|
||||
} else if len(req.Spec.StubPKCS10Request) == 0 && len(req.Spec.PKIXPublicKey) != 0 && len(req.Spec.ProofOfPossession) != 0 {
|
||||
// Valid, using PKIXPublicKey and ProofOfPossession
|
||||
allErrors = append(allErrors, validateDeprecatedPKIXPublicKey(req)...)
|
||||
return allErrors
|
||||
} else {
|
||||
// Invalid, any other combination.
|
||||
allErrors = append(allErrors, field.Invalid(field.NewPath("spec"), field.OmitValueType{}, "exactly one of (stubPKCS10Request) or (pkixPublicKey, proofOfPossession) must be set"))
|
||||
return allErrors
|
||||
}
|
||||
}
|
||||
|
||||
func validateDeprecatedPKIXPublicKey(req *certificates.PodCertificateRequest) field.ErrorList {
|
||||
var allErrors field.ErrorList
|
||||
|
||||
if len(req.Spec.PKIXPublicKey) > certificates.MaxPKIXPublicKeySize {
|
||||
allErrors = append(allErrors, field.TooLong(field.NewPath("spec", "pkixPublicKey"), req.Spec.PKIXPublicKey, certificates.MaxPKIXPublicKeySize))
|
||||
return allErrors
|
||||
|
|
@ -699,6 +719,85 @@ func ValidatePodCertificateRequestCreate(req *certificates.PodCertificateRequest
|
|||
return allErrors
|
||||
}
|
||||
|
||||
var (
|
||||
oidExtensionSubjectAltName = []int{2, 5, 29, 17}
|
||||
)
|
||||
|
||||
func validateStubPKCS10Request(req *certificates.PodCertificateRequest) field.ErrorList {
|
||||
var allErrors field.ErrorList
|
||||
|
||||
if len(req.Spec.StubPKCS10Request) > certificates.MaxStubPKCS10RequestSize {
|
||||
allErrors = append(allErrors, field.TooLong(pkcs10ReqPath, req.Spec.StubPKCS10Request, certificates.MaxStubPKCS10RequestSize))
|
||||
return allErrors
|
||||
}
|
||||
|
||||
pkcs10Req, err := x509.ParseCertificateRequest(req.Spec.StubPKCS10Request)
|
||||
if err != nil {
|
||||
allErrors = append(allErrors, field.Invalid(pkcs10ReqPath, field.OmitValueType{}, "must be a valid PKCS#10 CSR"))
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// Check key type and parameters
|
||||
switch pkcs10Pub := pkcs10Req.PublicKey.(type) {
|
||||
case ed25519.PublicKey:
|
||||
// ed25519 has no key configuration to check
|
||||
case *ecdsa.PublicKey:
|
||||
if pkcs10Pub.Curve != elliptic.P256() && pkcs10Pub.Curve != elliptic.P384() && pkcs10Pub.Curve != elliptic.P521() {
|
||||
allErrors = append(allErrors, field.Invalid(pkcs10ReqPath, "curve "+pkcs10Pub.Curve.Params().Name, "elliptic public keys must use curve P256, P384, or P521"))
|
||||
return allErrors
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
if pkcs10Pub.Size()*8 != 3072 && pkcs10Pub.Size()*8 != 4096 {
|
||||
allErrors = append(allErrors, field.Invalid(pkcs10ReqPath, fmt.Sprintf("%d-bit modulus", pkcs10Pub.Size()*8), "RSA keys must have modulus size 3072 or 4096"))
|
||||
return allErrors
|
||||
}
|
||||
default:
|
||||
allErrors = append(allErrors, field.Invalid(pkcs10ReqPath, field.OmitValueType{}, "unknown public key type; supported types are Ed25519, ECDSA, and RSA"))
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// Check that the request is empty, except for DNS or IP SANs.
|
||||
if len(pkcs10Req.Subject.Country) != 0 ||
|
||||
len(pkcs10Req.Subject.Organization) != 0 ||
|
||||
len(pkcs10Req.Subject.OrganizationalUnit) != 0 ||
|
||||
len(pkcs10Req.Subject.Locality) != 0 ||
|
||||
len(pkcs10Req.Subject.Province) != 0 ||
|
||||
len(pkcs10Req.Subject.StreetAddress) != 0 ||
|
||||
len(pkcs10Req.Subject.PostalCode) != 0 ||
|
||||
len(pkcs10Req.Subject.SerialNumber) != 0 ||
|
||||
len(pkcs10Req.Subject.CommonName) != 0 ||
|
||||
len(pkcs10Req.Subject.Names) != 0 {
|
||||
allErrors = append(allErrors, field.Invalid(pkcs10ReqPath, field.OmitValueType{}, "PKCS#10 request must have empty subject"))
|
||||
return allErrors
|
||||
}
|
||||
if len(pkcs10Req.EmailAddresses) != 0 {
|
||||
allErrors = append(allErrors, field.Invalid(pkcs10ReqPath, field.OmitValueType{}, "PKCS#10 request must not contain EmailAddress subject alternate names"))
|
||||
return allErrors
|
||||
}
|
||||
if len(pkcs10Req.URIs) != 0 {
|
||||
allErrors = append(allErrors, field.Invalid(pkcs10ReqPath, field.OmitValueType{}, "PKCS#10 request must not contain URI subject alternate names"))
|
||||
return allErrors
|
||||
}
|
||||
|
||||
for i := range pkcs10Req.Extensions {
|
||||
if pkcs10Req.Extensions[i].Id.Equal(oidExtensionSubjectAltName) {
|
||||
// If the SubjectAlternateName contained non-IP or non-DNS content,
|
||||
// then EmailAddresses or URIs would have been non-empty above.
|
||||
} else {
|
||||
// All other extensions are forbidden.
|
||||
allErrors = append(allErrors, field.Invalid(pkcs10ReqPath, field.OmitValueType{}, "PKCS#10 request may not contain arbitrary extensions"))
|
||||
return allErrors
|
||||
}
|
||||
}
|
||||
|
||||
if err := pkcs10Req.CheckSignature(); err != nil {
|
||||
allErrors = append(allErrors, field.Invalid(pkcs10ReqPath, field.OmitValueType{}, "invalid signature"))
|
||||
return allErrors
|
||||
}
|
||||
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func hashBytes(in []byte) []byte {
|
||||
out := sha256.Sum256(in)
|
||||
return out[:]
|
||||
|
|
@ -707,6 +806,7 @@ func hashBytes(in []byte) []byte {
|
|||
var (
|
||||
pkixPath = field.NewPath("spec", "pkixPublicKey")
|
||||
popPath = field.NewPath("spec", "proofOfPossession")
|
||||
pkcs10ReqPath = field.NewPath("spec", "stubPKCS10Request")
|
||||
certChainPath = field.NewPath("status", "certificateChain")
|
||||
notBeforePath = field.NewPath("status", "notBefore")
|
||||
notAfterPath = field.NewPath("status", "notAfter")
|
||||
|
|
@ -826,12 +926,24 @@ func ValidatePodCertificateRequestStatusUpdate(newReq, oldReq *certificates.PodC
|
|||
}
|
||||
}
|
||||
|
||||
// Was the certificate issued to the public key in the spec?
|
||||
wantPKAny, err := x509.ParsePKIXPublicKey(oldReq.Spec.PKIXPublicKey)
|
||||
if err != nil {
|
||||
allErrors = append(allErrors, field.Invalid(pkixPath, oldReq.Spec.PKIXPublicKey, "must be a valid PKIX-serialized public key"))
|
||||
return allErrors
|
||||
// Get the public key from either StubPKCS10Request or PKIXPublicKey.
|
||||
var wantPKAny crypto.PublicKey
|
||||
if len(oldReq.Spec.StubPKCS10Request) != 0 {
|
||||
pkcs10Req, err := x509.ParseCertificateRequest(oldReq.Spec.StubPKCS10Request)
|
||||
if err != nil {
|
||||
allErrors = append(allErrors, field.Invalid(pkcs10ReqPath, field.OmitValueType{}, "must be a valid PKCS#10 CSR"))
|
||||
return allErrors
|
||||
}
|
||||
wantPKAny = pkcs10Req.PublicKey
|
||||
} else {
|
||||
wantPKAny, err = x509.ParsePKIXPublicKey(oldReq.Spec.PKIXPublicKey)
|
||||
if err != nil {
|
||||
allErrors = append(allErrors, field.Invalid(pkixPath, field.OmitValueType{}, "must be a valid PKIX-serialized public key"))
|
||||
return allErrors
|
||||
}
|
||||
}
|
||||
|
||||
// Was the certificate issued to the public key in the spec?
|
||||
switch wantPK := wantPKAny.(type) {
|
||||
case ed25519.PublicKey:
|
||||
if !wantPK.Equal(leafCert.PublicKey) {
|
||||
|
|
|
|||
|
|
@ -1610,9 +1610,10 @@ func TestValidateClusterTrustBundleUpdate(t *testing.T) {
|
|||
|
||||
func TestValidatePodCertificateRequestCreate(t *testing.T) {
|
||||
podUID1 := "pod-uid-1"
|
||||
_, _, ed25519PubPKIX1, ed25519Proof1 := mustMakeEd25519KeyAndProof(t, []byte(podUID1))
|
||||
_, _, ed25519PubPKIX2, ed25519Proof2 := mustMakeEd25519KeyAndProof(t, []byte("other-value"))
|
||||
_, _, _, ed25519Proof3 := mustMakeEd25519KeyAndProof(t, []byte(podUID1))
|
||||
_, _, ed25519PubPKIX1, ed25519Proof1, ed25519CSR1 := mustMakeEd25519KeyAndProof(t, []byte(podUID1), []string{})
|
||||
_, _, ed25519PubPKIX2, ed25519Proof2, _ := mustMakeEd25519KeyAndProof(t, []byte("other-value"), []string{})
|
||||
_, _, _, ed25519Proof3, _ := mustMakeEd25519KeyAndProof(t, []byte(podUID1), []string{})
|
||||
_, _, _, _, ed25519CSR4 := mustMakeEd25519KeyAndProof(t, []byte(podUID1), []string{"example.com", "foo.example.example"})
|
||||
_, _, ecdsaP224PubPKIX1, ecdsaP224Proof1 := mustMakeECDSAKeyAndProof(t, elliptic.P224(), []byte(podUID1))
|
||||
_, _, ecdsaP256PubPKIX1, ecdsaP256Proof1 := mustMakeECDSAKeyAndProof(t, elliptic.P256(), []byte(podUID1))
|
||||
_, _, ecdsaP384PubPKIX1, ecdsaP384Proof1 := mustMakeECDSAKeyAndProof(t, elliptic.P384(), []byte(podUID1))
|
||||
|
|
@ -1624,7 +1625,7 @@ func TestValidatePodCertificateRequestCreate(t *testing.T) {
|
|||
_, _, rsaWrongProofPKIX, rsaWrongProof := mustMakeRSAKeyAndProof(t, 3072, []byte("other-value"))
|
||||
|
||||
podUIDEmpty := ""
|
||||
_, _, pubPKIXEmpty, proofEmpty := mustMakeEd25519KeyAndProof(t, []byte(podUIDEmpty))
|
||||
_, _, pubPKIXEmpty, proofEmpty, _ := mustMakeEd25519KeyAndProof(t, []byte(podUIDEmpty), []string{})
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
|
|
@ -1653,6 +1654,73 @@ func TestValidatePodCertificateRequestCreate(t *testing.T) {
|
|||
},
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
description: "valid Ed25519 PCR (using PKCS#10)",
|
||||
pcr: &capi.PodCertificateRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "foo",
|
||||
Name: "bar",
|
||||
},
|
||||
Spec: capi.PodCertificateRequestSpec{
|
||||
SignerName: "foo.com/abc",
|
||||
PodName: "pod-1",
|
||||
PodUID: types.UID(podUID1),
|
||||
ServiceAccountName: "sa-1",
|
||||
ServiceAccountUID: "sa-uid-1",
|
||||
NodeName: "node-1",
|
||||
NodeUID: "node-uid-1",
|
||||
MaxExpirationSeconds: ptr.To[int32](86400),
|
||||
StubPKCS10Request: ed25519CSR1,
|
||||
},
|
||||
},
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
description: "valid Ed25519 PCR (using PKCS#10, with DNS SANs)",
|
||||
pcr: &capi.PodCertificateRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "foo",
|
||||
Name: "bar",
|
||||
},
|
||||
Spec: capi.PodCertificateRequestSpec{
|
||||
SignerName: "foo.com/abc",
|
||||
PodName: "pod-1",
|
||||
PodUID: types.UID(podUID1),
|
||||
ServiceAccountName: "sa-1",
|
||||
ServiceAccountUID: "sa-uid-1",
|
||||
NodeName: "node-1",
|
||||
NodeUID: "node-uid-1",
|
||||
MaxExpirationSeconds: ptr.To[int32](86400),
|
||||
StubPKCS10Request: ed25519CSR4,
|
||||
},
|
||||
},
|
||||
wantErrors: nil,
|
||||
},
|
||||
{
|
||||
description: "invalid Ed25519 PCR (both StubPKCS10Request and PKIXPublicKey set)",
|
||||
pcr: &capi.PodCertificateRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "foo",
|
||||
Name: "bar",
|
||||
},
|
||||
Spec: capi.PodCertificateRequestSpec{
|
||||
SignerName: "foo.com/abc",
|
||||
PodName: "pod-1",
|
||||
PodUID: types.UID(podUID1),
|
||||
ServiceAccountName: "sa-1",
|
||||
ServiceAccountUID: "sa-uid-1",
|
||||
NodeName: "node-1",
|
||||
NodeUID: "node-uid-1",
|
||||
MaxExpirationSeconds: ptr.To[int32](86400),
|
||||
PKIXPublicKey: ed25519PubPKIX1,
|
||||
ProofOfPossession: ed25519Proof1,
|
||||
StubPKCS10Request: ed25519CSR1,
|
||||
},
|
||||
},
|
||||
wantErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec"), field.OmitValueType{}, "exactly one of (stubPKCS10Request) or (pkixPublicKey, proofOfPossession) must be set"),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "invalid Ed25519 proof of possession (correct key signed wrong message)",
|
||||
pcr: &capi.PodCertificateRequest{
|
||||
|
|
@ -2187,7 +2255,7 @@ func TestValidatePodCertificateRequestCreate(t *testing.T) {
|
|||
NodeUID: "node-uid-1",
|
||||
MaxExpirationSeconds: ptr.To[int32](86400),
|
||||
PKIXPublicKey: make([]byte, capi.MaxPKIXPublicKeySize+1),
|
||||
ProofOfPossession: []byte{},
|
||||
ProofOfPossession: []byte("abc"),
|
||||
},
|
||||
},
|
||||
wantErrors: field.ErrorList{
|
||||
|
|
@ -2210,7 +2278,7 @@ func TestValidatePodCertificateRequestCreate(t *testing.T) {
|
|||
NodeName: "node-1",
|
||||
NodeUID: "node-uid-1",
|
||||
MaxExpirationSeconds: ptr.To[int32](86400),
|
||||
PKIXPublicKey: []byte{},
|
||||
PKIXPublicKey: ed25519PubPKIX1,
|
||||
ProofOfPossession: make([]byte, capi.MaxProofOfPossessionSize+1),
|
||||
},
|
||||
},
|
||||
|
|
@ -2308,7 +2376,7 @@ func TestValidatePodCertificateRequestCreate(t *testing.T) {
|
|||
|
||||
func TestValidatePodCertificateRequestUpdate(t *testing.T) {
|
||||
podUID1 := "pod-uid-1"
|
||||
_, _, pubPKIX1, proof1 := mustMakeEd25519KeyAndProof(t, []byte(podUID1))
|
||||
_, _, pubPKIX1, proof1, _ := mustMakeEd25519KeyAndProof(t, []byte(podUID1), []string{})
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
|
|
@ -2396,7 +2464,7 @@ func TestValidatePodCertificateRequestStatusUpdate(t *testing.T) {
|
|||
intermediateCACertDER, intermediateCAPrivKey := mustMakeIntermediateCA(t, caCertDER, caPrivKey)
|
||||
|
||||
podUID1 := "pod-uid-1"
|
||||
_, pub1, pubPKIX1, proof1 := mustMakeEd25519KeyAndProof(t, []byte(podUID1))
|
||||
_, pub1, pubPKIX1, proof1, _ := mustMakeEd25519KeyAndProof(t, []byte(podUID1), []string{})
|
||||
|
||||
pod1Cert1 := mustSignCertForPublicKey(t, 24*time.Hour, pub1, caCertDER, caPrivKey, false, "", "")
|
||||
pod1Cert2 := mustSignCertForPublicKey(t, 18*time.Hour, pub1, caCertDER, caPrivKey, false, "", "")
|
||||
|
|
@ -4438,7 +4506,7 @@ func mustParseTime(t *testing.T, stamp string) time.Time {
|
|||
return got
|
||||
}
|
||||
|
||||
func mustMakeEd25519KeyAndProof(t *testing.T, toBeSigned []byte) (ed25519.PrivateKey, ed25519.PublicKey, []byte, []byte) {
|
||||
func mustMakeEd25519KeyAndProof(t *testing.T, toBeSigned []byte, pkcs10DNSSANS []string) (ed25519.PrivateKey, ed25519.PublicKey, []byte, []byte, []byte) {
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while generating ed25519 key: %v", err)
|
||||
|
|
@ -4448,7 +4516,13 @@ func mustMakeEd25519KeyAndProof(t *testing.T, toBeSigned []byte) (ed25519.Privat
|
|||
t.Fatalf("Error while marshaling PKIX public key: %v", err)
|
||||
}
|
||||
sig := ed25519.Sign(priv, toBeSigned)
|
||||
return priv, pub, pubPKIX, sig
|
||||
|
||||
pkcs10DER, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{DNSNames: pkcs10DNSSANS}, priv)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while creating PKCS#10 certificate signing request: %v", err)
|
||||
}
|
||||
|
||||
return priv, pub, pubPKIX, sig, pkcs10DER
|
||||
}
|
||||
|
||||
func mustMakeECDSAKeyAndProof(t *testing.T, curve elliptic.Curve, toBeSigned []byte) (*ecdsa.PrivateKey, *ecdsa.PublicKey, []byte, []byte) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import (
|
|||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
|
|
@ -742,18 +741,14 @@ func (m *IssuingManager) createPodCertificateRequest(
|
|||
podName string, podUID types.UID,
|
||||
serviceAccountName string, serviceAccountUID types.UID,
|
||||
nodeName types.NodeName, nodeUID types.UID,
|
||||
signerName, keyType string, maxExpirationSeconds *int32, userAnnotations map[string]string) ([]byte, *certificatesv1beta1.PodCertificateRequest, error) {
|
||||
|
||||
privateKey, publicKey, proof, err := generateKeyAndProof(keyType, []byte(podUID))
|
||||
signerName, keyType string, maxExpirationSeconds *int32,
|
||||
userAnnotations map[string]string,
|
||||
) ([]byte, *certificatesv1beta1.PodCertificateRequest, error) {
|
||||
privateKey, pkcs10Req, err := generateKeyAndProof(keyType)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("while generating keypair: %w", err)
|
||||
}
|
||||
|
||||
pkixPublicKey, err := x509.MarshalPKIXPublicKey(publicKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("while marshaling public key: %w", err)
|
||||
}
|
||||
|
||||
keyPEM, err := pemEncodeKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("while PEM-encoding private key: %w", err)
|
||||
|
|
@ -781,8 +776,7 @@ func (m *IssuingManager) createPodCertificateRequest(
|
|||
NodeName: nodeName,
|
||||
NodeUID: nodeUID,
|
||||
MaxExpirationSeconds: maxExpirationSeconds,
|
||||
PKIXPublicKey: pkixPublicKey,
|
||||
ProofOfPossession: proof,
|
||||
StubPKCS10Request: pkcs10Req,
|
||||
UnverifiedUserAnnotations: userAnnotations,
|
||||
},
|
||||
}
|
||||
|
|
@ -877,73 +871,57 @@ func (m *IssuingManager) MetricReport() *MetricReport {
|
|||
return report
|
||||
}
|
||||
|
||||
func hashBytes(in []byte) []byte {
|
||||
out := sha256.Sum256(in)
|
||||
return out[:]
|
||||
}
|
||||
func generateKeyAndProof(keyType string) (crypto.PrivateKey, []byte, error) {
|
||||
var privKey crypto.PrivateKey
|
||||
|
||||
func generateKeyAndProof(keyType string, toBeSigned []byte) (privKey crypto.PrivateKey, pubKey crypto.PublicKey, sig []byte, err error) {
|
||||
switch keyType {
|
||||
case "RSA3072":
|
||||
key, err := rsa.GenerateKey(rand.Reader, 3072)
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 3072)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while generating RSA 3072 key: %w", err)
|
||||
return nil, nil, fmt.Errorf("while generating RSA 3072 key: %w", err)
|
||||
}
|
||||
sig, err := rsa.SignPSS(rand.Reader, key, crypto.SHA256, hashBytes(toBeSigned), nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while signing proof: %w", err)
|
||||
}
|
||||
return key, &key.PublicKey, sig, nil
|
||||
privKey = priv
|
||||
case "RSA4096":
|
||||
key, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while generating RSA 4096 key: %w", err)
|
||||
return nil, nil, fmt.Errorf("while generating RSA 4096 key: %w", err)
|
||||
}
|
||||
sig, err := rsa.SignPSS(rand.Reader, key, crypto.SHA256, hashBytes(toBeSigned), nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while signing proof: %w", err)
|
||||
}
|
||||
return key, &key.PublicKey, sig, nil
|
||||
privKey = priv
|
||||
case "ECDSAP256":
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while generating ECDSA P256 key: %w", err)
|
||||
return nil, nil, fmt.Errorf("while generating ECDSA P256 key: %w", err)
|
||||
}
|
||||
sig, err := ecdsa.SignASN1(rand.Reader, key, hashBytes(toBeSigned))
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while signing proof: %w", err)
|
||||
}
|
||||
return key, &key.PublicKey, sig, nil
|
||||
privKey = priv
|
||||
case "ECDSAP384":
|
||||
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while generating ECDSA P384 key: %w", err)
|
||||
return nil, nil, fmt.Errorf("while generating ECDSA P384 key: %w", err)
|
||||
}
|
||||
sig, err := ecdsa.SignASN1(rand.Reader, key, hashBytes(toBeSigned))
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while signing proof: %w", err)
|
||||
}
|
||||
return key, &key.PublicKey, sig, nil
|
||||
privKey = priv
|
||||
case "ECDSAP521":
|
||||
key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while generating ECDSA P521 key: %w", err)
|
||||
return nil, nil, fmt.Errorf("while generating ECDSA P521 key: %w", err)
|
||||
}
|
||||
sig, err := ecdsa.SignASN1(rand.Reader, key, hashBytes(toBeSigned))
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while signing proof: %w", err)
|
||||
}
|
||||
return key, &key.PublicKey, sig, nil
|
||||
privKey = priv
|
||||
case "ED25519":
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
_, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("while generating Ed25519 key: %w", err)
|
||||
return nil, nil, fmt.Errorf("while generating Ed25519 key: %w", err)
|
||||
}
|
||||
sig := ed25519.Sign(priv, toBeSigned)
|
||||
return priv, pub, sig, nil
|
||||
privKey = priv
|
||||
default:
|
||||
return nil, nil, nil, fmt.Errorf("unknown key type %q", keyType)
|
||||
return nil, nil, fmt.Errorf("unknown key type %q", keyType)
|
||||
}
|
||||
|
||||
tmpl := &x509.CertificateRequest{}
|
||||
pkcs10Req, err := x509.CreateCertificateRequest(rand.Reader, tmpl, privKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("while generating stub PKCS#10 request: %w", err)
|
||||
}
|
||||
|
||||
return privKey, pkcs10Req, nil
|
||||
}
|
||||
|
||||
func pemEncodeKey(key crypto.PrivateKey) ([]byte, error) {
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ func NewStatusStrategy(strategy *Strategy, authorizer authorizer.Authorizer, clo
|
|||
// and should not be modified by the user.
|
||||
func (s *StatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
||||
fields := map[fieldpath.APIVersion]*fieldpath.Set{
|
||||
"certificates.k8s.io/v1alpha1": fieldpath.NewSet(
|
||||
"certificates.k8s.io/v1beta1": fieldpath.NewSet(
|
||||
fieldpath.MakePathOrDie("spec"),
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -438,8 +438,7 @@ type PodCertificateRequestSpec struct {
|
|||
// +default=86400
|
||||
MaxExpirationSeconds *int32 `json:"maxExpirationSeconds,omitempty" protobuf:"varint,8,opt,name=maxExpirationSeconds"`
|
||||
|
||||
// pkixPublicKey is the PKIX-serialized public key the signer will issue the
|
||||
// certificate to.
|
||||
// The PKIX-serialized public key the signer will issue the certificate to.
|
||||
//
|
||||
// The key must be one of RSA3072, RSA4096, ECDSAP256, ECDSAP384, ECDSAP521,
|
||||
// or ED25519. Note that this list may be expanded in the future.
|
||||
|
|
@ -451,11 +450,16 @@ type PodCertificateRequestSpec struct {
|
|||
// "UnsupportedKeyType". It may also suggest a key type that it does support
|
||||
// in the message field.
|
||||
//
|
||||
// +required
|
||||
// DEPRECATED: This field is replaced by StubPKCS10Request. If
|
||||
// StubPKCS10Request is set, this field must be empty. Signer
|
||||
// implementations should extract the public key from the StubPKCS10Request
|
||||
// field.
|
||||
//
|
||||
// +optional
|
||||
PKIXPublicKey []byte `json:"pkixPublicKey" protobuf:"bytes,9,opt,name=pkixPublicKey"`
|
||||
|
||||
// proofOfPossession proves that the requesting kubelet holds the private
|
||||
// key corresponding to pkixPublicKey.
|
||||
// A proof that the requesting kubelet holds the private key corresponding
|
||||
// to pkixPublicKey.
|
||||
//
|
||||
// It is contructed by signing the ASCII bytes of the pod's UID using
|
||||
// `pkixPublicKey`.
|
||||
|
|
@ -472,12 +476,42 @@ type PodCertificateRequestSpec struct {
|
|||
// golang library function crypto/ecdsa.SignASN1)
|
||||
//
|
||||
// If the key is an ED25519 key, the the signature is as described by the
|
||||
// [ED25519 Specification](https://ed25519.cr.yp.to/) (as implemented by
|
||||
// the golang library crypto/ed25519.Sign).
|
||||
// [ED25519 Specification](https://ed25519.cr.yp.to/) (as implemented by the
|
||||
// golang library crypto/ed25519.Sign).
|
||||
//
|
||||
// +required
|
||||
// DEPRECATED: This field is replaced by StubPKCS10Request. If
|
||||
// StubPKCS10Request is set, this field must be empty.
|
||||
//
|
||||
// +optional
|
||||
ProofOfPossession []byte `json:"proofOfPossession" protobuf:"bytes,10,opt,name=proofOfPossession"`
|
||||
|
||||
// A PKCS#10 certificate signing request (DER-serialized) generated by
|
||||
// Kubelet using the subject private key.
|
||||
//
|
||||
// Most signer implementations will ignore the contents of the CSR except to
|
||||
// extract the subject public key. The API server automatically verifies the
|
||||
// CSR signature during admission, so the signer does not need to repeat the
|
||||
// verification.
|
||||
//
|
||||
// The subject public key must be one of RSA3072, RSA4096, ECDSAP256,
|
||||
// ECDSAP384, ECDSAP521, or ED25519. Note that this list may be expanded in
|
||||
// the future.
|
||||
//
|
||||
// Signer implementations do not need to support all key types supported by
|
||||
// kube-apiserver and kubelet. If a signer does not support the key type
|
||||
// used for a given PodCertificateRequest, it must deny the request by
|
||||
// setting a status.conditions entry with a type of "Denied" and a reason of
|
||||
// "UnsupportedKeyType". It may also suggest a key type that it does support
|
||||
// in the message field.
|
||||
//
|
||||
// Some CA implementations require that the client (the signer
|
||||
// implementation, in this case) provide a PKCS#10 certificate signing
|
||||
// request, even if the CA only extracts the subject public key from the
|
||||
// request. To enable compatibility with these CAs, Kubelet will generate a
|
||||
// stub PKCS#10 request that the signer implementation can then pass on to
|
||||
// the CA.
|
||||
StubPKCS10Request []byte `json:"stubPKCS10Request" protobuf:"bytes,12,opt,name=stubPKCS10Request"`
|
||||
|
||||
// unverifiedUserAnnotations allow pod authors to pass additional information to
|
||||
// the signer implementation. Kubernetes does not restrict or validate this
|
||||
// metadata in any way.
|
||||
|
|
|
|||
|
|
@ -229,9 +229,9 @@ func (c *Controller) handlePCR(ctx context.Context, pcr *certsv1beta1.PodCertifi
|
|||
return nil
|
||||
}
|
||||
|
||||
subjectPublicKey, err := x509.ParsePKIXPublicKey(pcr.Spec.PKIXPublicKey)
|
||||
req, err := x509.ParseCertificateRequest(pcr.Spec.StubPKCS10Request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while parsing subject public key: %w", err)
|
||||
return fmt.Errorf("while parsing PKCS#10 request: %w", err)
|
||||
}
|
||||
|
||||
// If our signer had an opinion on which key types were allowable, it would
|
||||
|
|
@ -276,7 +276,7 @@ func (c *Controller) handlePCR(ctx context.Context, pcr *certsv1beta1.PodCertifi
|
|||
return fmt.Errorf("while parsing signing certificate: %w", err)
|
||||
}
|
||||
|
||||
subjectCertDER, err := x509.CreateCertificate(rand.Reader, template, signingCert, subjectPublicKey, c.caKeys[len(c.caKeys)-1])
|
||||
subjectCertDER, err := x509.CreateCertificate(rand.Reader, template, signingCert, req.PublicKey, c.caKeys[len(c.caKeys)-1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("while signing subject cert: %w", err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue