Skip to content

Commit a983356

Browse files
author
James Munnelly
committed
Add signerName field to CSR resource spec
Signed-off-by: James Munnelly <[email protected]>
1 parent aaca31c commit a983356

36 files changed

+1437
-93
lines changed

api/openapi-spec/swagger.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/kubelet/app/server_bootstrap_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ func (s *csrSimulator) ServeHTTP(w http.ResponseWriter, req *http.Request) {
300300
PrivateKey: s.serverPrivateKey,
301301
Backdate: s.backdate,
302302
}
303-
cr, err := capihelper.ParseCSR(csr)
303+
cr, err := capihelper.ParseCSR(csr.Spec.Request)
304304
if err != nil {
305305
t.Fatal(err)
306306
}

pkg/apis/certificates/fuzzer/fuzzer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
2929
func(obj *certificates.CertificateSigningRequestSpec, c fuzz.Continue) {
3030
c.FuzzNoCustom(obj) // fuzz self without calling this function again
3131
obj.Usages = []certificates.KeyUsage{certificates.UsageKeyEncipherment}
32+
obj.SignerName = "example.com/custom-sample-signer"
3233
},
3334
}
3435
}

pkg/apis/certificates/types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ type CertificateSigningRequestSpec struct {
4242
// Base64-encoded PKCS#10 CSR data
4343
Request []byte
4444

45+
// Requested signer for the request. It is a qualified name in the form:
46+
// `scope-hostname.io/name`.
47+
// Distribution of trust for signers happens out of band.
48+
// You can select on this field using `spec.signerName`.
49+
SignerName string
50+
4551
// usages specifies a set of usage contexts the key will be
4652
// valid for.
4753
// See: https://tools.ietf.org/html/rfc5280#section-4.2.1.3

pkg/apis/certificates/v1beta1/BUILD

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package(default_visibility = ["//visibility:public"])
33
load(
44
"@io_bazel_rules_go//go:def.bzl",
55
"go_library",
6+
"go_test",
67
)
78

89
go_library(
910
name = "go_default_library",
1011
srcs = [
12+
"conversion.go",
1113
"defaults.go",
1214
"doc.go",
1315
"helpers.go",
@@ -19,9 +21,11 @@ go_library(
1921
deps = [
2022
"//pkg/apis/certificates:go_default_library",
2123
"//staging/src/k8s.io/api/certificates/v1beta1:go_default_library",
24+
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
2225
"//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library",
2326
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
2427
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
28+
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
2529
],
2630
)
2731

@@ -37,3 +41,10 @@ filegroup(
3741
srcs = [":package-srcs"],
3842
tags = ["automanaged"],
3943
)
44+
45+
go_test(
46+
name = "go_default_test",
47+
srcs = ["defaults_test.go"],
48+
embed = [":go_default_library"],
49+
deps = ["//staging/src/k8s.io/api/certificates/v1beta1:go_default_library"],
50+
)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1beta1
18+
19+
import (
20+
"fmt"
21+
22+
"k8s.io/apimachinery/pkg/runtime"
23+
)
24+
25+
func addConversionFuncs(scheme *runtime.Scheme) error {
26+
// Add field conversion funcs.
27+
return scheme.AddFieldLabelConversionFunc(SchemeGroupVersion.WithKind("CertificateSigningRequest"),
28+
func(label, value string) (string, string, error) {
29+
switch label {
30+
case "metadata.name",
31+
"spec.signerName":
32+
return label, value, nil
33+
default:
34+
return "", "", fmt.Errorf("field label not supported: %s", label)
35+
}
36+
},
37+
)
38+
}

pkg/apis/certificates/v1beta1/defaults.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,113 @@ limitations under the License.
1717
package v1beta1
1818

1919
import (
20+
"crypto/x509"
21+
"reflect"
22+
"strings"
23+
2024
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
2125
"k8s.io/apimachinery/pkg/runtime"
26+
"k8s.io/apimachinery/pkg/util/sets"
2227
)
2328

2429
func addDefaultingFuncs(scheme *runtime.Scheme) error {
2530
return RegisterDefaults(scheme)
2631
}
32+
2733
func SetDefaults_CertificateSigningRequestSpec(obj *certificatesv1beta1.CertificateSigningRequestSpec) {
2834
if obj.Usages == nil {
2935
obj.Usages = []certificatesv1beta1.KeyUsage{certificatesv1beta1.UsageDigitalSignature, certificatesv1beta1.UsageKeyEncipherment}
3036
}
37+
38+
if obj.SignerName == nil {
39+
signerName := DefaultSignerNameFromSpec(obj)
40+
obj.SignerName = &signerName
41+
}
42+
}
43+
44+
// DefaultSignerNameFromSpec will determine the signerName that should be set
45+
// by attempting to inspect the 'request' content and the spec options.
46+
func DefaultSignerNameFromSpec(obj *certificatesv1beta1.CertificateSigningRequestSpec) string {
47+
csr, err := ParseCSR(obj.Request)
48+
switch {
49+
case err != nil:
50+
// Set the signerName to 'legacy-unknown' as the CSR could not be
51+
// recognised.
52+
return certificatesv1beta1.LegacyUnknownSignerName
53+
case IsKubeletClientCSR(csr, obj.Usages):
54+
return certificatesv1beta1.KubeAPIServerClientKubeletSignerName
55+
case IsKubeletServingCSR(csr, obj.Usages):
56+
return certificatesv1beta1.KubeletServingSignerName
57+
default:
58+
return certificatesv1beta1.LegacyUnknownSignerName
59+
}
60+
}
61+
62+
func IsKubeletServingCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage) bool {
63+
if !reflect.DeepEqual([]string{"system:nodes"}, req.Subject.Organization) {
64+
return false
65+
}
66+
67+
// at least one of dnsNames or ipAddresses must be specified
68+
if len(req.DNSNames) == 0 && len(req.IPAddresses) == 0 {
69+
return false
70+
}
71+
72+
if len(req.EmailAddresses) > 0 || len(req.URIs) > 0 {
73+
return false
74+
}
75+
76+
requiredUsages := []certificatesv1beta1.KeyUsage{
77+
certificatesv1beta1.UsageDigitalSignature,
78+
certificatesv1beta1.UsageKeyEncipherment,
79+
certificatesv1beta1.UsageServerAuth,
80+
}
81+
if !equalUnsorted(requiredUsages, usages) {
82+
return false
83+
}
84+
85+
if !strings.HasPrefix(req.Subject.CommonName, "system:node:") {
86+
return false
87+
}
88+
89+
return true
90+
}
91+
92+
func IsKubeletClientCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage) bool {
93+
if !reflect.DeepEqual([]string{"system:nodes"}, req.Subject.Organization) {
94+
return false
95+
}
96+
97+
if len(req.DNSNames) > 0 || len(req.EmailAddresses) > 0 || len(req.IPAddresses) > 0 || len(req.URIs) > 0 {
98+
return false
99+
}
100+
101+
if !strings.HasPrefix(req.Subject.CommonName, "system:node:") {
102+
return false
103+
}
104+
105+
requiredUsages := []certificatesv1beta1.KeyUsage{
106+
certificatesv1beta1.UsageDigitalSignature,
107+
certificatesv1beta1.UsageKeyEncipherment,
108+
certificatesv1beta1.UsageClientAuth,
109+
}
110+
if !equalUnsorted(requiredUsages, usages) {
111+
return false
112+
}
113+
114+
return true
115+
}
116+
117+
// equalUnsorted compares two []string for equality of contents regardless of
118+
// the order of the elements
119+
func equalUnsorted(left, right []certificatesv1beta1.KeyUsage) bool {
120+
l := sets.NewString()
121+
for _, s := range left {
122+
l.Insert(string(s))
123+
}
124+
r := sets.NewString()
125+
for _, s := range right {
126+
r.Insert(string(s))
127+
}
128+
return l.Equal(r)
31129
}

0 commit comments

Comments
 (0)