Skip to content

Commit 4c4c378

Browse files
authored
Merge pull request kubernetes#76014 from WanLinghao/auth_can-i_improve
Improve `kubectl auth can-i` command by warning users when they try access resource out of scope
2 parents 701e36b + 7fbd718 commit 4c4c378

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed

pkg/kubectl/cmd/auth/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ go_library(
3434
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
3535
"//staging/src/k8s.io/cli-runtime/pkg/printers:go_default_library",
3636
"//staging/src/k8s.io/cli-runtime/pkg/resource:go_default_library",
37+
"//staging/src/k8s.io/client-go/discovery:go_default_library",
3738
"//staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1:go_default_library",
3839
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
3940
"//staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",

pkg/kubectl/cmd/auth/cani.go

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"k8s.io/apimachinery/pkg/runtime/schema"
3434
utilerrors "k8s.io/apimachinery/pkg/util/errors"
3535
"k8s.io/cli-runtime/pkg/genericclioptions"
36+
discovery "k8s.io/client-go/discovery"
3637
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
3738
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
3839
describeutil "k8s.io/kubernetes/pkg/kubectl/describe/versioned"
@@ -44,11 +45,12 @@ import (
4445
// CanIOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
4546
// referencing the cmd.Flags()
4647
type CanIOptions struct {
47-
AllNamespaces bool
48-
Quiet bool
49-
NoHeaders bool
50-
Namespace string
51-
AuthClient authorizationv1client.AuthorizationV1Interface
48+
AllNamespaces bool
49+
Quiet bool
50+
NoHeaders bool
51+
Namespace string
52+
AuthClient authorizationv1client.AuthorizationV1Interface
53+
DiscoveryClient discovery.DiscoveryInterface
5254

5355
Verb string
5456
Resource schema.GroupVersionResource
@@ -169,6 +171,7 @@ func (o *CanIOptions) Complete(f cmdutil.Factory, args []string) error {
169171
return err
170172
}
171173
o.AuthClient = client.AuthorizationV1()
174+
o.DiscoveryClient = client.Discovery()
172175
o.Namespace = ""
173176
if !o.AllNamespaces {
174177
o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
@@ -196,6 +199,14 @@ func (o *CanIOptions) Validate() error {
196199
if o.Resource != (schema.GroupVersionResource{}) || o.ResourceName != "" {
197200
return fmt.Errorf("NonResourceURL and ResourceName can not specified together")
198201
}
202+
} else if !o.Resource.Empty() && !o.AllNamespaces && o.DiscoveryClient != nil {
203+
if namespaced, err := isNamespaced(o.Resource, o.DiscoveryClient); err == nil && !namespaced {
204+
if len(o.Resource.Group) == 0 {
205+
fmt.Fprintf(o.ErrOut, "Warning: resource '%s' is not namespace scoped\n", o.Resource.Resource)
206+
} else {
207+
fmt.Fprintf(o.ErrOut, "Warning: resource '%s' is not namespace scoped in group '%s'\n", o.Resource.Resource, o.Resource.Group)
208+
}
209+
}
199210
}
200211

201212
if o.NoHeaders {
@@ -360,3 +371,23 @@ func printAccess(out io.Writer, rules []rbacv1.PolicyRule) error {
360371
}
361372
return nil
362373
}
374+
375+
func isNamespaced(gvr schema.GroupVersionResource, discoveryClient discovery.DiscoveryInterface) (bool, error) {
376+
if gvr.Resource == "*" {
377+
return true, nil
378+
}
379+
apiResourceList, err := discoveryClient.ServerResourcesForGroupVersion(schema.GroupVersion{
380+
Group: gvr.Group, Version: gvr.Version,
381+
}.String())
382+
if err != nil {
383+
return true, err
384+
}
385+
386+
for _, resource := range apiResourceList.APIResources {
387+
if resource.Name == gvr.Resource {
388+
return resource.Namespaced, nil
389+
}
390+
}
391+
392+
return false, fmt.Errorf("the server doesn't have a resource type '%s' in group '%s'", gvr.Resource, gvr.Group)
393+
}

test/cmd/legacy-script.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,27 @@ runTests() {
761761

762762
output_message=$(kubectl auth can-i get pods --subresource=log --quiet 2>&1 "${kube_flags[@]}"; echo $?)
763763
kube::test::if_has_string "${output_message}" '0'
764+
765+
# kubectl auth can-i get '*' does not warn about namespaced scope or print an error
766+
output_message=$(kubectl auth can-i get '*' 2>&1 "${kube_flags[@]}")
767+
kube::test::if_has_not_string "${output_message}" "Warning"
768+
769+
# kubectl auth can-i get foo does not print a namespaced warning message, and only prints a single lookup error
770+
output_message=$(kubectl auth can-i get foo 2>&1 "${kube_flags[@]}")
771+
kube::test::if_has_string "${output_message}" "Warning: the server doesn't have a resource type 'foo'"
772+
kube::test::if_has_not_string "${output_message}" "Warning: resource 'foo' is not namespace scoped"
773+
774+
# kubectl auth can-i get pods does not print a namespaced warning message or a lookup error
775+
output_message=$(kubectl auth can-i get pods 2>&1 "${kube_flags[@]}")
776+
kube::test::if_has_not_string "${output_message}" "Warning"
777+
778+
# kubectl auth can-i get nodes prints a namespaced warning message
779+
output_message=$(kubectl auth can-i get nodes 2>&1 "${kube_flags[@]}")
780+
kube::test::if_has_string "${output_message}" "Warning: resource 'nodes' is not namespace scoped"
781+
782+
# kubectl auth can-i get nodes --all-namespaces does not print a namespaced warning message
783+
output_message=$(kubectl auth can-i get nodes --all-namespaces 2>&1 "${kube_flags[@]}")
784+
kube::test::if_has_not_string "${output_message}" "Warning: resource 'nodes' is not namespace scoped"
764785
fi
765786

766787
# kubectl auth reconcile

0 commit comments

Comments
 (0)