-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat: Provision minimal TokenReview RBAC for OIDC auth and add SSL error logging in token parser #6240
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
base: master
Are you sure you want to change the base?
feat: Provision minimal TokenReview RBAC for OIDC auth and add SSL error logging in token parser #6240
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ import ( | |
| // Deploy the feast authorization | ||
| func (authz *FeastAuthorization) Deploy() error { | ||
| if authz.isKubernetesAuth() { | ||
| authz.cleanupOidcRbac() | ||
| return authz.deployKubernetesAuth() | ||
| } | ||
|
|
||
|
|
@@ -27,12 +28,14 @@ func (authz *FeastAuthorization) Deploy() error { | |
| apimeta.RemoveStatusCondition(&authz.Handler.FeatureStore.Status.Conditions, feastKubernetesAuthConditions[metav1.ConditionTrue].Type) | ||
|
|
||
| if authz.isOidcAuth() { | ||
| if err := authz.createFeastClusterRole(); err != nil { | ||
| if err := authz.createOidcClusterRole(); err != nil { | ||
| return err | ||
| } | ||
| if err := authz.createFeastClusterRoleBinding(); err != nil { | ||
| if err := authz.createOidcClusterRoleBinding(); err != nil { | ||
| return err | ||
| } | ||
| } else { | ||
| authz.cleanupOidcRbac() | ||
| } | ||
|
|
||
| return nil | ||
|
|
@@ -327,6 +330,73 @@ func (authz *FeastAuthorization) setAuthRole(role *rbacv1.Role) error { | |
| return controllerutil.SetControllerReference(authz.Handler.FeatureStore, role, authz.Handler.Scheme) | ||
| } | ||
|
|
||
| func (authz *FeastAuthorization) createOidcClusterRole() error { | ||
| logger := log.FromContext(authz.Handler.Context) | ||
| clusterRole := &rbacv1.ClusterRole{ | ||
| ObjectMeta: metav1.ObjectMeta{Name: authz.getOidcClusterRoleName()}, | ||
| } | ||
| clusterRole.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("ClusterRole")) | ||
| if op, err := controllerutil.CreateOrUpdate(authz.Handler.Context, authz.Handler.Client, clusterRole, controllerutil.MutateFn(func() error { | ||
| clusterRole.Labels = authz.getLabels() | ||
| clusterRole.Rules = []rbacv1.PolicyRule{ | ||
| { | ||
| APIGroups: []string{"authentication.k8s.io"}, | ||
| Resources: []string{"tokenreviews"}, | ||
| Verbs: []string{"create"}, | ||
| }, | ||
| } | ||
| return nil | ||
| })); err != nil { | ||
| return err | ||
| } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { | ||
| logger.Info("Successfully reconciled", "ClusterRole", clusterRole.Name, "operation", op) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func (authz *FeastAuthorization) createOidcClusterRoleBinding() error { | ||
| logger := log.FromContext(authz.Handler.Context) | ||
| crb := &rbacv1.ClusterRoleBinding{ | ||
| ObjectMeta: metav1.ObjectMeta{Name: authz.getOidcClusterRoleBindingName()}, | ||
| } | ||
| crb.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("ClusterRoleBinding")) | ||
| if op, err := controllerutil.CreateOrUpdate(authz.Handler.Context, authz.Handler.Client, crb, controllerutil.MutateFn(func() error { | ||
| crb.Labels = authz.getLabels() | ||
| crb.Subjects = []rbacv1.Subject{ | ||
| { | ||
| Kind: "ServiceAccount", | ||
| Name: authz.getFeastServiceAccountName(), | ||
| Namespace: authz.Handler.FeatureStore.Namespace, | ||
| }, | ||
| } | ||
| crb.RoleRef = rbacv1.RoleRef{ | ||
| APIGroup: rbacv1.GroupName, | ||
| Kind: "ClusterRole", | ||
| Name: authz.getOidcClusterRoleName(), | ||
| } | ||
| return nil | ||
| })); err != nil { | ||
| return err | ||
| } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { | ||
| logger.Info("Successfully reconciled", "ClusterRoleBinding", crb.Name, "operation", op) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func (authz *FeastAuthorization) cleanupOidcRbac() { | ||
| crb := &rbacv1.ClusterRoleBinding{ObjectMeta: metav1.ObjectMeta{Name: authz.getOidcClusterRoleBindingName()}} | ||
| crb.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("ClusterRoleBinding")) | ||
| _ = authz.Handler.Client.Delete(authz.Handler.Context, crb) | ||
| } | ||
devin-ai-integration[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| func (authz *FeastAuthorization) getOidcClusterRoleName() string { | ||
| return "feast-oidc-token-review" | ||
| } | ||
|
|
||
| func (authz *FeastAuthorization) getOidcClusterRoleBindingName() string { | ||
| return services.GetFeastName(authz.Handler.FeatureStore) + "-oidc-token-review" | ||
| } | ||
|
|
||
| func (authz *FeastAuthorization) getLabels() map[string]string { | ||
| return map[string]string{ | ||
| services.NameLabelKey: authz.Handler.FeatureStore.Name, | ||
|
Comment on lines
400
to
402
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getLabels() stamps the FeatureStore-specific name. But the ClusterRole feast-oidc-token-review is shared across all OIDC FeatureStore instances. The last instance to reconcile overwrites the labels with its own name. This creates misleading audit trails — the ClusterRole appears to belong to one FeatureStore when it actually serves all of them. |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 Orphaned Kubernetes auth ClusterRoleBinding when transitioning from Kubernetes auth to OIDC auth
Before this PR, the OIDC auth path in
Deploy()calledcreateFeastClusterRoleBinding(), which used the same CRB name as Kubernetes auth (GetFeastName(fs) + "-cluster-binding"atinfra/feast-operator/internal/controller/authz/authz.go:444). Switching from Kubernetes to OIDC would therefore update the existing CRB in place viaCreateOrUpdate. After this PR, the OIDC path creates a separate CRB with a different name (GetFeastName(fs) + "-oidc-token-review"at line 397). When transitioning from Kubernetes auth to OIDC, the old Kubernetes CRB (feast-{name}-cluster-binding) is never deleted — it has no owner reference (setFeastClusterRoleBindingat line 202 returnsnilinstead of callingSetControllerReference), so it won't be garbage collected either. The orphaned CRB continues to bind the service account to the broad Kubernetes auth ClusterRole (which grants access tosubjectaccessreviews,rolebindings,namespaces,clusterroles,clusterrolebindingsper lines 142-178), even though OIDC only needstokenreviews:create. The PR correctly addscleanupOidcRbac()for the OIDC→Kubernetes transition (line 21), but there is no symmetric cleanup of the Kubernetes CRB when transitioning away from Kubernetes auth.Prompt for agents
Was this helpful? React with 👍 or 👎 to provide feedback.