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  }