Selaa lähdekoodia

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

Signed-off-by: Daniel Zhang <jmzwcn@gmail.com>
Daniel Zhang 8 vuotta sitten
vanhempi
commit
d7fda019bb

+ 1 - 0
daemon/config/config_unix.go

@@ -30,6 +30,7 @@ type Config struct {
 	InitPath             string                   `json:"init-path,omitempty"`
 	SeccompProfile       string                   `json:"seccomp-profile,omitempty"`
 	ShmSize              opts.MemBytes            `json:"default-shm-size,omitempty"`
+	NoNewPrivileges      bool                     `json:"no-new-privileges,omitempty"`
 }
 
 // BridgeConfig stores all the bridge driver specific

+ 1 - 1
daemon/container.go

@@ -181,7 +181,7 @@ func (daemon *Daemon) generateHostname(id string, config *containertypes.Config)
 func (daemon *Daemon) setSecurityOptions(container *container.Container, hostConfig *containertypes.HostConfig) error {
 	container.Lock()
 	defer container.Unlock()
-	return parseSecurityOpt(container, hostConfig)
+	return daemon.parseSecurityOpt(container, hostConfig)
 }
 
 func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error {

+ 4 - 0
daemon/daemon_solaris.go

@@ -66,6 +66,10 @@ func (daemon *Daemon) cleanupMountsByID(id string) error {
 	return nil
 }
 
+func (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfig *containertypes.HostConfig) error {
+	return parseSecurityOpt(container, hostConfig)
+}
+
 func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
 	//Since config.SecurityOpt is specifically defined as a "List of string values to
 	//customize labels for MLs systems, such as SELinux"

+ 11 - 0
daemon/daemon_unix.go

@@ -162,6 +162,11 @@ func getBlkioWeightDevices(config containertypes.Resources) ([]specs.WeightDevic
 	return blkioWeightDevices, nil
 }
 
+func (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfig *containertypes.HostConfig) error {
+	container.NoNewPrivileges = daemon.configStore.NoNewPrivileges
+	return parseSecurityOpt(container, hostConfig)
+}
+
 func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
 	var (
 		labelOpts []string
@@ -193,6 +198,12 @@ func parseSecurityOpt(container *container.Container, config *containertypes.Hos
 			container.AppArmorProfile = con[1]
 		case "seccomp":
 			container.SeccompProfile = con[1]
+		case "no-new-privileges":
+			noNewPrivileges, err := strconv.ParseBool(con[1])
+			if err != nil {
+				return fmt.Errorf("invalid --security-opt 2: %q", opt)
+			}
+			container.NoNewPrivileges = noNewPrivileges
 		default:
 			return fmt.Errorf("invalid --security-opt 2: %q", opt)
 		}

+ 29 - 0
daemon/daemon_unix_test.go

@@ -180,6 +180,35 @@ func TestParseSecurityOpt(t *testing.T) {
 	}
 }
 
+func TestParseNNPSecurityOptions(t *testing.T) {
+	daemon := &Daemon{
+		configStore: &config.Config{NoNewPrivileges: true},
+	}
+	container := &container.Container{}
+	config := &containertypes.HostConfig{}
+
+	// test NNP when "daemon:true" and "no-new-privileges=false""
+	config.SecurityOpt = []string{"no-new-privileges=false"}
+
+	if err := daemon.parseSecurityOpt(container, config); err != nil {
+		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
+	}
+	if container.NoNewPrivileges {
+		t.Fatalf("container.NoNewPrivileges should be FALSE: %v", container.NoNewPrivileges)
+	}
+
+	// test NNP when "daemon:false" and "no-new-privileges=true""
+	daemon.configStore.NoNewPrivileges = false
+	config.SecurityOpt = []string{"no-new-privileges=true"}
+
+	if err := daemon.parseSecurityOpt(container, config); err != nil {
+		t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err)
+	}
+	if !container.NoNewPrivileges {
+		t.Fatalf("container.NoNewPrivileges should be TRUE: %v", container.NoNewPrivileges)
+	}
+}
+
 func TestNetworkOptions(t *testing.T) {
 	daemon := &Daemon{}
 	dconfigCorrect := &config.Config{

+ 4 - 0
daemon/daemon_windows.go

@@ -48,6 +48,10 @@ func getBlkioWeightDevices(config *containertypes.HostConfig) ([]blkiodev.Weight
 	return nil, nil
 }
 
+func (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfig *containertypes.HostConfig) error {
+	return parseSecurityOpt(container, hostConfig)
+}
+
 func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
 	return nil
 }

+ 2 - 0
docs/reference/commandline/dockerd.md

@@ -70,6 +70,7 @@ Options:
       --max-concurrent-uploads int            Set the max concurrent uploads for each push (default 5)
       --metrics-addr string                   Set address and port to serve the metrics api (default "")
       --mtu int                               Set the containers network MTU
+      --no-new-privileges                     Disable container processes from gaining new privileges
       --oom-score-adjust int                  Set the oom_score_adj for the daemon (default -500)
   -p, --pidfile string                        Path to use for daemon PID file (default "/var/run/docker.pid")
       --raw-logs                              Full timestamps without ANSI coloring
@@ -1190,6 +1191,7 @@ This is a full example of the allowed configuration options on Linux:
 	"seccomp-profile": "",
 	"insecure-registries": [],
 	"disable-legacy-registry": false,
+	"no-new-privileges": false,
 	"default-runtime": "runc",
 	"oom-score-adjust": -500,
 	"runtimes": {

+ 1 - 1
docs/reference/run.md

@@ -630,7 +630,7 @@ with the same logic -- if the original volume was specified with a name it will
     --security-opt="label=level:LEVEL"   : Set the label level for the container
     --security-opt="label=disable"       : Turn off label confinement for the container
     --security-opt="apparmor=PROFILE"    : Set the apparmor profile to be applied to the container
-    --security-opt="no-new-privileges"   : Disable container processes from gaining new privileges
+    --security-opt="no-new-privileges:true|false"   : Disable/enable container processes from gaining new privileges
     --security-opt="seccomp=unconfined"  : Turn off seccomp confinement for the container
     --security-opt="seccomp=profile.json": White listed syscalls seccomp Json file to be used as a seccomp filter
 

+ 14 - 1
integration-cli/docker_cli_run_unix_test.go

@@ -1140,12 +1140,25 @@ func (s *DockerSuite) TestRunSeccompDefaultProfileNS(c *check.C) {
 	}
 }
 
-// TestRunNoNewPrivSetuid checks that --security-opt=no-new-privileges prevents
+// TestRunNoNewPrivSetuid checks that --security-opt='no-new-privileges=true' prevents
 // effective uid transtions on executing setuid binaries.
 func (s *DockerSuite) TestRunNoNewPrivSetuid(c *check.C) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon)
 	ensureNNPTest(c)
 
+	// test that running a setuid binary results in no effective uid transition
+	icmd.RunCommand(dockerBinary, "run", "--security-opt", "no-new-privileges=true", "--user", "1000",
+		"nnp-test", "/usr/bin/nnp-test").Assert(c, icmd.Expected{
+		Out: "EUID=1000",
+	})
+}
+
+// TestLegacyRunNoNewPrivSetuid checks that --security-opt=no-new-privileges prevents
+// effective uid transtions on executing setuid binaries.
+func (s *DockerSuite) TestLegacyRunNoNewPrivSetuid(c *check.C) {
+	testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon)
+	ensureNNPTest(c)
+
 	// test that running a setuid binary results in no effective uid transition
 	icmd.RunCommand(dockerBinary, "run", "--security-opt", "no-new-privileges", "--user", "1000",
 		"nnp-test", "/usr/bin/nnp-test").Assert(c, icmd.Expected{