Skip to content

Ocpbugs 32262 #3518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/blang/semver/v4 v4.0.0
github.com/containers/image/v5 v5.34.0
github.com/coreos/go-semver v0.3.1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/distribution/reference v0.6.0
github.com/evanphx/json-patch v5.9.11+incompatible
github.com/fsnotify/fsnotify v1.8.0
Expand Down Expand Up @@ -82,7 +83,6 @@ require (
github.com/containers/ocicrypt v1.2.1 // indirect
github.com/containers/storage v1.57.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v27.5.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v27.5.1+incompatible // indirect
Expand Down
138 changes: 138 additions & 0 deletions pkg/controller/registry/resolver/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import (
"crypto/sha256"
"encoding/json"
"fmt"
"hash/fnv"
"math/big"

utilrand "k8s.io/apimachinery/pkg/util/rand"

"github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
hashutil "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/hash"
Expand All @@ -29,6 +32,16 @@ func generateName(base string, o interface{}) (string, error) {
return fmt.Sprintf("%s-%s", base, hash), nil
}

func legacyGenerateName(base string, o interface{}) string {
hasher := fnv.New32a()
hashutil.LegacyDeepHashObject(hasher, o)
hash := utilrand.SafeEncodeString(fmt.Sprint(hasher.Sum32()))
if len(base)+len(hash) > maxNameLength {
base = base[:maxNameLength-len(hash)-1]
}
return fmt.Sprintf("%s-%s", base, hash)
}

type OperatorPermissions struct {
ServiceAccount *corev1.ServiceAccount
Roles []*rbacv1.Role
Expand Down Expand Up @@ -233,3 +246,128 @@ func RBACForClusterServiceVersion(csv *v1alpha1.ClusterServiceVersion) (map[stri
}
return permissions, nil
}

func LegacyRBACForClusterServiceVersion(csv *v1alpha1.ClusterServiceVersion) (map[string]*OperatorPermissions, error) {
permissions := map[string]*OperatorPermissions{}

// Use a StrategyResolver to get the strategy details
strategyResolver := install.StrategyResolver{}
strategy, err := strategyResolver.UnmarshalStrategy(csv.Spec.InstallStrategy)
if err != nil {
return nil, err
}

// Assume the strategy is for a deployment
strategyDetailsDeployment, ok := strategy.(*v1alpha1.StrategyDetailsDeployment)
if !ok {
return nil, fmt.Errorf("could not assert strategy implementation as deployment for CSV %s", csv.GetName())
}

// Resolve Permissions
for _, permission := range strategyDetailsDeployment.Permissions {
// Create ServiceAccount if necessary
if _, ok := permissions[permission.ServiceAccountName]; !ok {
serviceAccount := &corev1.ServiceAccount{}
serviceAccount.SetNamespace(csv.GetNamespace())
serviceAccount.SetName(permission.ServiceAccountName)
ownerutil.AddNonBlockingOwner(serviceAccount, csv)

permissions[permission.ServiceAccountName] = NewOperatorPermissions(serviceAccount)
}

// Create Role
role := &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: legacyGenerateName(fmt.Sprintf("%s-%s", csv.GetName(), permission.ServiceAccountName), []interface{}{csv.GetName(), permission}),
Namespace: csv.GetNamespace(),
OwnerReferences: []metav1.OwnerReference{ownerutil.NonBlockingOwner(csv)},
Labels: ownerutil.OwnerLabel(csv, v1alpha1.ClusterServiceVersionKind),
},
Rules: permission.Rules,
}
hash, err := PolicyRuleHashLabelValue(permission.Rules)
if err != nil {
return nil, fmt.Errorf("failed to hash permission rules: %w", err)
}
role.Labels[ContentHashLabelKey] = hash
permissions[permission.ServiceAccountName].AddRole(role)

// Create RoleBinding
roleBinding := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: role.GetName(),
Namespace: csv.GetNamespace(),
OwnerReferences: []metav1.OwnerReference{ownerutil.NonBlockingOwner(csv)},
Labels: ownerutil.OwnerLabel(csv, v1alpha1.ClusterServiceVersionKind),
},
RoleRef: rbacv1.RoleRef{
Kind: "Role",
Name: role.GetName(),
APIGroup: rbacv1.GroupName},
Subjects: []rbacv1.Subject{{
Kind: "ServiceAccount",
Name: permission.ServiceAccountName,
Namespace: csv.GetNamespace(),
}},
}
hash, err = RoleReferenceAndSubjectHashLabelValue(roleBinding.RoleRef, roleBinding.Subjects)
if err != nil {
return nil, fmt.Errorf("failed to hash binding content: %w", err)
}
roleBinding.Labels[ContentHashLabelKey] = hash
permissions[permission.ServiceAccountName].AddRoleBinding(roleBinding)
}

// Resolve ClusterPermissions as StepResources
for _, permission := range strategyDetailsDeployment.ClusterPermissions {
// Create ServiceAccount if necessary
if _, ok := permissions[permission.ServiceAccountName]; !ok {
serviceAccount := &corev1.ServiceAccount{}
ownerutil.AddOwner(serviceAccount, csv, false, false)
serviceAccount.SetName(permission.ServiceAccountName)

permissions[permission.ServiceAccountName] = NewOperatorPermissions(serviceAccount)
}

// Create ClusterRole
role := &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: legacyGenerateName(csv.GetName(), []interface{}{csv.GetName(), csv.GetNamespace(), permission}),
Labels: ownerutil.OwnerLabel(csv, v1alpha1.ClusterServiceVersionKind),
},
Rules: permission.Rules,
}
hash, err := PolicyRuleHashLabelValue(permission.Rules)
if err != nil {
return nil, fmt.Errorf("failed to hash permission rules: %w", err)
}
role.Labels[ContentHashLabelKey] = hash
permissions[permission.ServiceAccountName].AddClusterRole(role)

// Create ClusterRoleBinding
roleBinding := &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: role.GetName(),
Namespace: csv.GetNamespace(),
Labels: ownerutil.OwnerLabel(csv, v1alpha1.ClusterServiceVersionKind),
},
RoleRef: rbacv1.RoleRef{
Kind: "ClusterRole",
Name: role.GetName(),
APIGroup: rbacv1.GroupName,
},
Subjects: []rbacv1.Subject{{
Kind: "ServiceAccount",
Name: permission.ServiceAccountName,
Namespace: csv.GetNamespace(),
}},
}
hash, err = RoleReferenceAndSubjectHashLabelValue(roleBinding.RoleRef, roleBinding.Subjects)
if err != nil {
return nil, fmt.Errorf("failed to hash binding content: %w", err)
}
roleBinding.Labels[ContentHashLabelKey] = hash
permissions[permission.ServiceAccountName].AddClusterRoleBinding(roleBinding)
}
return permissions, nil
}
7 changes: 7 additions & 0 deletions pkg/controller/registry/resolver/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,13 @@ func NewServiceAccountStepResources(csv *v1alpha1.ClusterServiceVersion, catalog
if err != nil {
return nil, err
}
legacyPerms, err := LegacyRBACForClusterServiceVersion(csv)
if err != nil {
return nil, err
}
for k, v := range legacyPerms {
operatorPermissions[k] = v
}

for _, perms := range operatorPermissions {
if perms.ServiceAccount.Name != "default" {
Expand Down
14 changes: 14 additions & 0 deletions pkg/lib/kubernetes/pkg/util/hash/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import (
"crypto/sha256"
"encoding/json"
"fmt"
"hash"
"math/big"

"github.com/davecgh/go-spew/spew"
)

// DeepHashObject writes specified object to hash using the spew library
Expand Down Expand Up @@ -53,3 +56,14 @@ func DeepHashObject(obj interface{}) (string, error) {
i.SetBytes(hash[:])
return i.Text(62), nil
}

func LegacyDeepHashObject(hasher hash.Hash, objectToWrite interface{}) {
hasher.Reset()
printer := spew.ConfigState{
Indent: " ",
SortKeys: true,
DisableMethods: true,
SpewKeys: true,
}
printer.Fprintf(hasher, "%#v", objectToWrite)
}
Loading