Change:
In kubectl v1.8.0+, schema info is cached in the location specified by (defaulting to ), written with world-writeable permissions (rw-rw-rw-). If is specified and pointed at a different location accessible to other users/groups, the written files may be modified by other users/groups and disrupt the kubectl invocation. CVSS score: CVSS:3.0/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N (3.3, low) What versions are affected? kubectl v1.8.0+ What configurations are affected? Invocations that point at world-writeable locations Impact Malformed responses written to the cache directory can disrupt the kubectl invocation Workaround Use the default location in the $HOME directory or point it at a directory that is only accessible to desired users/groups. (original description follows) ==== What happened: The files inside of ".kube/http-cache" are world writeable (rw-rw-rw-). While the default for these files appears to be the home directory, using the "--cache-dir" flag could put these files into a place where world writeable files would allow any user / process to modify the cache files. Modification of the cache files could influence the kubectl utility in a negative way for other users. What you expected to happen: Apply stricter file permissions to the http-cache files. How to reproduce it (as minimally and precisely as possible): Run any generic kubectl command which is successful and then list the cache directory $ kubectl get pods --all-namespaces $ ls -la Anything else we need to know?: I estimate this is a low severity security issue with a CVSS score of "3.3 / CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N" - Environment: Linux Kubernetes version (use kubectl version):Client Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.6", GitCommit:"", GitTreeState:"clean", BuildDate:"2019-02-26T12:49:28Z", GoVersion:"go1.10.8", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.6", GitCommit:"", GitTreeState:"clean", BuildDate:"2019-02-26T12:49:28Z", GoVersion:"go1.10.8", Compiler:"gc", Platform:"linux/amd64"} Cloud provider or hardware configuration: AWS. Running kube api server in hyperkube. OS (e.g: cat /etc/os-release): NAME="CentOS Linux" VERSION="7.1808 (Core)" ID="centos" IDLIKE="rhel fedora" VERSIONID="7" PRETTYNAME="CentOS Linux 7.1808 (Core)" ANSICOLOR="0;31" CPENAME="cpe:/o:centos:centos:7" HOMEURL="" BUGREPORTURL="" CENTOSMANTISBTPROJECT="CentOS-7" CENTOSMANTISBTPROJECTVERSION="7" REDHATSUPPORTPRODUCT="centos" REDHATSUPPORTPRODUCTVERSION="7" OSTREEVERSION=7.1808 Kernel (e.g. uname -a): Linux hackit.internal 3.10.0-862.11.6.el7.x8664 SMP Tue Aug 14 21:49:04 UTC 2018 x8664 x8664 x86_64 GNU/Linux Install tools: Manual installation. Others: n/a
/sig product-security
Closing this issue. <detailsIn response to : if you think this is serious, send an email thanks! </details
Adjusted the CVSS score to to indicate user interaction is required (since they must specify a non-default cache option pointed at a shared writeable location)
There are two places this should be done: PathPerm and FilePerm should be set here: the temp file and final file should have stricter permissions (probably 0660) here:
/cc
/help
This request has been marked as needing help from a contributor. Please ensure the request meets the requirements listed . If this request no longer meets these requirements, the label can be removed by commenting with the command. <detailsIn response to : /help Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/assign
/assign I would like to work on release-1.9, as I just made a hot fix for our team's k8s version of 1.9.
GitHub didn't allow me to assign the following users: yuchengwu. Note that only and repo collaborators can be assigned and that issues/PRs can only have 10 assignees at the same time. For more information please see <detailsIn response to : /assign I would like to work on release-1.9, as I just made a hot fix for our team's k8s version of 1.9. Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
help fixing this is welcome, though it would only be fixed in releases 1.12+, per
/label official-cve-feed (Related to kubernetes/sig-security)

Discussion:
} func (d *CachedDiscoveryClient) writeCachedFile(filename string, obj runtime.Object) error { <del> if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { </del> <ins> if err := os.MkdirAll(filepath.Dir(filename), 0750); err != nil { </ins> return err }

return err } <del> err = os.Chmod(f.Name(), 0755) </del> <ins> err = os.Chmod(f.Name(), 0660) </ins> if err != nil { return err }

import ( "io/ioutil" "os" <ins> "path/filepath" </ins> "testing" "time"

assert.Equal(c.groupCalls, 2) } <ins> func TestNewCachedDiscoveryClient_PathPerm(t *testing.T) { assert := assert.New(t) d, err := ioutil.TempDir("", "") assert.NoError(err) os.RemoveAll(d) defer os.RemoveAll(d) c := fakeDiscoveryClient{} cdc := newCachedDiscoveryClient(&c, d, 1*time.Nanosecond) cdc.ServerGroups() err = filepath.Walk(d, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { assert.Equal(os.FileMode(0750), info.Mode().Perm()) } else { assert.Equal(os.FileMode(0660), info.Mode().Perm()) } return nil }) assert.NoError(err) } </ins> type fakeDiscoveryClient struct { groupCalls    int resourceCalls int

import ( "net/http" <ins> "os" </ins> "path/filepath" "github.com/gregjones/httpcache"

// corresponding requests. func newCacheRoundTripper(cacheDir string, rt http.RoundTripper) http.RoundTripper { d := diskv.New(diskv.Options{ <ins> PathPerm: os.FileMode(0750), FilePerm: os.FileMode(0660), </ins> BasePath: cacheDir, TempDir:  filepath.Join(cacheDir, ".diskv-temp"), })

"net/http" "net/url" "os" <ins> "path/filepath" </ins> "testing" <ins> "github.com/stretchr/testify/assert" </ins> ) // copied from k8s.io/client-go/transport/round_trippers_test.go

t.Errorf("Invalid content read from cache %q", string(content)) } } <ins> func TestCacheRoundTripperPathPerm(t *testing.T) { assert := assert.New(t) rt := &testRoundTripper{} cacheDir, err := ioutil.TempDir("", "cache-rt") os.RemoveAll(cacheDir) defer os.RemoveAll(cacheDir) if err != nil { t.Fatal(err) } cache := newCacheRoundTripper(cacheDir, rt) // First call, caches the response req := &http.Request{ Method: http.MethodGet, URL:    &url.URL{Host: "localhost"}, } rt.Response = &http.Response{ Header:     http.Header{"ETag": []string{`"123456"`}}, Body:       ioutil.NopCloser(bytes.NewReader([]byte("Content"))), StatusCode: http.StatusOK, } resp, err := cache.RoundTrip(req) if err != nil { t.Fatal(err) } content, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } if string(content) != "Content" { t.Errorf(`Expected Body to be "Content", got %q`, string(content)) } err = filepath.Walk(cacheDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { assert.Equal(os.FileMode(0750), info.Mode().Perm()) } else { assert.Equal(os.FileMode(0660), info.Mode().Perm()) } return nil }) assert.NoError(err) } </ins>