Merge pull request #65909 from liggitt/rbac-escalation-msg

Automatic merge from submit-queue (batch tested with PRs 65897, 65909, 65856, 65815). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

make RBAC escalation error message more useful

Fixes #65804

Tested by granting a namespaced admin role to a user, then attempt to grant a broader role as that user:

```
kubectl create rolebinding user1-admin --clusterrole=admin --user=user1
kubectl create rolebinding user2-volume --as=user1 --clusterrole=system:volume-scheduler --user=user2
```

before:
> Error from server (Forbidden): rolebindings.rbac.authorization.k8s.io "user2-volume" is forbidden: attempt to grant extra privileges: [{[get] [] [persistentvolumes] [] []} {[list] [] [persistentvolumes] [] []} {[patch] [] [persistentvolumes] [] []} {[update] [] [persistentvolumes] [] []} {[watch] [] [persistentvolumes] [] []} {[get] [storage.k8s.io] [storageclasses] [] []} {[list] [storage.k8s.io] [storageclasses] [] []} {[watch] [storage.k8s.io] [storageclasses] [] []}] user=&{user1  [system:authenticated] map[]} ownerrules=[{[create] [authorization.k8s.io] [selfsubjectaccessreviews selfsubjectrulesreviews] [] []} {[get] [] [] [] [/api /api/* /apis /apis/* /healthz /openapi /openapi/* /swagger-2.0.0.pb-v1 /swagger.json /swaggerapi /swaggerapi/* /version /version/]} {[create delete deletecollection get list patch update watch] [] [pods pods/attach pods/exec pods/portforward pods/proxy] [] []} {[create delete deletecollection get list patch update watch] [] [configmaps endpoints persistentvolumeclaims replicationcontrollers replicationcontrollers/scale secrets serviceaccounts services services/proxy] [] []} {[get list watch] [] [bindings events limitranges namespaces/status pods/log pods/status replicationcontrollers/status resourcequotas resourcequotas/status] [] []} {[get list watch] [] [namespaces] [] []} {[impersonate] [] [serviceaccounts] [] []} {[create delete deletecollection get list patch update watch] [apps] [daemonsets deployments deployments/rollback deployments/scale replicasets replicasets/scale statefulsets statefulsets/scale] [] []} {[create delete deletecollection get list patch update watch] [autoscaling] [horizontalpodautoscalers] [] []} {[create delete deletecollection get list patch update watch] [batch] [cronjobs jobs] [] []} {[create delete deletecollection get list patch update watch] [extensions] [daemonsets deployments deployments/rollback deployments/scale ingresses networkpolicies replicasets replicasets/scale replicationcontrollers/scale] [] []} {[create delete deletecollection get list patch update watch] [policy] [poddisruptionbudgets] [] []} {[create delete deletecollection get list patch update watch] [networking.k8s.io] [networkpolicies] [] []} {[create] [authorization.k8s.io] [localsubjectaccessreviews] [] []} {[create delete deletecollection get list patch update watch] [rbac.authorization.k8s.io] [rolebindings roles] [] []}] ruleResolutionErrors=[]

after
> Error from server (Forbidden): rolebindings.rbac.authorization.k8s.io "user2-volume" is forbidden: user "user1" (groups=["system:authenticated"]) is attempting to grant RBAC permissions not currently held:
> {APIGroups:[""], Resources:["persistentvolumes"], Verbs:["get" "list" "patch" "update" "watch"]}
> {APIGroups:["storage.k8s.io"], Resources:["storageclasses"], Verbs:["get" "list" "watch"]}
This commit is contained in:
Kubernetes Submit Queue 2018-07-06 12:25:05 -07:00 committed by GitHub
commit 5b9cc7fb79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 3 deletions

View File

@ -36,8 +36,8 @@ go_library(
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/rbac/v1:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",

View File

@ -20,15 +20,17 @@ import (
"context"
"errors"
"fmt"
"strings"
"github.com/golang/glog"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/authentication/serviceaccount"
"k8s.io/apiserver/pkg/authentication/user"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
)
type AuthorizationRuleResolver interface {
@ -65,7 +67,22 @@ func ConfirmNoEscalation(ctx context.Context, ruleResolver AuthorizationRuleReso
ownerRightsCover, missingRights := Covers(ownerRules, rules)
if !ownerRightsCover {
return apierrors.NewUnauthorized(fmt.Sprintf("attempt to grant extra privileges: %v user=%v ownerrules=%v ruleResolutionErrors=%v", missingRights, user, ownerRules, ruleResolutionErrors))
compactMissingRights := missingRights
if compact, err := CompactRules(missingRights); err == nil {
compactMissingRights = compact
}
missingDescriptions := sets.NewString()
for _, missing := range compactMissingRights {
missingDescriptions.Insert(rbacv1helpers.CompactString(missing))
}
msg := fmt.Sprintf("user %q (groups=%q) is attempting to grant RBAC permissions not currently held:\n%s", user.GetName(), user.GetGroups(), strings.Join(missingDescriptions.List(), "\n"))
if len(ruleResolutionErrors) > 0 {
msg = msg + fmt.Sprintf("; resolution errors: %v", ruleResolutionErrors)
}
return errors.New(msg)
}
return nil
}