github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/registry/resolver/rbac.go (about) 1 package resolver 2 3 import ( 4 "crypto/sha256" 5 "encoding/json" 6 "fmt" 7 "math/big" 8 9 "github.com/operator-framework/api/pkg/operators/v1alpha1" 10 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" 11 hashutil "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/hash" 12 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" 13 corev1 "k8s.io/api/core/v1" 14 rbacv1 "k8s.io/api/rbac/v1" 15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 ) 17 18 const maxNameLength = 63 19 20 func generateName(base string, o interface{}) (string, error) { 21 hash, err := hashutil.DeepHashObject(o) 22 if err != nil { 23 return "", err 24 } 25 if len(base)+len(hash) > maxNameLength { 26 base = base[:maxNameLength-len(hash)-1] 27 } 28 29 return fmt.Sprintf("%s-%s", base, hash), nil 30 } 31 32 type OperatorPermissions struct { 33 ServiceAccount *corev1.ServiceAccount 34 Roles []*rbacv1.Role 35 RoleBindings []*rbacv1.RoleBinding 36 ClusterRoles []*rbacv1.ClusterRole 37 ClusterRoleBindings []*rbacv1.ClusterRoleBinding 38 } 39 40 func NewOperatorPermissions(serviceAccount *corev1.ServiceAccount) *OperatorPermissions { 41 return &OperatorPermissions{ 42 ServiceAccount: serviceAccount, 43 Roles: []*rbacv1.Role{}, 44 RoleBindings: []*rbacv1.RoleBinding{}, 45 ClusterRoles: []*rbacv1.ClusterRole{}, 46 ClusterRoleBindings: []*rbacv1.ClusterRoleBinding{}, 47 } 48 } 49 50 func (o *OperatorPermissions) AddRole(role *rbacv1.Role) { 51 o.Roles = append(o.Roles, role) 52 } 53 54 func (o *OperatorPermissions) AddRoleBinding(roleBinding *rbacv1.RoleBinding) { 55 o.RoleBindings = append(o.RoleBindings, roleBinding) 56 } 57 58 func (o *OperatorPermissions) AddClusterRole(clusterRole *rbacv1.ClusterRole) { 59 o.ClusterRoles = append(o.ClusterRoles, clusterRole) 60 } 61 62 func (o *OperatorPermissions) AddClusterRoleBinding(clusterRoleBinding *rbacv1.ClusterRoleBinding) { 63 o.ClusterRoleBindings = append(o.ClusterRoleBindings, clusterRoleBinding) 64 } 65 66 const ContentHashLabelKey = "olm.permissions.hash" 67 68 func PolicyRuleHashLabelValue(rules []rbacv1.PolicyRule) (string, error) { 69 raw, err := json.Marshal(rules) 70 if err != nil { 71 return "", err 72 } 73 return toBase62(sha256.Sum224(raw)), nil 74 } 75 76 func RoleReferenceAndSubjectHashLabelValue(roleRef rbacv1.RoleRef, subjects []rbacv1.Subject) (string, error) { 77 var container = struct { 78 RoleRef rbacv1.RoleRef 79 Subjects []rbacv1.Subject 80 }{ 81 RoleRef: roleRef, 82 Subjects: subjects, 83 } 84 raw, err := json.Marshal(&container) 85 if err != nil { 86 return "", err 87 } 88 return toBase62(sha256.Sum224(raw)), nil 89 } 90 91 func toBase62(hash [28]byte) string { 92 var i big.Int 93 i.SetBytes(hash[:]) 94 return i.Text(62) 95 } 96 97 func RBACForClusterServiceVersion(csv *v1alpha1.ClusterServiceVersion) (map[string]*OperatorPermissions, error) { 98 permissions := map[string]*OperatorPermissions{} 99 100 // Use a StrategyResolver to get the strategy details 101 strategyResolver := install.StrategyResolver{} 102 strategy, err := strategyResolver.UnmarshalStrategy(csv.Spec.InstallStrategy) 103 if err != nil { 104 return nil, err 105 } 106 107 // Assume the strategy is for a deployment 108 strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment) 109 if !ok { 110 return nil, fmt.Errorf("could not assert strategy implementation as deployment for CSV %s", csv.GetName()) 111 } 112 113 // Resolve Permissions 114 for _, permission := range strategyDetailsDeployment.Permissions { 115 // Create ServiceAccount if necessary 116 if _, ok := permissions[permission.ServiceAccountName]; !ok { 117 serviceAccount := &corev1.ServiceAccount{} 118 serviceAccount.SetNamespace(csv.GetNamespace()) 119 serviceAccount.SetName(permission.ServiceAccountName) 120 ownerutil.AddNonBlockingOwner(serviceAccount, csv) 121 122 permissions[permission.ServiceAccountName] = NewOperatorPermissions(serviceAccount) 123 } 124 125 // Create Role 126 name, err := generateName(fmt.Sprintf("%s-%s", csv.GetName(), permission.ServiceAccountName), struct { 127 Name string `json:"name,omitempty"` 128 Permission v1alpha1.StrategyDeploymentPermissions `json:"permission"` 129 }{Name: csv.GetName(), Permission: permission}) 130 if err != nil { 131 return nil, err 132 } 133 role := &rbacv1.Role{ 134 ObjectMeta: metav1.ObjectMeta{ 135 Name: name, 136 Namespace: csv.GetNamespace(), 137 OwnerReferences: []metav1.OwnerReference{ownerutil.NonBlockingOwner(csv)}, 138 Labels: ownerutil.OwnerLabel(csv, v1alpha1.ClusterServiceVersionKind), 139 }, 140 Rules: permission.Rules, 141 } 142 hash, err := PolicyRuleHashLabelValue(permission.Rules) 143 if err != nil { 144 return nil, fmt.Errorf("failed to hash permission rules: %w", err) 145 } 146 role.Labels[ContentHashLabelKey] = hash 147 permissions[permission.ServiceAccountName].AddRole(role) 148 149 // Create RoleBinding 150 roleBinding := &rbacv1.RoleBinding{ 151 ObjectMeta: metav1.ObjectMeta{ 152 Name: role.GetName(), 153 Namespace: csv.GetNamespace(), 154 OwnerReferences: []metav1.OwnerReference{ownerutil.NonBlockingOwner(csv)}, 155 Labels: ownerutil.OwnerLabel(csv, v1alpha1.ClusterServiceVersionKind), 156 }, 157 RoleRef: rbacv1.RoleRef{ 158 Kind: "Role", 159 Name: role.GetName(), 160 APIGroup: rbacv1.GroupName}, 161 Subjects: []rbacv1.Subject{{ 162 Kind: "ServiceAccount", 163 Name: permission.ServiceAccountName, 164 Namespace: csv.GetNamespace(), 165 }}, 166 } 167 hash, err = RoleReferenceAndSubjectHashLabelValue(roleBinding.RoleRef, roleBinding.Subjects) 168 if err != nil { 169 return nil, fmt.Errorf("failed to hash binding content: %w", err) 170 } 171 roleBinding.Labels[ContentHashLabelKey] = hash 172 permissions[permission.ServiceAccountName].AddRoleBinding(roleBinding) 173 } 174 175 // Resolve ClusterPermissions as StepResources 176 for _, permission := range strategyDetailsDeployment.ClusterPermissions { 177 // Create ServiceAccount if necessary 178 if _, ok := permissions[permission.ServiceAccountName]; !ok { 179 serviceAccount := &corev1.ServiceAccount{} 180 ownerutil.AddOwner(serviceAccount, csv, false, false) 181 serviceAccount.SetName(permission.ServiceAccountName) 182 183 permissions[permission.ServiceAccountName] = NewOperatorPermissions(serviceAccount) 184 } 185 186 // Create ClusterRole 187 name, err := generateName(csv.GetName(), struct { 188 Name string `json:"name,omitempty"` 189 Namespace string `json:"namespace,omitempty"` 190 Permission v1alpha1.StrategyDeploymentPermissions `json:"permission"` 191 }{Name: csv.GetName(), Namespace: csv.GetNamespace(), Permission: permission}) 192 if err != nil { 193 return nil, err 194 } 195 role := &rbacv1.ClusterRole{ 196 ObjectMeta: metav1.ObjectMeta{ 197 Name: name, 198 Labels: ownerutil.OwnerLabel(csv, v1alpha1.ClusterServiceVersionKind), 199 }, 200 Rules: permission.Rules, 201 } 202 hash, err := PolicyRuleHashLabelValue(permission.Rules) 203 if err != nil { 204 return nil, fmt.Errorf("failed to hash permission rules: %w", err) 205 } 206 role.Labels[ContentHashLabelKey] = hash 207 permissions[permission.ServiceAccountName].AddClusterRole(role) 208 209 // Create ClusterRoleBinding 210 roleBinding := &rbacv1.ClusterRoleBinding{ 211 ObjectMeta: metav1.ObjectMeta{ 212 Name: role.GetName(), 213 Namespace: csv.GetNamespace(), 214 Labels: ownerutil.OwnerLabel(csv, v1alpha1.ClusterServiceVersionKind), 215 }, 216 RoleRef: rbacv1.RoleRef{ 217 Kind: "ClusterRole", 218 Name: role.GetName(), 219 APIGroup: rbacv1.GroupName, 220 }, 221 Subjects: []rbacv1.Subject{{ 222 Kind: "ServiceAccount", 223 Name: permission.ServiceAccountName, 224 Namespace: csv.GetNamespace(), 225 }}, 226 } 227 hash, err = RoleReferenceAndSubjectHashLabelValue(roleBinding.RoleRef, roleBinding.Subjects) 228 if err != nil { 229 return nil, fmt.Errorf("failed to hash binding content: %w", err) 230 } 231 roleBinding.Labels[ContentHashLabelKey] = hash 232 permissions[permission.ServiceAccountName].AddClusterRoleBinding(roleBinding) 233 } 234 return permissions, nil 235 }