github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/install/rule_checker.go (about) 1 package install 2 3 import ( 4 "fmt" 5 6 rbacauthorizer "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/plugin/pkg/auth/authorizer/rbac" 7 corev1 "k8s.io/api/core/v1" 8 rbacv1 "k8s.io/api/rbac/v1" 9 "k8s.io/apimachinery/pkg/labels" 10 "k8s.io/apiserver/pkg/authorization/authorizer" 11 crbacv1 "k8s.io/client-go/listers/rbac/v1" 12 13 "github.com/operator-framework/api/pkg/operators/v1alpha1" 14 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" 15 ) 16 17 // RuleChecker is used to verify whether PolicyRules are satisfied by existing Roles or ClusterRoles 18 type RuleChecker interface { 19 // RuleSatisfied determines whether a PolicyRule is satisfied for a ServiceAccount 20 // by existing Roles and ClusterRoles 21 RuleSatisfied(sa *corev1.ServiceAccount, namespace string, rule rbacv1.PolicyRule) (bool, error) 22 } 23 24 // CSVRuleChecker determines whether a PolicyRule is satisfied for a ServiceAccount 25 // by existing Roles and ClusterRoles 26 type CSVRuleChecker struct { 27 roleLister crbacv1.RoleLister 28 roleBindingLister crbacv1.RoleBindingLister 29 clusterRoleLister crbacv1.ClusterRoleLister 30 clusterRoleBindingLister crbacv1.ClusterRoleBindingLister 31 csv *v1alpha1.ClusterServiceVersion 32 } 33 34 // NewCSVRuleChecker returns a pointer to a new CSVRuleChecker 35 func NewCSVRuleChecker(roleLister crbacv1.RoleLister, roleBindingLister crbacv1.RoleBindingLister, clusterRoleLister crbacv1.ClusterRoleLister, clusterRoleBindingLister crbacv1.ClusterRoleBindingLister, csv *v1alpha1.ClusterServiceVersion) *CSVRuleChecker { 36 return &CSVRuleChecker{ 37 roleLister: roleLister, 38 roleBindingLister: roleBindingLister, 39 clusterRoleLister: clusterRoleLister, 40 clusterRoleBindingLister: clusterRoleBindingLister, 41 csv: csv.DeepCopy(), 42 } 43 } 44 45 // RuleSatisfied returns true if a ServiceAccount is authorized to perform all actions described by a PolicyRule in a namespace 46 func (c *CSVRuleChecker) RuleSatisfied(sa *corev1.ServiceAccount, namespace string, rule rbacv1.PolicyRule) (bool, error) { 47 // check if the rule is valid 48 err := ruleValid(rule) 49 if err != nil { 50 return false, fmt.Errorf("rule invalid: %s", err.Error()) 51 } 52 53 // get attributes set for the given Role and ServiceAccount 54 user := toDefaultInfo(sa) 55 attributesSet := toAttributesSet(user, namespace, rule) 56 57 // create a new RBACAuthorizer 58 rbacAuthorizer := rbacauthorizer.New(c, c, c, c) 59 60 // ensure all attributes are authorized 61 for _, attributes := range attributesSet { 62 decision, _, err := rbacAuthorizer.Authorize(attributes) 63 if err != nil { 64 return false, err 65 } 66 67 if decision == authorizer.DecisionDeny || decision == authorizer.DecisionNoOpinion { 68 return false, nil 69 } 70 } 71 72 return true, nil 73 } 74 75 func (c *CSVRuleChecker) GetRole(namespace, name string) (*rbacv1.Role, error) { 76 // get the Role 77 role, err := c.roleLister.Roles(namespace).Get(name) 78 if err != nil { 79 return nil, err 80 } 81 82 // check if the Role has an OwnerConflict with the client's CSV 83 if role != nil && ownerutil.HasOwnerConflict(c.csv, role.GetOwnerReferences()) { 84 return &rbacv1.Role{}, nil 85 } 86 87 return role, nil 88 } 89 90 func (c *CSVRuleChecker) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) { 91 // get all RoleBindings 92 rbList, err := c.roleBindingLister.RoleBindings(namespace).List(labels.Everything()) 93 if err != nil { 94 return nil, err 95 } 96 97 // filter based on OwnerReferences 98 var filtered []*rbacv1.RoleBinding 99 for _, rb := range rbList { 100 if !ownerutil.HasOwnerConflict(c.csv, rb.GetOwnerReferences()) { 101 filtered = append(filtered, rb) 102 } 103 } 104 105 return filtered, nil 106 } 107 108 func (c *CSVRuleChecker) GetClusterRole(name string) (*rbacv1.ClusterRole, error) { 109 // get the ClusterRole 110 clusterRole, err := c.clusterRoleLister.Get(name) 111 if err != nil { 112 return nil, err 113 } 114 115 // check if the ClusterRole has an OwnerConflict with the client's CSV 116 if clusterRole != nil && ownerutil.HasOwnerConflict(c.csv, clusterRole.GetOwnerReferences()) { 117 return &rbacv1.ClusterRole{}, nil 118 } 119 120 return clusterRole, nil 121 } 122 123 func (c *CSVRuleChecker) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) { 124 // get all RoleBindings 125 crbList, err := c.clusterRoleBindingLister.List(labels.Everything()) 126 if err != nil { 127 return nil, err 128 } 129 130 // filter based on OwnerReferences 131 var filtered []*rbacv1.ClusterRoleBinding 132 for _, crb := range crbList { 133 if !ownerutil.HasOwnerConflict(c.csv, crb.GetOwnerReferences()) { 134 filtered = append(filtered, crb) 135 } 136 } 137 138 return filtered, nil 139 } 140 141 // ruleValid returns an error if the given PolicyRule is not valid (resource and nonresource attributes defined) 142 func ruleValid(rule rbacv1.PolicyRule) error { 143 if len(rule.Verbs) == 0 { 144 return fmt.Errorf("policy rule must have at least one verb") 145 } 146 147 resourceCount := len(rule.APIGroups) + len(rule.Resources) + len(rule.ResourceNames) 148 if resourceCount > 0 && len(rule.NonResourceURLs) > 0 { 149 return fmt.Errorf("rule cannot apply to both regular resources and non-resource URLs") 150 } 151 152 return nil 153 }