Signed-off-by: Daniel Zhang <jmzwcn@gmail.com>
| ... | ... |
@@ -30,6 +30,7 @@ type Config struct {
|
| 30 | 30 |
InitPath string `json:"init-path,omitempty"` |
| 31 | 31 |
SeccompProfile string `json:"seccomp-profile,omitempty"` |
| 32 | 32 |
ShmSize opts.MemBytes `json:"default-shm-size,omitempty"` |
| 33 |
+ NoNewPrivileges bool `json:"no-new-privileges,omitempty"` |
|
| 33 | 34 |
} |
| 34 | 35 |
|
| 35 | 36 |
// BridgeConfig stores all the bridge driver specific |
| ... | ... |
@@ -181,7 +181,7 @@ func (daemon *Daemon) generateHostname(id string, config *containertypes.Config) |
| 181 | 181 |
func (daemon *Daemon) setSecurityOptions(container *container.Container, hostConfig *containertypes.HostConfig) error {
|
| 182 | 182 |
container.Lock() |
| 183 | 183 |
defer container.Unlock() |
| 184 |
- return parseSecurityOpt(container, hostConfig) |
|
| 184 |
+ return daemon.parseSecurityOpt(container, hostConfig) |
|
| 185 | 185 |
} |
| 186 | 186 |
|
| 187 | 187 |
func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error {
|
| ... | ... |
@@ -66,6 +66,10 @@ func (daemon *Daemon) cleanupMountsByID(id string) error {
|
| 66 | 66 |
return nil |
| 67 | 67 |
} |
| 68 | 68 |
|
| 69 |
+func (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfig *containertypes.HostConfig) error {
|
|
| 70 |
+ return parseSecurityOpt(container, hostConfig) |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 69 | 73 |
func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
|
| 70 | 74 |
//Since config.SecurityOpt is specifically defined as a "List of string values to |
| 71 | 75 |
//customize labels for MLs systems, such as SELinux" |
| ... | ... |
@@ -162,6 +162,11 @@ func getBlkioWeightDevices(config containertypes.Resources) ([]specs.WeightDevic |
| 162 | 162 |
return blkioWeightDevices, nil |
| 163 | 163 |
} |
| 164 | 164 |
|
| 165 |
+func (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfig *containertypes.HostConfig) error {
|
|
| 166 |
+ container.NoNewPrivileges = daemon.configStore.NoNewPrivileges |
|
| 167 |
+ return parseSecurityOpt(container, hostConfig) |
|
| 168 |
+} |
|
| 169 |
+ |
|
| 165 | 170 |
func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
|
| 166 | 171 |
var ( |
| 167 | 172 |
labelOpts []string |
| ... | ... |
@@ -193,6 +198,12 @@ func parseSecurityOpt(container *container.Container, config *containertypes.Hos |
| 193 | 193 |
container.AppArmorProfile = con[1] |
| 194 | 194 |
case "seccomp": |
| 195 | 195 |
container.SeccompProfile = con[1] |
| 196 |
+ case "no-new-privileges": |
|
| 197 |
+ noNewPrivileges, err := strconv.ParseBool(con[1]) |
|
| 198 |
+ if err != nil {
|
|
| 199 |
+ return fmt.Errorf("invalid --security-opt 2: %q", opt)
|
|
| 200 |
+ } |
|
| 201 |
+ container.NoNewPrivileges = noNewPrivileges |
|
| 196 | 202 |
default: |
| 197 | 203 |
return fmt.Errorf("invalid --security-opt 2: %q", opt)
|
| 198 | 204 |
} |
| ... | ... |
@@ -180,6 +180,35 @@ func TestParseSecurityOpt(t *testing.T) {
|
| 180 | 180 |
} |
| 181 | 181 |
} |
| 182 | 182 |
|
| 183 |
+func TestParseNNPSecurityOptions(t *testing.T) {
|
|
| 184 |
+ daemon := &Daemon{
|
|
| 185 |
+ configStore: &config.Config{NoNewPrivileges: true},
|
|
| 186 |
+ } |
|
| 187 |
+ container := &container.Container{}
|
|
| 188 |
+ config := &containertypes.HostConfig{}
|
|
| 189 |
+ |
|
| 190 |
+ // test NNP when "daemon:true" and "no-new-privileges=false"" |
|
| 191 |
+ config.SecurityOpt = []string{"no-new-privileges=false"}
|
|
| 192 |
+ |
|
| 193 |
+ if err := daemon.parseSecurityOpt(container, config); err != nil {
|
|
| 194 |
+ t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
|
|
| 195 |
+ } |
|
| 196 |
+ if container.NoNewPrivileges {
|
|
| 197 |
+ t.Fatalf("container.NoNewPrivileges should be FALSE: %v", container.NoNewPrivileges)
|
|
| 198 |
+ } |
|
| 199 |
+ |
|
| 200 |
+ // test NNP when "daemon:false" and "no-new-privileges=true"" |
|
| 201 |
+ daemon.configStore.NoNewPrivileges = false |
|
| 202 |
+ config.SecurityOpt = []string{"no-new-privileges=true"}
|
|
| 203 |
+ |
|
| 204 |
+ if err := daemon.parseSecurityOpt(container, config); err != nil {
|
|
| 205 |
+ t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
|
|
| 206 |
+ } |
|
| 207 |
+ if !container.NoNewPrivileges {
|
|
| 208 |
+ t.Fatalf("container.NoNewPrivileges should be TRUE: %v", container.NoNewPrivileges)
|
|
| 209 |
+ } |
|
| 210 |
+} |
|
| 211 |
+ |
|
| 183 | 212 |
func TestNetworkOptions(t *testing.T) {
|
| 184 | 213 |
daemon := &Daemon{}
|
| 185 | 214 |
dconfigCorrect := &config.Config{
|
| ... | ... |
@@ -48,6 +48,10 @@ func getBlkioWeightDevices(config *containertypes.HostConfig) ([]blkiodev.Weight |
| 48 | 48 |
return nil, nil |
| 49 | 49 |
} |
| 50 | 50 |
|
| 51 |
+func (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfig *containertypes.HostConfig) error {
|
|
| 52 |
+ return parseSecurityOpt(container, hostConfig) |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 51 | 55 |
func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
|
| 52 | 56 |
return nil |
| 53 | 57 |
} |
| ... | ... |
@@ -70,6 +70,7 @@ Options: |
| 70 | 70 |
--max-concurrent-uploads int Set the max concurrent uploads for each push (default 5) |
| 71 | 71 |
--metrics-addr string Set address and port to serve the metrics api (default "") |
| 72 | 72 |
--mtu int Set the containers network MTU |
| 73 |
+ --no-new-privileges Disable container processes from gaining new privileges |
|
| 73 | 74 |
--oom-score-adjust int Set the oom_score_adj for the daemon (default -500) |
| 74 | 75 |
-p, --pidfile string Path to use for daemon PID file (default "/var/run/docker.pid") |
| 75 | 76 |
--raw-logs Full timestamps without ANSI coloring |
| ... | ... |
@@ -1190,6 +1191,7 @@ This is a full example of the allowed configuration options on Linux: |
| 1190 | 1190 |
"seccomp-profile": "", |
| 1191 | 1191 |
"insecure-registries": [], |
| 1192 | 1192 |
"disable-legacy-registry": false, |
| 1193 |
+ "no-new-privileges": false, |
|
| 1193 | 1194 |
"default-runtime": "runc", |
| 1194 | 1195 |
"oom-score-adjust": -500, |
| 1195 | 1196 |
"runtimes": {
|
| ... | ... |
@@ -630,7 +630,7 @@ with the same logic -- if the original volume was specified with a name it will |
| 630 | 630 |
--security-opt="label=level:LEVEL" : Set the label level for the container |
| 631 | 631 |
--security-opt="label=disable" : Turn off label confinement for the container |
| 632 | 632 |
--security-opt="apparmor=PROFILE" : Set the apparmor profile to be applied to the container |
| 633 |
- --security-opt="no-new-privileges" : Disable container processes from gaining new privileges |
|
| 633 |
+ --security-opt="no-new-privileges:true|false" : Disable/enable container processes from gaining new privileges |
|
| 634 | 634 |
--security-opt="seccomp=unconfined" : Turn off seccomp confinement for the container |
| 635 | 635 |
--security-opt="seccomp=profile.json": White listed syscalls seccomp Json file to be used as a seccomp filter |
| 636 | 636 |
|
| ... | ... |
@@ -1140,13 +1140,26 @@ func (s *DockerSuite) TestRunSeccompDefaultProfileNS(c *check.C) {
|
| 1140 | 1140 |
} |
| 1141 | 1141 |
} |
| 1142 | 1142 |
|
| 1143 |
-// TestRunNoNewPrivSetuid checks that --security-opt=no-new-privileges prevents |
|
| 1143 |
+// TestRunNoNewPrivSetuid checks that --security-opt='no-new-privileges=true' prevents |
|
| 1144 | 1144 |
// effective uid transtions on executing setuid binaries. |
| 1145 | 1145 |
func (s *DockerSuite) TestRunNoNewPrivSetuid(c *check.C) {
|
| 1146 | 1146 |
testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon) |
| 1147 | 1147 |
ensureNNPTest(c) |
| 1148 | 1148 |
|
| 1149 | 1149 |
// test that running a setuid binary results in no effective uid transition |
| 1150 |
+ icmd.RunCommand(dockerBinary, "run", "--security-opt", "no-new-privileges=true", "--user", "1000", |
|
| 1151 |
+ "nnp-test", "/usr/bin/nnp-test").Assert(c, icmd.Expected{
|
|
| 1152 |
+ Out: "EUID=1000", |
|
| 1153 |
+ }) |
|
| 1154 |
+} |
|
| 1155 |
+ |
|
| 1156 |
+// TestLegacyRunNoNewPrivSetuid checks that --security-opt=no-new-privileges prevents |
|
| 1157 |
+// effective uid transtions on executing setuid binaries. |
|
| 1158 |
+func (s *DockerSuite) TestLegacyRunNoNewPrivSetuid(c *check.C) {
|
|
| 1159 |
+ testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon) |
|
| 1160 |
+ ensureNNPTest(c) |
|
| 1161 |
+ |
|
| 1162 |
+ // test that running a setuid binary results in no effective uid transition |
|
| 1150 | 1163 |
icmd.RunCommand(dockerBinary, "run", "--security-opt", "no-new-privileges", "--user", "1000", |
| 1151 | 1164 |
"nnp-test", "/usr/bin/nnp-test").Assert(c, icmd.Expected{
|
| 1152 | 1165 |
Out: "EUID=1000", |