-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add CLI flags for Swarm Service seccomp, AppArmor, and no-new-privileges #5698
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5698 +/- ##
==========================================
+ Coverage 59.51% 59.61% +0.09%
==========================================
Files 346 346
Lines 29376 29466 +90
==========================================
+ Hits 17483 17566 +83
- Misses 10923 10928 +5
- Partials 970 972 +2 |
cli/command/service/opts_test.go
Outdated
const testJson = `{ | ||
"json": "you betcha" | ||
} | ||
` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the linter complains on this one;
68.89 cli/command/service/opts_test.go:376:7: ST1003: const testJson should be testJSON (stylecheck)
68.89 const testJson = `{
68.89 ^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this const is only used in the test below, so perhaps consider defining the const inside that test. Give that it's a very small JSON, perhaps put it on one line?
const testJSON = `{"json": "you betcha"}`
Oh; I guess that also means changing the file to be the same , well 😂
c1cec5b
to
b10de33
Compare
What is done:
What needs to be done still:
What I have up now is ready for review, even in its incomplete state, but not ready for merging. |
2ba8aa5
to
7832807
Compare
cli/command/service/opts.go
Outdated
// TODO(dperny): is it safe/secure to unconditionally read a file like | ||
// this? what if the file is REALLY BIG? is that a user problem for | ||
// passing a too-big file, or an us problem for ingesting it | ||
// unquestioningly? | ||
data, err := os.ReadFile(options.seccomp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO it's the user's problem. The flag value is always a file path, except when it isn't; not reading a local file is the astonishing case. It's on them if the CLI process gets OOM-killed because they chose the wrong file.
That being said, interpreting any value aside from the special cases as a file path leaves no room to add new special cases, like additional seccomp modes. Adding a new special case would result in the flag --seccomp=foo
changing semantics from reading a local file to whatever the special case is --- a breaking change.
I suggest that only values containing a path separator character be interpreted as a path to a custom seccomp profile. That cleanly solves several problems at once.
- Loading a custom profile from a file is always explicit and unambiguous. Users wanting to use a profile located in the CWD would have to pass
--seccomp=./my-custom-profile.json
in much the same way that a shell user has to type./my-script.sh
to run an executable from the CWD. - All values not containing a slash or backslash character (aside from
default
andunconfined
) are reserved for future use.
cli/command/service/opts.go
Outdated
// TODO(dperny): return this, or return "unrecognized option" or some such? | ||
return nil, errors.Wrap(err, "unable to read seccomp custom profile file") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return this. The CLI understands the option and what the user requested. We should tell the user why reading the file failed so they can take corrective action: fix the typo in the file name, sort out file permissions, etc.
a5b78ec
to
b66fa05
Compare
Adds support for some security options to the docker service create and docker service update commands. These include: * --no-new-privileges, which does what it says on the tin * --seccomp, which sets seccomp options * --apparmor, which sets apparmor options Does not include compose file changes. Signed-off-by: Drew Erny <[email protected]>
b66fa05
to
dc75d4b
Compare
// why make an invalid json file when we have one lying right there? | ||
flags.Set("seccomp", "testdata/service-context-write-raw.golden") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should re-use a golden file from another test here. We can rather create a malformed json file inside a temp directory.
fileName := filepath.Join(t.TempDir(), "malformed-json")
err := os.WriteFile(fileName, []byte("not json"), 0o644)
assert.NilError(t, err)
flags.Set("seccomp", fileName)
// check if the file exists locally | ||
if _, err := os.Stat(arg); errors.Is(err, os.ErrNotExist) { | ||
return nil, errors.Errorf("unknown seccomp mode %q.", arg) | ||
} | ||
return nil, errors.Errorf( | ||
"unknown seccomp mode %q. (did you mean custom a seccomp profile \"./%s\"?)", | ||
arg, arg, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why it is necessary to check if the file exists if we expect to error anyway.
I would just remove the if statement and return
"unknown seccomp mode %q. a custom seccomp profile must be specified as a relative or absolute file path "./profile.json" or "/path/profile.json"."
} | ||
data, err := os.ReadFile(options.seccomp) | ||
if err != nil { | ||
// TODO(dperny): return this, or return "unrecognized option" or some such? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forgot to remove?
oldwd, err := os.Getwd() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if err := os.Chdir("testdata"); err != nil { | ||
t.Fatal(err) | ||
} | ||
t.Cleanup(func() { | ||
if err := os.Chdir(oldwd); err != nil { | ||
panic(err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like these kind of tests that could impact all other tests from running properly. I would remove the chdir
and just pass it filename. I suggested above to not check if the file exists since it's a bit superfluous. That way you won't need to chdir
.
assert.Check(t, is.DeepEqual(privileges, &swarm.Privileges{ | ||
Seccomp: &swarm.SeccompOpts{ | ||
Mode: swarm.SeccompModeCustom, | ||
Profile: []byte(testJSON), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can immediately use the json like so
Profile: []byte(testJSON), | |
Profile: []byte(`{ | |
"json": "you betcha", | |
}`), |
const testJSON = `{ | ||
"json": "you betcha" | ||
} | ||
` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can just remove this const.
- What I did
Add 3 flags to the
docker service create
anddocker service update
CLI commands to support the security options in moby/moby#46386.--apparmor
allows setting AppArmor todefault
ordisabled
.--no-new-privileges
does what it says on the tin--seccomp
allows eitherdefault
,unconfined
, or a file name of a JSON file with a custom seccomp profile.- How I did it
Added CLI flags in the standard way. Mostly boilerplate.
- How to verify it
Added tests for the flags.
- Description for the changelog