'kubectl auth can-i` command would not hint user when they try to access

some resource out of scope. For example, try get namespace inside defaut namespace.
It would be reject by api-server but `kubectl auth can-i get namespace --namespace=default`
would give a `yes`. After this patch, a warning info would be given.
For more detail, please refer issue #75950
k3s-v1.15.3
WanLinghao 2019-04-02 17:26:30 +08:00
parent aa74064600
commit 7fbd71835e
3 changed files with 58 additions and 5 deletions

View File

@ -34,6 +34,7 @@ go_library(
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library", "//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
"//staging/src/k8s.io/cli-runtime/pkg/printers:go_default_library", "//staging/src/k8s.io/cli-runtime/pkg/printers:go_default_library",
"//staging/src/k8s.io/cli-runtime/pkg/resource:go_default_library", "//staging/src/k8s.io/cli-runtime/pkg/resource:go_default_library",
"//staging/src/k8s.io/client-go/discovery:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",

View File

@ -33,6 +33,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericclioptions"
discovery "k8s.io/client-go/discovery"
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1" authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
describeutil "k8s.io/kubernetes/pkg/kubectl/describe/versioned" describeutil "k8s.io/kubernetes/pkg/kubectl/describe/versioned"
@ -49,6 +50,7 @@ type CanIOptions struct {
NoHeaders bool NoHeaders bool
Namespace string Namespace string
AuthClient authorizationv1client.AuthorizationV1Interface AuthClient authorizationv1client.AuthorizationV1Interface
DiscoveryClient discovery.DiscoveryInterface
Verb string Verb string
Resource schema.GroupVersionResource Resource schema.GroupVersionResource
@ -169,6 +171,7 @@ func (o *CanIOptions) Complete(f cmdutil.Factory, args []string) error {
return err return err
} }
o.AuthClient = client.AuthorizationV1() o.AuthClient = client.AuthorizationV1()
o.DiscoveryClient = client.Discovery()
o.Namespace = "" o.Namespace = ""
if !o.AllNamespaces { if !o.AllNamespaces {
o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
@ -196,6 +199,14 @@ func (o *CanIOptions) Validate() error {
if o.Resource != (schema.GroupVersionResource{}) || o.ResourceName != "" { if o.Resource != (schema.GroupVersionResource{}) || o.ResourceName != "" {
return fmt.Errorf("NonResourceURL and ResourceName can not specified together") return fmt.Errorf("NonResourceURL and ResourceName can not specified together")
} }
} else if !o.Resource.Empty() && !o.AllNamespaces && o.DiscoveryClient != nil {
if namespaced, err := isNamespaced(o.Resource, o.DiscoveryClient); err == nil && !namespaced {
if len(o.Resource.Group) == 0 {
fmt.Fprintf(o.ErrOut, "Warning: resource '%s' is not namespace scoped\n", o.Resource.Resource)
} else {
fmt.Fprintf(o.ErrOut, "Warning: resource '%s' is not namespace scoped in group '%s'\n", o.Resource.Resource, o.Resource.Group)
}
}
} }
if o.NoHeaders { if o.NoHeaders {
@ -360,3 +371,23 @@ func printAccess(out io.Writer, rules []rbacv1.PolicyRule) error {
} }
return nil return nil
} }
func isNamespaced(gvr schema.GroupVersionResource, discoveryClient discovery.DiscoveryInterface) (bool, error) {
if gvr.Resource == "*" {
return true, nil
}
apiResourceList, err := discoveryClient.ServerResourcesForGroupVersion(schema.GroupVersion{
Group: gvr.Group, Version: gvr.Version,
}.String())
if err != nil {
return true, err
}
for _, resource := range apiResourceList.APIResources {
if resource.Name == gvr.Resource {
return resource.Namespaced, nil
}
}
return false, fmt.Errorf("the server doesn't have a resource type '%s' in group '%s'", gvr.Resource, gvr.Group)
}

View File

@ -761,6 +761,27 @@ runTests() {
output_message=$(kubectl auth can-i get pods --subresource=log --quiet 2>&1 "${kube_flags[@]}"; echo $?) output_message=$(kubectl auth can-i get pods --subresource=log --quiet 2>&1 "${kube_flags[@]}"; echo $?)
kube::test::if_has_string "${output_message}" '0' kube::test::if_has_string "${output_message}" '0'
# kubectl auth can-i get '*' does not warn about namespaced scope or print an error
output_message=$(kubectl auth can-i get '*' 2>&1 "${kube_flags[@]}")
kube::test::if_has_not_string "${output_message}" "Warning"
# kubectl auth can-i get foo does not print a namespaced warning message, and only prints a single lookup error
output_message=$(kubectl auth can-i get foo 2>&1 "${kube_flags[@]}")
kube::test::if_has_string "${output_message}" "Warning: the server doesn't have a resource type 'foo'"
kube::test::if_has_not_string "${output_message}" "Warning: resource 'foo' is not namespace scoped"
# kubectl auth can-i get pods does not print a namespaced warning message or a lookup error
output_message=$(kubectl auth can-i get pods 2>&1 "${kube_flags[@]}")
kube::test::if_has_not_string "${output_message}" "Warning"
# kubectl auth can-i get nodes prints a namespaced warning message
output_message=$(kubectl auth can-i get nodes 2>&1 "${kube_flags[@]}")
kube::test::if_has_string "${output_message}" "Warning: resource 'nodes' is not namespace scoped"
# kubectl auth can-i get nodes --all-namespaces does not print a namespaced warning message
output_message=$(kubectl auth can-i get nodes --all-namespaces 2>&1 "${kube_flags[@]}")
kube::test::if_has_not_string "${output_message}" "Warning: resource 'nodes' is not namespace scoped"
fi fi
# kubectl auth reconcile # kubectl auth reconcile