Merge pull request #111061 from pacoxu/key-encipherment-optional

modify the signing/approving controller to tolerate either set of usages for kubelet client and serving certificates
This commit is contained in:
Kubernetes Prow Robot
2022-08-02 18:55:51 -07:00
committed by GitHub
7 changed files with 191 additions and 39 deletions

View File

@@ -50,16 +50,22 @@ var (
uriSANNotAllowedErr = fmt.Errorf("URI subjectAltNames are not allowed")
)
var kubeletServingRequiredUsages = sets.NewString(
string(UsageDigitalSignature),
string(UsageKeyEncipherment),
string(UsageServerAuth),
var (
kubeletServingRequiredUsages = sets.NewString(
string(UsageDigitalSignature),
string(UsageKeyEncipherment),
string(UsageServerAuth),
)
kubeletServingRequiredUsagesNoRSA = sets.NewString(
string(UsageDigitalSignature),
string(UsageServerAuth),
)
)
func IsKubeletServingCSR(req *x509.CertificateRequest, usages sets.String) bool {
return ValidateKubeletServingCSR(req, usages) == nil
func IsKubeletServingCSR(req *x509.CertificateRequest, usages sets.String, allowOmittingUsageKeyEncipherment bool) bool {
return ValidateKubeletServingCSR(req, usages, allowOmittingUsageKeyEncipherment) == nil
}
func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages sets.String) error {
func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages sets.String, allowOmittingUsageKeyEncipherment bool) error {
if !reflect.DeepEqual([]string{"system:nodes"}, req.Subject.Organization) {
return organizationNotSystemNodesErr
}
@@ -76,8 +82,14 @@ func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages sets.String)
return uriSANNotAllowedErr
}
if !kubeletServingRequiredUsages.Equal(usages) {
return fmt.Errorf("usages did not match %v", kubeletServingRequiredUsages.List())
if allowOmittingUsageKeyEncipherment {
if !kubeletServingRequiredUsages.Equal(usages) && !kubeletServingRequiredUsagesNoRSA.Equal(usages) {
return fmt.Errorf("usages did not match %v", kubeletServingRequiredUsages.List())
}
} else {
if !kubeletServingRequiredUsages.Equal(usages) {
return fmt.Errorf("usages did not match %v", kubeletServingRequiredUsages.List())
}
}
if !strings.HasPrefix(req.Subject.CommonName, "system:node:") {
@@ -87,16 +99,22 @@ func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages sets.String)
return nil
}
var kubeletClientRequiredUsages = sets.NewString(
string(UsageDigitalSignature),
string(UsageKeyEncipherment),
string(UsageClientAuth),
var (
kubeletClientRequiredUsagesNoRSA = sets.NewString(
string(UsageDigitalSignature),
string(UsageClientAuth),
)
kubeletClientRequiredUsages = sets.NewString(
string(UsageDigitalSignature),
string(UsageKeyEncipherment),
string(UsageClientAuth),
)
)
func IsKubeletClientCSR(req *x509.CertificateRequest, usages sets.String) bool {
return ValidateKubeletClientCSR(req, usages) == nil
func IsKubeletClientCSR(req *x509.CertificateRequest, usages sets.String, allowOmittingUsageKeyEncipherment bool) bool {
return ValidateKubeletClientCSR(req, usages, allowOmittingUsageKeyEncipherment) == nil
}
func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages sets.String) error {
func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages sets.String, allowOmittingUsageKeyEncipherment bool) error {
if !reflect.DeepEqual([]string{"system:nodes"}, req.Subject.Organization) {
return organizationNotSystemNodesErr
}
@@ -118,8 +136,14 @@ func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages sets.String)
return commonNameNotSystemNode
}
if !kubeletClientRequiredUsages.Equal(usages) {
return fmt.Errorf("usages did not match %v", kubeletClientRequiredUsages.List())
if allowOmittingUsageKeyEncipherment {
if !kubeletClientRequiredUsages.Equal(usages) && !kubeletClientRequiredUsagesNoRSA.Equal(usages) {
return fmt.Errorf("usages did not match %v", kubeletClientRequiredUsages.List())
}
} else {
if !kubeletClientRequiredUsages.Equal(usages) {
return fmt.Errorf("usages did not match %v", kubeletClientRequiredUsages.List())
}
}
return nil

View File

@@ -56,27 +56,27 @@ func DefaultSignerNameFromSpec(obj *certificatesv1beta1.CertificateSigningReques
// Set the signerName to 'legacy-unknown' as the CSR could not be
// recognised.
return certificatesv1beta1.LegacyUnknownSignerName
case IsKubeletClientCSR(csr, obj.Usages):
case IsKubeletClientCSR(csr, obj.Usages, false):
return certificatesv1beta1.KubeAPIServerClientKubeletSignerName
case IsKubeletServingCSR(csr, obj.Usages):
case IsKubeletServingCSR(csr, obj.Usages, false):
return certificatesv1beta1.KubeletServingSignerName
default:
return certificatesv1beta1.LegacyUnknownSignerName
}
}
func IsKubeletServingCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage) bool {
return certificates.IsKubeletServingCSR(req, usagesToSet(usages))
func IsKubeletServingCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage, allowOmittingUsageKeyEncipherment bool) bool {
return certificates.IsKubeletServingCSR(req, usagesToSet(usages), allowOmittingUsageKeyEncipherment)
}
func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage) error {
return certificates.ValidateKubeletServingCSR(req, usagesToSet(usages))
func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage, allowOmittingUsageKeyEncipherment bool) error {
return certificates.ValidateKubeletServingCSR(req, usagesToSet(usages), allowOmittingUsageKeyEncipherment)
}
func IsKubeletClientCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage) bool {
return certificates.IsKubeletClientCSR(req, usagesToSet(usages))
func IsKubeletClientCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage, allowOmittingUsageKeyEncipherment bool) bool {
return certificates.IsKubeletClientCSR(req, usagesToSet(usages), allowOmittingUsageKeyEncipherment)
}
func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage) error {
return certificates.ValidateKubeletClientCSR(req, usagesToSet(usages))
func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage, allowOmittingUsageKeyEncipherment bool) error {
return certificates.ValidateKubeletClientCSR(req, usagesToSet(usages), allowOmittingUsageKeyEncipherment)
}
func usagesToSet(usages []certificatesv1beta1.KeyUsage) sets.String {

View File

@@ -40,15 +40,28 @@ func TestIsKubeletServingCSR(t *testing.T) {
return csr
}
tests := map[string]struct {
req *x509.CertificateRequest
usages []capi.KeyUsage
exp bool
req *x509.CertificateRequest
usages []capi.KeyUsage
allowOmittingUsageKeyEncipherment bool
exp bool
}{
"defaults for kubelet-serving": {
req: newCSR(kubeletServerPEMOptions),
usages: kubeletServerUsages,
exp: true,
},
"defaults without key encipherment for kubelet-serving if allow omitting key encipherment": {
req: newCSR(kubeletServerPEMOptions),
usages: kubeletServerUsagesNoRSA,
allowOmittingUsageKeyEncipherment: true,
exp: true,
},
"defaults for kubelet-serving if allow omitting key encipherment": {
req: newCSR(kubeletServerPEMOptions),
usages: kubeletServerUsages,
allowOmittingUsageKeyEncipherment: true,
exp: true,
},
"does not default to kube-apiserver-client-kubelet if org is not 'system:nodes'": {
req: newCSR(kubeletServerPEMOptions, pemOptions{org: "not-system:nodes"}),
usages: kubeletServerUsages,
@@ -69,6 +82,17 @@ func TestIsKubeletServingCSR(t *testing.T) {
usages: kubeletServerUsages[1:],
exp: false,
},
"does not default to kubelet-serving if it is missing an expected usage if allow omitting key encipherment": {
req: newCSR(kubeletServerPEMOptions),
usages: kubeletServerUsagesNoRSA[1:],
allowOmittingUsageKeyEncipherment: true,
exp: false,
},
"does not default to kubelet-serving if it is missing an expected usage withou key encipherment": {
req: newCSR(kubeletServerPEMOptions),
usages: kubeletServerUsagesNoRSA,
exp: false,
},
"does not default to kubelet-serving if it does not specify any dnsNames or ipAddresses": {
req: newCSR(kubeletServerPEMOptions, pemOptions{ipAddresses: []net.IP{}, dnsNames: []string{}}),
usages: kubeletServerUsages[1:],
@@ -87,7 +111,7 @@ func TestIsKubeletServingCSR(t *testing.T) {
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := IsKubeletServingCSR(test.req, test.usages)
got := IsKubeletServingCSR(test.req, test.usages, test.allowOmittingUsageKeyEncipherment)
if test.exp != got {
t.Errorf("unexpected IsKubeletClientCSR output: exp=%v, got=%v", test.exp, got)
}
@@ -105,9 +129,10 @@ func TestIsKubeletClientCSR(t *testing.T) {
return csr
}
tests := map[string]struct {
req *x509.CertificateRequest
usages []capi.KeyUsage
exp bool
req *x509.CertificateRequest
usages []capi.KeyUsage
allowOmittingUsageKeyEncipherment bool
exp bool
}{
"defaults for kube-apiserver-client-kubelet": {
req: newCSR(kubeletClientPEMOptions),
@@ -154,10 +179,28 @@ func TestIsKubeletClientCSR(t *testing.T) {
usages: kubeletClientUsages[1:],
exp: false,
},
"does not default to kube-apiserver-client-kubelet if it is missing an expected usage without key encipherment": {
req: newCSR(kubeletClientPEMOptions),
usages: kubeletClientUsagesNoRSA[1:],
allowOmittingUsageKeyEncipherment: true,
exp: false,
},
"default to kube-apiserver-client-kubelet with key encipherment": {
req: newCSR(kubeletClientPEMOptions),
usages: kubeletClientUsages,
allowOmittingUsageKeyEncipherment: true,
exp: true,
},
"default to kube-apiserver-client-kubelet without key encipherment": {
req: newCSR(kubeletClientPEMOptions),
usages: kubeletClientUsagesNoRSA,
allowOmittingUsageKeyEncipherment: true,
exp: true,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := IsKubeletClientCSR(test.req, test.usages)
got := IsKubeletClientCSR(test.req, test.usages, test.allowOmittingUsageKeyEncipherment)
if test.exp != got {
t.Errorf("unexpected IsKubeletClientCSR output: exp=%v, got=%v", test.exp, got)
}
@@ -171,6 +214,10 @@ var (
capi.UsageKeyEncipherment,
capi.UsageClientAuth,
}
kubeletClientUsagesNoRSA = []capi.KeyUsage{
capi.UsageDigitalSignature,
capi.UsageClientAuth,
}
kubeletClientPEMOptions = pemOptions{
cn: "system:node:nodename",
org: "system:nodes",
@@ -181,6 +228,10 @@ var (
capi.UsageKeyEncipherment,
capi.UsageServerAuth,
}
kubeletServerUsagesNoRSA = []capi.KeyUsage{
capi.UsageDigitalSignature,
capi.UsageServerAuth,
}
kubeletServerPEMOptions = pemOptions{
cn: "system:node:requester-name",
org: "system:nodes",