1
16
17 package certificates
18
19 import (
20 "context"
21 "testing"
22
23 certv1 "k8s.io/api/certificates/v1"
24 rbacv1 "k8s.io/api/rbac/v1"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/runtime/schema"
27 clientset "k8s.io/client-go/kubernetes"
28 restclient "k8s.io/client-go/rest"
29 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
30 "k8s.io/kubernetes/test/integration/authutil"
31 "k8s.io/kubernetes/test/integration/framework"
32 )
33
34
35
36 func TestCSRSignerNameSigningPlugin(t *testing.T) {
37 tests := map[string]struct {
38 allowedSignerName string
39 signerName string
40 error string
41 }{
42 "should admit when a user has permission for the exact signerName": {
43 allowedSignerName: "example.com/something",
44 signerName: "example.com/something",
45 },
46 "should admit when a user has permission for the wildcard-suffixed signerName": {
47 allowedSignerName: "example.com/*",
48 signerName: "example.com/something",
49 },
50 "should deny if a user does not have permission for the given signerName": {
51 allowedSignerName: "example.com/not-something",
52 signerName: "example.com/something",
53 error: `certificatesigningrequests.certificates.k8s.io "csr" is forbidden: user not permitted to sign requests with signerName "example.com/something"`,
54 },
55 }
56 for name, test := range tests {
57 t.Run(name, func(t *testing.T) {
58
59 s := kubeapiservertesting.StartTestServerOrDie(t, kubeapiservertesting.NewDefaultTestServerOptions(), []string{"--authorization-mode=RBAC"}, framework.SharedEtcd())
60 defer s.TearDownFn()
61 client := clientset.NewForConfigOrDie(s.ClientConfig)
62
63
64 if err := client.RbacV1().ClusterRoleBindings().Delete(context.TODO(), "cluster-admin", metav1.DeleteOptions{}); err != nil {
65 t.Fatal(err)
66 }
67
68
69 const username = "test-user"
70 grantUserPermissionToSignFor(t, client, username, test.allowedSignerName)
71
72 csr := createTestingCSR(t, client.CertificatesV1().CertificateSigningRequests(), "csr", test.signerName, "")
73
74
75 testuserConfig := restclient.CopyConfig(s.ClientConfig)
76 testuserConfig.Impersonate = restclient.ImpersonationConfig{UserName: username}
77 testuserClient := clientset.NewForConfigOrDie(testuserConfig)
78
79
80
81 csr.Status.Certificate = []byte(`
82 Leading non-PEM content
83 -----BEGIN CERTIFICATE-----
84 MIIBqDCCAU2gAwIBAgIUfbqeieihh/oERbfvRm38XvS/xHAwCgYIKoZIzj0EAwIw
85 GjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMCAXDTE2MTAxMTA1MDYwMFoYDzIx
86 MTYwOTE3MDUwNjAwWjAUMRIwEAYDVQQDEwlNeSBDbGllbnQwWTATBgcqhkjOPQIB
87 BggqhkjOPQMBBwNCAARv6N4R/sjMR65iMFGNLN1GC/vd7WhDW6J4X/iAjkRLLnNb
88 KbRG/AtOUZ+7upJ3BWIRKYbOabbQGQe2BbKFiap4o3UwczAOBgNVHQ8BAf8EBAMC
89 BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU
90 K/pZOWpNcYai6eHFpmJEeFpeQlEwHwYDVR0jBBgwFoAUX6nQlxjfWnP6aM1meO/Q
91 a6b3a9kwCgYIKoZIzj0EAwIDSQAwRgIhAIWTKw/sjJITqeuNzJDAKU4xo1zL+xJ5
92 MnVCuBwfwDXCAiEAw/1TA+CjPq9JC5ek1ifR0FybTURjeQqYkKpve1dveps=
93 -----END CERTIFICATE-----
94 Intermediate non-PEM content
95 -----BEGIN CERTIFICATE-----
96 MIIBqDCCAU6gAwIBAgIUfqZtjoFgczZ+oQZbEC/BDSS2J6wwCgYIKoZIzj0EAwIw
97 EjEQMA4GA1UEAxMHUm9vdC1DQTAgFw0xNjEwMTEwNTA2MDBaGA8yMTE2MDkxNzA1
98 MDYwMFowGjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMFkwEwYHKoZIzj0CAQYI
99 KoZIzj0DAQcDQgAEyWHEMMCctJg8Xa5YWLqaCPbk3MjB+uvXac42JM9pj4k9jedD
100 kpUJRkWIPzgJI8Zk/3cSzluUTixP6JBSDKtwwaN4MHYwDgYDVR0PAQH/BAQDAgGm
101 MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
102 FF+p0JcY31pz+mjNZnjv0Gum92vZMB8GA1UdIwQYMBaAFB7P6+i4/pfNjqZgJv/b
103 dgA7Fe4tMAoGCCqGSM49BAMCA0gAMEUCIQCTT1YWQZaAqfQ2oBxzOkJE2BqLFxhz
104 3smQlrZ5gCHddwIgcvT7puhYOzAgcvMn9+SZ1JOyZ7edODjshCVCRnuHK2c=
105 -----END CERTIFICATE-----
106 Trailing non-PEM content
107 `)
108
109
110 if _, err := client.CertificatesV1().CertificateSigningRequests().UpdateStatus(context.TODO(), csr, metav1.UpdateOptions{DryRun: []string{metav1.DryRunAll}}); err != nil {
111 t.Errorf("expected no superuser error but got: %v", err)
112 }
113
114 _, err := testuserClient.CertificatesV1().CertificateSigningRequests().UpdateStatus(context.TODO(), csr, metav1.UpdateOptions{})
115 if err != nil && test.error != err.Error() {
116 t.Errorf("expected error %q but got: %v", test.error, err)
117 }
118 if err == nil && test.error != "" {
119 t.Errorf("expected to get an error %q but got none", test.error)
120 }
121 })
122 }
123 }
124
125 func grantUserPermissionToSignFor(t *testing.T, client clientset.Interface, username string, signerNames ...string) {
126 resourceName := "signername-" + username
127 cr := buildSigningClusterRoleForSigners(resourceName, signerNames...)
128 crb := buildClusterRoleBindingForUser(resourceName, username, cr.Name)
129 if _, err := client.RbacV1().ClusterRoles().Create(context.TODO(), cr, metav1.CreateOptions{}); err != nil {
130 t.Fatalf("failed to create test fixtures: %v", err)
131 }
132 if _, err := client.RbacV1().ClusterRoleBindings().Create(context.TODO(), crb, metav1.CreateOptions{}); err != nil {
133 t.Fatalf("failed to create test fixtures: %v", err)
134 }
135 signRule := cr.Rules[0]
136 statusRule := cr.Rules[1]
137 authutil.WaitForNamedAuthorizationUpdate(t, context.TODO(), client.AuthorizationV1(), username, "", signRule.Verbs[0], signRule.ResourceNames[0], schema.GroupResource{Group: signRule.APIGroups[0], Resource: signRule.Resources[0]}, true)
138 authutil.WaitForNamedAuthorizationUpdate(t, context.TODO(), client.AuthorizationV1(), username, "", statusRule.Verbs[0], "", schema.GroupResource{Group: statusRule.APIGroups[0], Resource: statusRule.Resources[0]}, true)
139 }
140
141 func buildSigningClusterRoleForSigners(name string, signerNames ...string) *rbacv1.ClusterRole {
142 return &rbacv1.ClusterRole{
143 ObjectMeta: metav1.ObjectMeta{
144 Name: name,
145 },
146 Rules: []rbacv1.PolicyRule{
147
148
149 {
150 Verbs: []string{"sign"},
151 APIGroups: []string{certv1.SchemeGroupVersion.Group},
152 Resources: []string{"signers"},
153 ResourceNames: signerNames,
154 },
155 {
156 Verbs: []string{"update"},
157 APIGroups: []string{certv1.SchemeGroupVersion.Group},
158 Resources: []string{"certificatesigningrequests/status"},
159 },
160 },
161 }
162 }
163
View as plain text