sigs.k8s.io/cluster-api@v1.7.1/controlplane/kubeadm/internal/workload_cluster_rbac.go (about) 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 internal 18 19 import ( 20 "context" 21 "fmt" 22 23 "github.com/blang/semver/v4" 24 "github.com/pkg/errors" 25 rbacv1 "k8s.io/api/rbac/v1" 26 apierrors "k8s.io/apimachinery/pkg/api/errors" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 30 "sigs.k8s.io/cluster-api/util/version" 31 ) 32 33 const ( 34 // NodeBootstrapTokenAuthGroup specifies which group a Node Bootstrap Token should be authenticated in. 35 NodeBootstrapTokenAuthGroup = "system:bootstrappers:kubeadm:default-node-token" 36 37 // GetNodesClusterRoleName defines the name of the ClusterRole and ClusterRoleBinding to get nodes. 38 GetNodesClusterRoleName = "kubeadm:get-nodes" 39 40 // ClusterAdminsGroupAndClusterRoleBinding is the name of the Group used for kubeadm generated cluster 41 // admin credentials and the name of the ClusterRoleBinding that binds the same Group to the "cluster-admin" 42 // built-in ClusterRole. 43 ClusterAdminsGroupAndClusterRoleBinding = "kubeadm:cluster-admins" 44 45 // NodesGroup defines the well-known group for all nodes. 46 NodesGroup = "system:nodes" 47 48 // KubeletConfigMapRolePrefix defines base kubelet configuration ConfigMap role prefix. 49 KubeletConfigMapRolePrefix = "kubeadm:" 50 51 // KubeletConfigMapName defines base kubelet configuration ConfigMap name for kubeadm < 1.24. 52 KubeletConfigMapName = "kubelet-config-%d.%d" 53 54 // UnversionedKubeletConfigMapName defines base kubelet configuration ConfigMap for kubeadm >= 1.24. 55 UnversionedKubeletConfigMapName = "kubelet-config" 56 ) 57 58 // EnsureResource creates a resoutce if the target resource doesn't exist. If the resource exists already, this function will ignore the resource instead. 59 func (w *Workload) EnsureResource(ctx context.Context, obj client.Object) error { 60 testObj := obj.DeepCopyObject().(client.Object) 61 key := client.ObjectKeyFromObject(obj) 62 if err := w.Client.Get(ctx, key, testObj); err != nil && !apierrors.IsNotFound(err) { 63 return errors.Wrapf(err, "failed to determine if resource %s/%s already exists", key.Namespace, key.Name) 64 } else if err == nil { 65 // If object already exists, nothing left to do 66 return nil 67 } 68 if err := w.Client.Create(ctx, obj); err != nil { 69 if !apierrors.IsAlreadyExists(err) { 70 return errors.Wrapf(err, "unable to create resource %s/%s on workload cluster", key.Namespace, key.Name) 71 } 72 } 73 return nil 74 } 75 76 // AllowClusterAdminPermissions creates ClusterRoleBinding rules to use the kubeadm:cluster-admins Cluster Role created in Kubeadm v1.29. 77 func (w *Workload) AllowClusterAdminPermissions(ctx context.Context, targetVersion semver.Version) error { 78 // We intentionally only parse major/minor/patch so that the subsequent code 79 // also already applies to pre-release versions of new releases. 80 // Do nothing for Kubernetes < 1.29. 81 if version.Compare(targetVersion, semver.Version{Major: 1, Minor: 29, Patch: 0}, version.WithoutPreReleases()) < 0 { 82 return nil 83 } 84 return w.EnsureResource(ctx, &rbacv1.ClusterRoleBinding{ 85 ObjectMeta: metav1.ObjectMeta{ 86 Name: ClusterAdminsGroupAndClusterRoleBinding, 87 }, 88 RoleRef: rbacv1.RoleRef{ 89 APIGroup: rbacv1.GroupName, 90 Kind: "ClusterRole", 91 Name: "cluster-admin", 92 }, 93 Subjects: []rbacv1.Subject{ 94 { 95 Kind: rbacv1.GroupKind, 96 Name: ClusterAdminsGroupAndClusterRoleBinding, 97 }, 98 }, 99 }, 100 ) 101 } 102 103 // AllowBootstrapTokensToGetNodes creates RBAC rules to allow Node Bootstrap Tokens to list nodes. 104 func (w *Workload) AllowBootstrapTokensToGetNodes(ctx context.Context) error { 105 if err := w.EnsureResource(ctx, &rbacv1.ClusterRole{ 106 ObjectMeta: metav1.ObjectMeta{ 107 Name: GetNodesClusterRoleName, 108 Namespace: metav1.NamespaceSystem, 109 }, 110 Rules: []rbacv1.PolicyRule{ 111 { 112 Verbs: []string{"get"}, 113 APIGroups: []string{""}, 114 Resources: []string{"nodes"}, 115 }, 116 }, 117 }); err != nil { 118 return err 119 } 120 121 return w.EnsureResource(ctx, &rbacv1.ClusterRoleBinding{ 122 ObjectMeta: metav1.ObjectMeta{ 123 Name: GetNodesClusterRoleName, 124 Namespace: metav1.NamespaceSystem, 125 }, 126 RoleRef: rbacv1.RoleRef{ 127 APIGroup: rbacv1.GroupName, 128 Kind: "ClusterRole", 129 Name: GetNodesClusterRoleName, 130 }, 131 Subjects: []rbacv1.Subject{ 132 { 133 Kind: rbacv1.GroupKind, 134 Name: NodeBootstrapTokenAuthGroup, 135 }, 136 }, 137 }) 138 } 139 140 func generateKubeletConfigName(version semver.Version) string { 141 majorMinor := semver.Version{Major: version.Major, Minor: version.Minor} 142 if majorMinor.GTE(minVerUnversionedKubeletConfig) { 143 return UnversionedKubeletConfigMapName 144 } 145 return fmt.Sprintf(KubeletConfigMapName, version.Major, version.Minor) 146 } 147 148 func generateKubeletConfigRoleName(version semver.Version) string { 149 return KubeletConfigMapRolePrefix + generateKubeletConfigName(version) 150 } 151 152 // ReconcileKubeletRBACBinding will create a RoleBinding for the new kubelet version during upgrades. 153 // If the role binding already exists this function is a no-op. 154 func (w *Workload) ReconcileKubeletRBACBinding(ctx context.Context, version semver.Version) error { 155 roleName := generateKubeletConfigRoleName(version) 156 return w.EnsureResource(ctx, &rbacv1.RoleBinding{ 157 ObjectMeta: metav1.ObjectMeta{ 158 Namespace: metav1.NamespaceSystem, 159 Name: roleName, 160 }, 161 Subjects: []rbacv1.Subject{ 162 { 163 APIGroup: rbacv1.GroupName, 164 Kind: rbacv1.GroupKind, 165 Name: NodesGroup, 166 }, 167 { 168 APIGroup: rbacv1.GroupName, 169 Kind: rbacv1.GroupKind, 170 Name: NodeBootstrapTokenAuthGroup, 171 }, 172 }, 173 RoleRef: rbacv1.RoleRef{ 174 APIGroup: rbacv1.GroupName, 175 Kind: "Role", 176 Name: roleName, 177 }, 178 }) 179 } 180 181 // ReconcileKubeletRBACRole will create a Role for the new kubelet version during upgrades. 182 // If the role already exists this function is a no-op. 183 func (w *Workload) ReconcileKubeletRBACRole(ctx context.Context, version semver.Version) error { 184 return w.EnsureResource(ctx, &rbacv1.Role{ 185 ObjectMeta: metav1.ObjectMeta{ 186 Name: generateKubeletConfigRoleName(version), 187 Namespace: metav1.NamespaceSystem, 188 }, 189 Rules: []rbacv1.PolicyRule{ 190 { 191 Verbs: []string{"get"}, 192 APIGroups: []string{""}, 193 Resources: []string{"configmaps"}, 194 ResourceNames: []string{generateKubeletConfigName(version)}, 195 }, 196 }, 197 }) 198 }