// ChangedClusterRoles returns the roles that must be created and/or updated to // match the recommended bootstrap policy func (o *ReconcileClusterRolesOptions) ChangedClusterRoles() ([]*authorizationapi.ClusterRole, []*authorizationapi.ClusterRole, error) { changedRoles := []*authorizationapi.ClusterRole{} skippedRoles := []*authorizationapi.ClusterRole{} rolesToReconcile := sets.NewString(o.RolesToReconcile...) rolesNotFound := sets.NewString(o.RolesToReconcile...) bootstrapClusterRoles := bootstrappolicy.GetBootstrapClusterRoles() for i := range bootstrapClusterRoles { expectedClusterRole := &bootstrapClusterRoles[i] if (len(rolesToReconcile) > 0) && !rolesToReconcile.Has(expectedClusterRole.Name) { continue } rolesNotFound.Delete(expectedClusterRole.Name) actualClusterRole, err := o.RoleClient.Get(expectedClusterRole.Name) if kapierrors.IsNotFound(err) { changedRoles = append(changedRoles, expectedClusterRole) continue } if err != nil { return nil, nil, err } // Copy any existing labels/annotations, so the displayed update is correct // This assumes bootstrap roles will not set any labels/annotations // These aren't actually used during update; the latest labels/annotations are pulled from the existing object again expectedClusterRole.Labels = actualClusterRole.Labels expectedClusterRole.Annotations = actualClusterRole.Annotations _, extraRules := rulevalidation.Covers(expectedClusterRole.Rules, actualClusterRole.Rules) _, missingRules := rulevalidation.Covers(actualClusterRole.Rules, expectedClusterRole.Rules) // We need to reconcile: // 1. if we're missing rules // 2. if there are extra rules we need to remove if (len(missingRules) > 0) || (!o.Union && len(extraRules) > 0) { if o.Union { expectedClusterRole.Rules = append(expectedClusterRole.Rules, extraRules...) } if actualClusterRole.Annotations[ReconcileProtectAnnotation] == "true" { skippedRoles = append(skippedRoles, expectedClusterRole) } else { changedRoles = append(changedRoles, expectedClusterRole) } } } if len(rolesNotFound) != 0 { // return the known changes and the error so that a caller can decide if he wants a partial update return changedRoles, skippedRoles, fmt.Errorf("did not find requested cluster role %s", rolesNotFound.List()) } return changedRoles, skippedRoles, nil }
func (d *ClusterRoles) Check() types.DiagnosticResult { r := types.NewDiagnosticResult(ClusterRolesName) reconcileOptions := &policycmd.ReconcileClusterRolesOptions{ Confirmed: false, Union: false, Out: ioutil.Discard, RoleClient: d.ClusterRolesClient.ClusterRoles(), } changedClusterRoles, err := reconcileOptions.ChangedClusterRoles() if err != nil { r.Error("CRD1000", err, fmt.Sprintf("Error inspecting ClusterRoles: %v", err)) return r } // success if len(changedClusterRoles) == 0 { return r } for _, changedClusterRole := range changedClusterRoles { actualClusterRole, err := d.ClusterRolesClient.ClusterRoles().Get(changedClusterRole.Name) if kerrs.IsNotFound(err) { r.Error("CRD1002", nil, fmt.Sprintf("clusterrole/%s is missing.\n\nUse the `oadm policy reconcile-cluster-roles` command to create the role.", changedClusterRole.Name)) continue } if err != nil { r.Error("CRD1001", err, fmt.Sprintf("Unable to get clusterrole/%s: %v", changedClusterRole.Name, err)) } _, missingRules := rulevalidation.Covers(actualClusterRole.Rules, changedClusterRole.Rules) if len(missingRules) == 0 { r.Warn("CRD1003", nil, fmt.Sprintf("clusterrole/%s has changed, but the existing role has more permissions than the new role.\n\nUse the `oadm policy reconcile-cluster-roles` command to update the role to reduce permissions.", changedClusterRole.Name)) _, extraRules := rulevalidation.Covers(changedClusterRole.Rules, actualClusterRole.Rules) for _, extraRule := range extraRules { r.Info("CRD1008", fmt.Sprintf("clusterrole/%s has extra permission %v.", changedClusterRole.Name, extraRule)) } continue } r.Error("CRD1005", nil, fmt.Sprintf("clusterrole/%s has changed and the existing role does not have enough permissions.\n\nUse the `oadm policy reconcile-cluster-roles` command to update the role.", changedClusterRole.Name)) for _, missingRule := range missingRules { r.Info("CRD1007", fmt.Sprintf("clusterrole/%s is missing permission %v.", changedClusterRole.Name, missingRule)) } r.Debug("CRD1006", fmt.Sprintf("clusterrole/%s is now %v.", changedClusterRole.Name, changedClusterRole)) } return r }
func (d *ClusterRoles) Check() types.DiagnosticResult { r := types.NewDiagnosticResult(ClusterRolesName) reconcileOptions := &policycmd.ReconcileClusterRolesOptions{ Confirmed: false, Union: false, Out: ioutil.Discard, RoleClient: d.ClusterRolesClient.ClusterRoles(), } changedClusterRoles, _, err := reconcileOptions.ChangedClusterRoles() if err != nil { r.Error("CRD1000", err, fmt.Sprintf("Error inspecting ClusterRoles: %v", err)) return r } // success if len(changedClusterRoles) == 0 { return r } for _, changedClusterRole := range changedClusterRoles { actualClusterRole, err := d.ClusterRolesClient.ClusterRoles().Get(changedClusterRole.Name) if kerrs.IsNotFound(err) { r.Error("CRD1002", nil, fmt.Sprintf(clusterRoleMissing, changedClusterRole.Name)) continue } if err != nil { r.Error("CRD1001", err, fmt.Sprintf("Unable to get clusterrole/%s: %v", changedClusterRole.Name, err)) } _, missingRules := rulevalidation.Covers(actualClusterRole.Rules, changedClusterRole.Rules) if len(missingRules) == 0 { r.Info("CRD1003", fmt.Sprintf(clusterRoleReduced, changedClusterRole.Name)) _, extraRules := rulevalidation.Covers(changedClusterRole.Rules, actualClusterRole.Rules) for _, extraRule := range extraRules { r.Info("CRD1008", fmt.Sprintf("clusterrole/%s has extra permission %v.", changedClusterRole.Name, extraRule)) } continue } r.Error("CRD1005", nil, fmt.Sprintf(clusterRoleChanged, changedClusterRole.Name)) for _, missingRule := range missingRules { r.Info("CRD1007", fmt.Sprintf("clusterrole/%s is missing permission %v.", changedClusterRole.Name, missingRule)) } r.Debug("CRD1006", fmt.Sprintf("clusterrole/%s is now %v.", changedClusterRole.Name, changedClusterRole)) } return r }
func (m *VirtualStorage) confirmNoEscalation(ctx kapi.Context, roleBinding *authorizationapi.RoleBinding) error { modifyingRole, err := m.getReferencedRole(roleBinding.RoleRef) if err != nil { return err } ruleResolver := rulevalidation.NewDefaultRuleResolver( m.PolicyRegistry, m.BindingRegistry, m.ClusterPolicyRegistry, m.ClusterPolicyBindingRegistry, ) ownerLocalRules, err := ruleResolver.GetEffectivePolicyRules(ctx) if err != nil { return kapierrors.NewInternalError(err) } masterContext := kapi.WithNamespace(ctx, "") ownerGlobalRules, err := ruleResolver.GetEffectivePolicyRules(masterContext) if err != nil { return kapierrors.NewInternalError(err) } ownerRules := make([]authorizationapi.PolicyRule, 0, len(ownerGlobalRules)+len(ownerLocalRules)) ownerRules = append(ownerRules, ownerLocalRules...) ownerRules = append(ownerRules, ownerGlobalRules...) ownerRightsCover, missingRights := rulevalidation.Covers(ownerRules, modifyingRole.Rules) if !ownerRightsCover { user, _ := kapi.UserFrom(ctx) return kapierrors.NewUnauthorized(fmt.Sprintf("attempt to grant extra privileges: %v user=%v ownerrules=%v", missingRights, user, ownerRules)) } return nil }
// ChangedClusterRoles returns the roles that must be created and/or updated to // match the recommended bootstrap policy func (o *ReconcileClusterRolesOptions) ChangedClusterRoles() ([]*authorizationapi.ClusterRole, error) { changedRoles := []*authorizationapi.ClusterRole{} bootstrapClusterRoles := bootstrappolicy.GetBootstrapClusterRoles() for i := range bootstrapClusterRoles { expectedClusterRole := &bootstrapClusterRoles[i] actualClusterRole, err := o.RoleClient.Get(expectedClusterRole.Name) if kapierrors.IsNotFound(err) { changedRoles = append(changedRoles, expectedClusterRole) continue } if err != nil { return nil, err } if !kapi.Semantic.DeepEqual(expectedClusterRole.Rules, actualClusterRole.Rules) { if o.Union { _, missingRules := rulevalidation.Covers(expectedClusterRole.Rules, actualClusterRole.Rules) expectedClusterRole.Rules = append(expectedClusterRole.Rules, missingRules...) } changedRoles = append(changedRoles, expectedClusterRole) } } return changedRoles, nil }
// ChangedClusterRoles returns the roles that must be created and/or updated to // match the recommended bootstrap policy func (o *ReconcileClusterRolesOptions) ChangedClusterRoles() ([]*authorizationapi.ClusterRole, error) { changedRoles := []*authorizationapi.ClusterRole{} bootstrapClusterRoles := bootstrappolicy.GetBootstrapClusterRoles() for i := range bootstrapClusterRoles { expectedClusterRole := &bootstrapClusterRoles[i] actualClusterRole, err := o.RoleClient.Get(expectedClusterRole.Name) if kapierrors.IsNotFound(err) { changedRoles = append(changedRoles, expectedClusterRole) continue } if err != nil { return nil, err } // Copy any existing labels/annotations, so the displayed update is correct // This assumes bootstrap roles will not set any labels/annotations // These aren't actually used during update; the latest labels/annotations are pulled from the existing object again expectedClusterRole.Labels = actualClusterRole.Labels expectedClusterRole.Annotations = actualClusterRole.Annotations if !kapi.Semantic.DeepEqual(expectedClusterRole.Rules, actualClusterRole.Rules) { if o.Union { _, missingRules := rulevalidation.Covers(expectedClusterRole.Rules, actualClusterRole.Rules) expectedClusterRole.Rules = append(expectedClusterRole.Rules, missingRules...) } changedRoles = append(changedRoles, expectedClusterRole) } } return changedRoles, nil }
// leave this in place so I can use when converting the SAs func DisableTestClusterRoles(t *testing.T) { currentRoles := bootstrappolicy.GetBootstrapClusterRoles() oldRoles := oldGetBootstrapClusterRoles() // old roles don't have the SAs appended, so run through them. The SAs haven't been converted yet for i := range oldRoles { oldRole := oldRoles[i] newRole := currentRoles[i] if oldRole.Name != newRole.Name { t.Fatalf("%v vs %v", oldRole.Name, newRole.Name) } // @liggitt don't whine about a temporary test fataling if covers, missing := rulevalidation.Covers(oldRole.Rules, newRole.Rules); !covers { t.Fatalf("%v/%v: %#v", oldRole.Name, newRole.Name, missing) } if covers, missing := rulevalidation.Covers(newRole.Rules, oldRole.Rules); !covers { t.Fatalf("%v/%v: %#v", oldRole.Name, newRole.Name, missing) } } }
func filterRulesByScopes(rules []authorizationapi.PolicyRule, scopes []string, namespace string, clusterPolicyGetter client.ClusterPolicyLister) ([]authorizationapi.PolicyRule, error) { scopeRules, err := scope.ScopesToRules(scopes, namespace, clusterPolicyGetter) if err != nil { return nil, err } filteredRules := []authorizationapi.PolicyRule{} for _, rule := range rules { if allowed, _ := rulevalidation.Covers(scopeRules, []authorizationapi.PolicyRule{rule}); allowed { filteredRules = append(filteredRules, rule) } } return filteredRules, nil }
// Some roles should always cover others func TestCovers(t *testing.T) { allRoles := bootstrappolicy.GetBootstrapClusterRoles() var admin *authorizationapi.ClusterRole var editor *authorizationapi.ClusterRole var viewer *authorizationapi.ClusterRole var registryAdmin *authorizationapi.ClusterRole var registryEditor *authorizationapi.ClusterRole var registryViewer *authorizationapi.ClusterRole for i := range allRoles { role := allRoles[i] switch role.Name { case bootstrappolicy.AdminRoleName: admin = &role case bootstrappolicy.EditRoleName: editor = &role case bootstrappolicy.ViewRoleName: viewer = &role case bootstrappolicy.RegistryAdminRoleName: registryAdmin = &role case bootstrappolicy.RegistryEditorRoleName: registryEditor = &role case bootstrappolicy.RegistryViewerRoleName: registryViewer = &role } } if covers, _ := rulevalidation.Covers(admin.Rules, editor.Rules); !covers { t.Errorf("failed to cover") } if covers, _ := rulevalidation.Covers(admin.Rules, editor.Rules); !covers { t.Errorf("failed to cover") } if covers, _ := rulevalidation.Covers(admin.Rules, viewer.Rules); !covers { t.Errorf("failed to cover") } if covers, _ := rulevalidation.Covers(admin.Rules, registryAdmin.Rules); !covers { t.Errorf("failed to cover") } if covers, _ := rulevalidation.Covers(registryAdmin.Rules, registryEditor.Rules); !covers { t.Errorf("failed to cover") } if covers, _ := rulevalidation.Covers(registryAdmin.Rules, registryViewer.Rules); !covers { t.Errorf("failed to cover") } }
// Some roles should always cover others func TestCovers(t *testing.T) { allRoles := bootstrappolicy.GetBootstrapClusterRoles() var admin *authorizationapi.ClusterRole var editor *authorizationapi.ClusterRole var viewer *authorizationapi.ClusterRole var registryAdmin *authorizationapi.ClusterRole var registryEditor *authorizationapi.ClusterRole var registryViewer *authorizationapi.ClusterRole var systemMaster *authorizationapi.ClusterRole var systemDiscovery *authorizationapi.ClusterRole var clusterAdmin *authorizationapi.ClusterRole var storageAdmin *authorizationapi.ClusterRole for i := range allRoles { role := allRoles[i] switch role.Name { case bootstrappolicy.AdminRoleName: admin = &role case bootstrappolicy.EditRoleName: editor = &role case bootstrappolicy.ViewRoleName: viewer = &role case bootstrappolicy.RegistryAdminRoleName: registryAdmin = &role case bootstrappolicy.RegistryEditorRoleName: registryEditor = &role case bootstrappolicy.RegistryViewerRoleName: registryViewer = &role case bootstrappolicy.MasterRoleName: systemMaster = &role case bootstrappolicy.DiscoveryRoleName: systemDiscovery = &role case bootstrappolicy.ClusterAdminRoleName: clusterAdmin = &role case bootstrappolicy.StorageAdminRoleName: storageAdmin = &role } } if covers, miss := rulevalidation.Covers(admin.Rules, editor.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } if covers, miss := rulevalidation.Covers(admin.Rules, editor.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } if covers, miss := rulevalidation.Covers(admin.Rules, viewer.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } if covers, miss := rulevalidation.Covers(admin.Rules, registryAdmin.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } if covers, miss := rulevalidation.Covers(clusterAdmin.Rules, storageAdmin.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } if covers, miss := rulevalidation.Covers(registryAdmin.Rules, registryEditor.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } if covers, miss := rulevalidation.Covers(registryAdmin.Rules, registryViewer.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } // Make sure we can auto-reconcile discovery if covers, miss := rulevalidation.Covers(systemMaster.Rules, systemDiscovery.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } // Make sure the master has full permissions if covers, miss := rulevalidation.Covers(systemMaster.Rules, clusterAdmin.Rules); !covers { t.Errorf("failed to cover: %#v", miss) } }