kubeadm: use dedicated ClusterRole for apiserver kubelet client

Signed-off-by: Micah Hausler <mhausler@amazon.com>
This commit is contained in:
Micah Hausler
2026-02-16 10:27:20 -06:00
committed by Lubomir I. Ivanov
parent 32137bf43b
commit 5ea68035bb
6 changed files with 96 additions and 3 deletions

View File

@@ -109,6 +109,11 @@ func runBootstrapToken(c workflow.RunData) error {
return err
}
// Create RBAC rules that allow the API server kubelet client to access the kubelet API
if err := nodebootstraptokenphase.AllowAPIServerToAccessKubeletAPI(client); err != nil {
return errors.Wrap(err, "error allowing API server to access kubelet API")
}
// Create the cluster-info ConfigMap with the associated RBAC rules
if err := clusterinfophase.CreateBootstrapConfigMapIfNotExists(client, kubeconfig); err != nil {
return errors.Wrap(err, "error creating bootstrap ConfigMap")

View File

@@ -80,6 +80,11 @@ func runBootstrapToken(c workflow.RunData) error {
errs = append(errs, err)
}
// Create/update RBAC rules that allow the API server kubelet client to access the kubelet API
if err := nodebootstraptoken.AllowAPIServerToAccessKubeletAPI(client); err != nil {
errs = append(errs, err)
}
// Create/update RBAC rules that makes the cluster-info ConfigMap reachable
if err := clusterinfophase.CreateClusterInfoRBACRules(client); err != nil {
errs = append(errs, err)

View File

@@ -210,6 +210,11 @@ const (
// built-in ClusterRole.
ClusterAdminsGroupAndClusterRoleBinding = "kubeadm:cluster-admins"
// KubeletAPIAdminClusterRoleBindingName is the name of the ClusterRoleBinding for the apiserver kubelet client
KubeletAPIAdminClusterRoleBindingName = "kubeadm:apiserver-kubelet-client"
// KubeletAPIAdminClusterRoleName is the name of the built-in ClusterRole for kubelet API access
KubeletAPIAdminClusterRoleName = "system:kubelet-api-admin"
// KubernetesAPICallTimeout specifies how long kubeadm should wait for API calls
KubernetesAPICallTimeout = 1 * time.Minute
// KubernetesAPICallRetryInterval defines how long kubeadm should wait before retrying a failed API operation

View File

@@ -130,3 +130,25 @@ func AutoApproveNodeCertificateRotation(client clientset.Interface) error {
},
})
}
// AllowAPIServerToAccessKubeletAPI creates RBAC rules that allow the API server kubelet client to access the kubelet API
func AllowAPIServerToAccessKubeletAPI(client clientset.Interface) error {
fmt.Println("[bootstrap-token] Configured RBAC rules to allow the API server kubelet client certificate to access the kubelet API")
return apiclient.CreateOrUpdate(client.RbacV1().ClusterRoleBindings(), &rbac.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeletAPIAdminClusterRoleBindingName,
},
RoleRef: rbac.RoleRef{
APIGroup: rbac.GroupName,
Kind: "ClusterRole",
Name: constants.KubeletAPIAdminClusterRoleName,
},
Subjects: []rbac.Subject{
{
Kind: rbac.UserKind,
Name: constants.APIServerKubeletClientCertCommonName,
},
},
})
}

View File

@@ -277,6 +277,63 @@ func TestAllowBootstrapTokensToGetNodes(t *testing.T) {
}
}
func TestAllowAPIServerToAccessKubeletAPI(t *testing.T) {
tests := []struct {
name string
client clientset.Interface
}{
{
name: "ClusterRoleBindings is empty",
client: clientsetfake.NewSimpleClientset(),
},
{
name: "ClusterRoleBindings already exists",
client: newMockClusterRoleBinddingClientForTest(t, &rbac.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeletAPIAdminClusterRoleBindingName,
},
RoleRef: rbac.RoleRef{
APIGroup: rbac.GroupName,
Kind: "ClusterRole",
Name: constants.KubeletAPIAdminClusterRoleName,
},
Subjects: []rbac.Subject{
{
Kind: rbac.UserKind,
Name: constants.APIServerKubeletClientCertCommonName,
},
},
}),
},
{
name: "Create new ClusterRoleBindings",
client: newMockClusterRoleBinddingClientForTest(t, &rbac.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeletAPIAdminClusterRoleBindingName,
},
RoleRef: rbac.RoleRef{
APIGroup: rbac.GroupName,
Kind: "ClusterRole",
Name: constants.KubeletAPIAdminClusterRoleName,
},
Subjects: []rbac.Subject{
{
Kind: rbac.GroupKind,
Name: constants.NodesGroup,
},
},
}),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := AllowAPIServerToAccessKubeletAPI(tt.client); err != nil {
t.Errorf("AllowAPIServerToAccessKubeletAPI() return error = %v", err)
}
})
}
}
func newMockClusterRoleBinddingClientForTest(t *testing.T, clusterRoleBinding *rbac.ClusterRoleBinding) *clientsetfake.Clientset {
client := clientsetfake.NewSimpleClientset()
_, err := client.RbacV1().ClusterRoleBindings().Create(context.TODO(), clusterRoleBinding, metav1.CreateOptions{})

View File

@@ -318,9 +318,8 @@ func KubeadmCertKubeletClient() *KubeadmCert {
CAName: "ca",
config: pkiutil.CertConfig{
Config: certutil.Config{
CommonName: kubeadmconstants.APIServerKubeletClientCertCommonName,
Organization: []string{kubeadmconstants.ClusterAdminsGroupAndClusterRoleBinding},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
CommonName: kubeadmconstants.APIServerKubeletClientCertCommonName,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
},
}