Change:
While auditing absolute/relative path behavior in the Kubelet, I came across this: It looks like it just ignores the root directory altogether, and reads relative to the Kubelet's CWD. /cc
could you take a look? Thanks!
Not really, the seccompProfile is the full path passed from CRI:
Hmm, comments is wrong. Will send to PR to fix the comments.
I see. In the next version of the CRI, I think we should make seccomp a message with separate fields for the different options.
do you mean add a filed in podspec instead of annoations?
Isn't it already a field? I meant that instead of a we have something like (pseudocode):
It's a field in CRI already, but still comes from kubernetes pod annotations, not a field of PodSpec.
so are you saying the annotation now has to contain an absolute path on the node? Is there validation for this somewhere? It looks like the code in just trims the prefix, including the after localhost, and then just reads that path, which would be relative because it doesn't have a leading . AFAICT from messing around in the go playground, nothing there adds a leading .

Discussion:
"//conditions:default": [], }), data = [ <del> "fixtures/seccomp/sub/subtest", "fixtures/seccomp/test", </del> ], importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim", library = ":go_default_library",

return nil, fmt.Errorf("unknown seccomp profile option: %s", seccompProfile) } <del> fname := strings.TrimPrefix(seccompProfile, "localhost/") // by pod annotation validation, name is a valid subpath </del> <ins> // get the full path of seccomp profile when prefixed with 'localhost/'. fname := strings.TrimPrefix(seccompProfile, "localhost/") if !filepath.IsAbs(fname) { return nil, fmt.Errorf("seccomp profile path must be absolute, but got relative path %q", fname) } </ins> file, err := ioutil.ReadFile(filepath.FromSlash(fname)) if err != nil { return nil, fmt.Errorf("cannot load seccomp profile %q: %v", fname, err)

import ( "fmt" <ins> "io/ioutil" "os" "path/filepath" </ins> "testing" "github.com/stretchr/testify/assert" <ins> "github.com/stretchr/testify/require" </ins> ) func TestGetSeccompSecurityOpts(t *testing.T) {

} func TestLoadSeccompLocalhostProfiles(t *testing.T) { <ins> tmpdir, err := ioutil.TempDir("", "seccomp-local-profile-test") require.NoError(t, err) defer os.RemoveAll(tmpdir) testProfile := `{"foo": "bar"}` err = ioutil.WriteFile(filepath.Join(tmpdir, "test"), []byte(testProfile), 0644) require.NoError(t, err) </ins> tests := []struct { msg            string seccompProfile string expectedOpts   []string expectErr      bool }{{ <del> msg: "Seccomp localhost/test profile", // We are abusing localhost for loading test seccomp profiles. // The profile should be an absolute path while we are using a relative one. seccompProfile: "localhost/fixtures/seccomp/test", </del> <ins> msg:            "Seccomp localhost/test profile should return correct seccomp profiles", seccompProfile: "localhost/" + filepath.Join(tmpdir, "test"), </ins> expectedOpts:   []string{`seccomp={"foo":"bar"}`}, expectErr:      false, }, { <del> msg:            "Seccomp localhost/sub/subtest profile", seccompProfile: "localhost/fixtures/seccomp/sub/subtest", expectedOpts:   []string{`seccomp={"abc":"def"}`}, expectErr:      false, </del> <ins> msg:            "Non-existent profile should return error", seccompProfile: "localhost/" + filepath.Join(tmpdir, "fixtures/non-existent"), expectedOpts:   nil, expectErr:      true, </ins> }, { <del> msg:            "Seccomp non-existent", seccompProfile: "localhost/fixtures/seccomp/non-existent", </del> <ins> msg:            "Relative profile path should return error", seccompProfile: "localhost/fixtures/test", </ins> expectedOpts:   nil, expectErr:      true, }}

proberesults "k8s.io/kubernetes/pkg/kubelet/prober/results" ) <ins> const ( fakeSeccompProfileRoot = "/fakeSeccompProfileRoot" ) </ins> type fakeHTTP struct { url string err error

runtimeService:      runtimeService, imageService:        imageService, keyring:             keyring, <ins> seccompProfileRoot:  fakeSeccompProfileRoot, </ins> internalLifecycle:   cm.NewFakeInternalContainerLifecycle(), }

if strings.HasPrefix(profile, "localhost/") { name := strings.TrimPrefix(profile, "localhost/") fname := filepath.Join(m.seccompProfileRoot, filepath.FromSlash(name)) <del> return fname </del> <ins> return "localhost/" + fname </ins> } return profile

package kuberuntime import ( <ins> "path/filepath" </ins> "testing" "github.com/stretchr/testify/assert" <ins> "github.com/stretchr/testify/require" </ins> "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimetesting "k8s.io/kubernetes/pkg/kubelet/apis/cri/testing"

assert.Equal(t, test.expectedImageUserValues.username, username, "TestCase[%d]", j) } } <ins> func TestGetSeccompProfileFromAnnotations(t *testing.T) { _, _, m, err := createTestRuntimeManager() require.NoError(t, err) tests := []struct { description     string annotation      map[string]string containerName   string expectedProfile string }{ { description:     "no seccomp should return empty string", expectedProfile: "", }, { description:     "no seccomp with containerName should return exmpty string", containerName:   "container1", expectedProfile: "", }, { description: "pod docker/default seccomp profile should return docker/default", annotation: map[string]string{ v1.SeccompPodAnnotationKey: "docker/default", }, expectedProfile: "docker/default", }, { description: "pod docker/default seccomp profile with containerName should return docker/default", annotation: map[string]string{ v1.SeccompPodAnnotationKey: "docker/default", }, containerName:   "container1", expectedProfile: "docker/default", }, { description: "pod unconfined seccomp profile should return unconfined", annotation: map[string]string{ v1.SeccompPodAnnotationKey: "unconfined", }, expectedProfile: "unconfined", }, { description: "pod unconfined seccomp profile with containerName should return unconfined", annotation: map[string]string{ v1.SeccompPodAnnotationKey: "unconfined", }, containerName:   "container1", expectedProfile: "unconfined", }, { description: "pod localhost seccomp profile should return local profile path", annotation: map[string]string{ v1.SeccompPodAnnotationKey: "localhost/chmod.json", }, expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "chmod.json"), }, { description: "pod localhost seccomp profile with containerName should return local profile path", annotation: map[string]string{ v1.SeccompPodAnnotationKey: "localhost/chmod.json", }, containerName:   "container1", expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "chmod.json"), }, { description: "container localhost seccomp profile with containerName should return local profile path", annotation: map[string]string{ v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json", }, containerName:   "container1", expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "chmod.json"), }, { description: "container localhost seccomp profile should override pod profile", annotation: map[string]string{ v1.SeccompPodAnnotationKey:                            "unconfined", v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json", }, containerName:   "container1", expectedProfile: "localhost/" + filepath.Join(fakeSeccompProfileRoot, "chmod.json"), }, { description: "container localhost seccomp profile with unmatched containerName should return empty string", annotation: map[string]string{ v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json", }, containerName:   "container2", expectedProfile: "", }, } for i, test := range tests { seccompProfile := m.getSeccompProfileFromAnnotations(test.annotation, test.containerName) assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]", i) } } </ins>