1
16
17 package clustertrustbundles
18
19 import (
20 "context"
21 "crypto/x509"
22 "crypto/x509/pkix"
23 "math/big"
24 "testing"
25
26 certsv1alpha1 "k8s.io/api/certificates/v1alpha1"
27 rbacv1 "k8s.io/api/rbac/v1"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/runtime/schema"
30 "k8s.io/client-go/kubernetes"
31 "k8s.io/client-go/rest"
32 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
33 "k8s.io/kubernetes/test/integration/authutil"
34 "k8s.io/kubernetes/test/integration/framework"
35 )
36
37
38
39 func TestCTBAttestPlugin(t *testing.T) {
40 testCases := []struct {
41 description string
42 trustBundleName string
43 allowedSignerName string
44 targetSignerName string
45 wantError string
46 }{
47 {
48 description: "should admit if the clustertrustbundle doesn't target a signer",
49 trustBundleName: "foo",
50 allowedSignerName: "foo.com/bar",
51 },
52 {
53 description: "should admit if the user has attest for the exact signer name",
54 trustBundleName: "foo.com:bar:abc",
55 allowedSignerName: "foo.com/bar",
56 targetSignerName: "foo.com/bar",
57 },
58 {
59 description: "should admit if the user has attest for the wildcard-suffixed signer name",
60 trustBundleName: "foo.com:bar:abc",
61 allowedSignerName: "foo.com/*",
62 targetSignerName: "foo.com/bar",
63 },
64 {
65 description: "should deny if the user does not have permission for the signer name",
66 trustBundleName: "foo.com:bar:abc",
67 allowedSignerName: "abc.com/def",
68 targetSignerName: "foo.com/bar",
69 wantError: "clustertrustbundles.certificates.k8s.io \"foo.com:bar:abc\" is forbidden: user not permitted to attest for signerName \"foo.com/bar\"",
70 },
71 }
72 for _, tc := range testCases {
73 t.Run(tc.description, func(t *testing.T) {
74 ctx := context.Background()
75
76 server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--authorization-mode=RBAC", "--feature-gates=ClusterTrustBundle=true"}, framework.SharedEtcd())
77 defer server.TearDownFn()
78
79 client := kubernetes.NewForConfigOrDie(server.ClientConfig)
80
81 if tc.allowedSignerName != "" {
82 grantUserPermissionToAttestFor(ctx, t, client, "test-user", tc.allowedSignerName)
83 }
84
85
86 testUserConfig := rest.CopyConfig(server.ClientConfig)
87 testUserConfig.Impersonate = rest.ImpersonationConfig{UserName: "test-user"}
88 testUserClient := kubernetes.NewForConfigOrDie(testUserConfig)
89
90 bundle := &certsv1alpha1.ClusterTrustBundle{
91 ObjectMeta: metav1.ObjectMeta{
92 Name: tc.trustBundleName,
93 },
94 Spec: certsv1alpha1.ClusterTrustBundleSpec{
95 SignerName: tc.targetSignerName,
96 TrustBundle: mustMakePEMBlock("CERTIFICATE", nil, mustMakeCertificate(t, &x509.Certificate{
97 SerialNumber: big.NewInt(0),
98 Subject: pkix.Name{
99 CommonName: "root1",
100 },
101 IsCA: true,
102 BasicConstraintsValid: true,
103 })),
104 },
105 }
106 _, err := testUserClient.CertificatesV1alpha1().ClusterTrustBundles().Create(ctx, bundle, metav1.CreateOptions{})
107 if err != nil && err.Error() != tc.wantError {
108 t.Fatalf("Bad error while creating ClusterTrustBundle; got %q want %q", err.Error(), tc.wantError)
109 } else if err == nil && tc.wantError != "" {
110 t.Fatalf("Bad error while creating ClusterTrustBundle; got nil want %q", tc.wantError)
111 }
112 })
113 }
114 }
115
116 func grantUserPermissionToAttestFor(ctx context.Context, t *testing.T, client kubernetes.Interface, username string, signerNames ...string) {
117 resourceName := "signername-" + username
118 cr := buildApprovalClusterRoleForSigners(resourceName, signerNames...)
119 crb := buildClusterRoleBindingForUser(resourceName, username, cr.Name)
120 if _, err := client.RbacV1().ClusterRoles().Create(ctx, cr, metav1.CreateOptions{}); err != nil {
121 t.Fatalf("unable to create test fixture RBAC rules: %v", err)
122 }
123 if _, err := client.RbacV1().ClusterRoleBindings().Create(ctx, crb, metav1.CreateOptions{}); err != nil {
124 t.Fatalf("unable to create test fixture RBAC rules: %v", err)
125 }
126 attestRule := cr.Rules[0]
127 createRule := cr.Rules[1]
128 authutil.WaitForNamedAuthorizationUpdate(t, ctx, client.AuthorizationV1(), username, "", attestRule.Verbs[0], attestRule.ResourceNames[0], schema.GroupResource{Group: attestRule.APIGroups[0], Resource: attestRule.Resources[0]}, true)
129 authutil.WaitForNamedAuthorizationUpdate(t, ctx, client.AuthorizationV1(), username, "", createRule.Verbs[0], "", schema.GroupResource{Group: createRule.APIGroups[0], Resource: createRule.Resources[0]}, true)
130 }
131
132 func buildApprovalClusterRoleForSigners(name string, signerNames ...string) *rbacv1.ClusterRole {
133 return &rbacv1.ClusterRole{
134 ObjectMeta: metav1.ObjectMeta{
135 Name: name,
136 },
137 Rules: []rbacv1.PolicyRule{
138 {
139 Verbs: []string{"attest"},
140 APIGroups: []string{"certificates.k8s.io"},
141 Resources: []string{"signers"},
142 ResourceNames: signerNames,
143 },
144 {
145 Verbs: []string{"create"},
146 APIGroups: []string{"certificates.k8s.io"},
147 Resources: []string{"clustertrustbundles"},
148 },
149 },
150 }
151 }
152
153 func buildClusterRoleBindingForUser(name, username, clusterRoleName string) *rbacv1.ClusterRoleBinding {
154 return &rbacv1.ClusterRoleBinding{
155 ObjectMeta: metav1.ObjectMeta{
156 Name: name,
157 },
158 Subjects: []rbacv1.Subject{
159 {
160 Kind: rbacv1.UserKind,
161 Name: username,
162 },
163 },
164 RoleRef: rbacv1.RoleRef{
165 APIGroup: rbacv1.SchemeGroupVersion.Group,
166 Kind: "ClusterRole",
167 Name: clusterRoleName,
168 },
169 }
170 }
171
View as plain text