...

Source file src/k8s.io/kubernetes/test/integration/certificates/admission_sign_test.go

Documentation: k8s.io/kubernetes/test/integration/certificates

     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 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  // Verifies that the CSR approval admission plugin correctly enforces that a
    35  // user has permission to sign CSRs for the named signer
    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  			// Run an apiserver with the default configuration options.
    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  			// Drop the default RBAC superuser permissions to rely on the internal superuser authorizer
    64  			if err := client.RbacV1().ClusterRoleBindings().Delete(context.TODO(), "cluster-admin", metav1.DeleteOptions{}); err != nil {
    65  				t.Fatal(err)
    66  			}
    67  
    68  			// Grant 'test-user' permission to sign CertificateSigningRequests with the specified signerName.
    69  			const username = "test-user"
    70  			grantUserPermissionToSignFor(t, client, username, test.allowedSignerName)
    71  			// Create a CSR to attempt to sign.
    72  			csr := createTestingCSR(t, client.CertificatesV1().CertificateSigningRequests(), "csr", test.signerName, "")
    73  
    74  			// Create a second client, impersonating the 'test-user' for us to test with.
    75  			testuserConfig := restclient.CopyConfig(s.ClientConfig)
    76  			testuserConfig.Impersonate = restclient.ImpersonationConfig{UserName: username}
    77  			testuserClient := clientset.NewForConfigOrDie(testuserConfig)
    78  
    79  			// Attempt to 'sign' the certificate.
    80  			// random valid pem
    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  			// superuser should always have permission to sign; dry-run so we don't actually modify the CSR so the non-superuser can attempt as well
   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  			// must have permission to 'approve' the 'certificatesigners' named
   148  			// 'signerName' to approve CSRs with the given signerName.
   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