Signed-off-by: Daniel Zhang <jmzwcn@gmail.com>
Daniel Zhang authored on 2017/01/09 10:22:05... | ... |
@@ -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", |