Browse code

Add daemon flag to set no_new_priv as default for unprivileged containers.

Signed-off-by: Daniel Zhang <jmzwcn@gmail.com>

Daniel Zhang authored on 2017/01/09 10:22:05
Showing 9 changed files
... ...
@@ -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",