Sfoglia il codice sorgente

daemon: add a flag to override the default seccomp profile

Signed-off-by: Antonio Murdaca <runcom@redhat.com>
Antonio Murdaca 8 anni fa
parent
commit
b237189e6c

+ 12 - 2
api/server/router/system/system_routes.go

@@ -42,10 +42,20 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
 	if versions.LessThan(httputils.VersionFromContext(ctx), "1.25") {
 		// TODO: handle this conversion in engine-api
 		type oldInfo struct {
-			*types.Info
+			*types.InfoBase
 			ExecutionDriver string
+			SecurityOptions []string
 		}
-		return httputils.WriteJSON(w, http.StatusOK, &oldInfo{Info: info, ExecutionDriver: "<not supported>"})
+		old := &oldInfo{
+			InfoBase:        info.InfoBase,
+			ExecutionDriver: "<not supported>",
+		}
+		for _, s := range info.SecurityOptions {
+			if s.Key == "Name" {
+				old.SecurityOptions = append(old.SecurityOptions, s.Value)
+			}
+		}
+		return httputils.WriteJSON(w, http.StatusOK, old)
 	}
 	return httputils.WriteJSON(w, http.StatusOK, info)
 }

+ 14 - 3
api/types/types.go

@@ -142,9 +142,9 @@ type Version struct {
 	BuildTime     string `json:",omitempty"`
 }
 
-// Info contains response of Remote API:
+// InfoBase contains the base response of Remote API:
 // GET "/info"
-type Info struct {
+type InfoBase struct {
 	ID                 string
 	Containers         int
 	ContainersRunning  int
@@ -191,7 +191,6 @@ type Info struct {
 	ServerVersion      string
 	ClusterStore       string
 	ClusterAdvertise   string
-	SecurityOptions    []string
 	Runtimes           map[string]Runtime
 	DefaultRuntime     string
 	Swarm              swarm.Info
@@ -202,6 +201,18 @@ type Info struct {
 	Isolation          container.Isolation
 }
 
+// SecurityOpt holds key/value pair about a security option
+type SecurityOpt struct {
+	Key, Value string
+}
+
+// Info contains response of Remote API:
+// GET "/info"
+type Info struct {
+	*InfoBase
+	SecurityOptions []SecurityOpt
+}
+
 // PluginsInfo is a temp struct holding Plugins name
 // registered with docker daemon. It is used by Info struct
 type PluginsInfo struct {

+ 14 - 3
cli/command/system/info.go

@@ -140,9 +140,20 @@ func prettyPrintInfo(dockerCli *command.DockerCli, info types.Info) error {
 	}
 
 	if info.OSType == "linux" {
-		fmt.Fprintf(dockerCli.Out(), "Security Options:")
-		ioutils.FprintfIfNotEmpty(dockerCli.Out(), " %s", strings.Join(info.SecurityOptions, " "))
-		fmt.Fprintf(dockerCli.Out(), "\n")
+		if len(info.SecurityOptions) != 0 {
+			fmt.Fprintf(dockerCli.Out(), "Security Options:\n")
+			for _, o := range info.SecurityOptions {
+				switch o.Key {
+				case "Name":
+					fmt.Fprintf(dockerCli.Out(), " %s\n", o.Value)
+				case "Profile":
+					if o.Key != "default" {
+						fmt.Fprintf(dockerCli.Err(), "  WARNING: You're not using the Docker's default seccomp profile\n")
+					}
+					fmt.Fprintf(dockerCli.Out(), "  %s: %s\n", o.Key, o.Value)
+				}
+			}
+		}
 	}
 
 	// Isolation only has meaning on a Windows daemon.

+ 4 - 2
client/info_test.go

@@ -46,8 +46,10 @@ func TestInfo(t *testing.T) {
 				return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
 			}
 			info := &types.Info{
-				ID:         "daemonID",
-				Containers: 3,
+				InfoBase: &types.InfoBase{
+					ID:         "daemonID",
+					Containers: 3,
+				},
 			}
 			b, err := json.Marshal(info)
 			if err != nil {

+ 2 - 0
daemon/config_unix.go

@@ -39,6 +39,7 @@ type Config struct {
 	OOMScoreAdjust       int                      `json:"oom-score-adjust,omitempty"`
 	Init                 bool                     `json:"init,omitempty"`
 	InitPath             string                   `json:"init-path,omitempty"`
+	SeccompProfile       string                   `json:"seccomp-profile,omitempty"`
 }
 
 // bridgeConfig stores all the bridge driver specific
@@ -101,6 +102,7 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) {
 	flags.StringVar(&config.InitPath, "init-path", "", "Path to the docker-init binary")
 	flags.Int64Var(&config.CPURealtimePeriod, "cpu-rt-period", 0, "Limit the CPU real-time period in microseconds")
 	flags.Int64Var(&config.CPURealtimeRuntime, "cpu-rt-runtime", 0, "Limit the CPU real-time runtime in microseconds")
+	flags.StringVar(&config.SeccompProfile, "seccomp-profile", "", "Path to seccomp profile")
 
 	config.attachExperimentalFlags(flags)
 }

+ 7 - 0
daemon/daemon.go

@@ -104,6 +104,9 @@ type Daemon struct {
 	defaultIsolation          containertypes.Isolation // Default isolation mode on Windows
 	clusterProvider           cluster.Provider
 	cluster                   Cluster
+
+	seccompProfile     []byte
+	seccompProfilePath string
 }
 
 // HasExperimental returns whether the experimental features of the daemon are enabled or not
@@ -530,6 +533,10 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
 		}
 	}()
 
+	if err := d.setupSeccompProfile(); err != nil {
+		return nil, err
+	}
+
 	// Set the default isolation mode (only applicable on Windows)
 	if err := d.setDefaultIsolation(); err != nil {
 		return nil, fmt.Errorf("error setting default isolation mode: %v", err)

+ 4 - 0
daemon/daemon_solaris.go

@@ -177,3 +177,7 @@ func setupDaemonProcess(config *Config) error {
 func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
 	return nil
 }
+
+func (daemon *Daemon) setupSeccompProfile() error {
+	return nil
+}

+ 18 - 0
daemon/daemon_unix.go

@@ -4,6 +4,7 @@ package daemon
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"net"
@@ -1242,6 +1243,23 @@ func (daemon *Daemon) initCgroupsPath(path string) error {
 			return err
 		}
 	}
+	return nil
+}
 
+func (daemon *Daemon) setupSeccompProfile() error {
+	if daemon.configStore.SeccompProfile != "" {
+		daemon.seccompProfilePath = daemon.configStore.SeccompProfile
+		b, err := ioutil.ReadFile(daemon.configStore.SeccompProfile)
+		if err != nil {
+			return fmt.Errorf("opening seccomp profile (%s) failed: %v", daemon.configStore.SeccompProfile, err)
+		}
+		daemon.seccompProfile = b
+		p := struct {
+			DefaultAction string `json:"defaultAction"`
+		}{}
+		if err := json.Unmarshal(daemon.seccompProfile, &p); err != nil {
+			return err
+		}
+	}
 	return nil
 }

+ 4 - 0
daemon/daemon_windows.go

@@ -532,3 +532,7 @@ func setupDaemonProcess(config *Config) error {
 func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
 	return nil
 }
+
+func (daemon *Daemon) setupSeccompProfile() error {
+	return nil
+}

+ 19 - 8
daemon/info.go

@@ -68,22 +68,29 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
 		}
 	})
 
-	var securityOptions []string
+	securityOptions := []types.SecurityOpt{}
 	if sysInfo.AppArmor {
-		securityOptions = append(securityOptions, "apparmor")
+		securityOptions = append(securityOptions, types.SecurityOpt{Key: "Name", Value: "apparmor"})
 	}
 	if sysInfo.Seccomp && supportsSeccomp {
-		securityOptions = append(securityOptions, "seccomp")
+		profile := daemon.seccompProfilePath
+		if profile == "" {
+			profile = "default"
+		}
+		securityOptions = append(securityOptions,
+			types.SecurityOpt{Key: "Name", Value: "seccomp"},
+			types.SecurityOpt{Key: "Profile", Value: profile},
+		)
 	}
 	if selinuxEnabled() {
-		securityOptions = append(securityOptions, "selinux")
+		securityOptions = append(securityOptions, types.SecurityOpt{Key: "Name", Value: "selinux"})
 	}
 	uid, gid := daemon.GetRemappedUIDGID()
 	if uid != 0 || gid != 0 {
-		securityOptions = append(securityOptions, "userns")
+		securityOptions = append(securityOptions, types.SecurityOpt{Key: "Name", Value: "userns"})
 	}
 
-	v := &types.Info{
+	v := &types.InfoBase{
 		ID:                 daemon.ID,
 		Containers:         int(cRunning + cPaused + cStopped),
 		ContainersRunning:  int(cRunning),
@@ -120,7 +127,6 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
 		HTTPProxy:          sockets.GetProxyEnv("http_proxy"),
 		HTTPSProxy:         sockets.GetProxyEnv("https_proxy"),
 		NoProxy:            sockets.GetProxyEnv("no_proxy"),
-		SecurityOptions:    securityOptions,
 		LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled,
 		Isolation:          daemon.defaultIsolation,
 	}
@@ -150,7 +156,12 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
 	}
 	v.Name = hostname
 
-	return v, nil
+	i := &types.Info{
+		InfoBase:        v,
+		SecurityOptions: securityOptions,
+	}
+
+	return i, nil
 }
 
 // SystemVersion returns version information about the daemon.

+ 10 - 3
daemon/seccomp_linux.go

@@ -37,9 +37,16 @@ func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error {
 			return err
 		}
 	} else {
-		profile, err = seccomp.GetDefaultProfile(rs)
-		if err != nil {
-			return err
+		if daemon.seccompProfile != nil {
+			profile, err = seccomp.LoadProfile(string(daemon.seccompProfile), rs)
+			if err != nil {
+				return err
+			}
+		} else {
+			profile, err = seccomp.GetDefaultProfile(rs)
+			if err != nil {
+				return err
+			}
 		}
 	}
 

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

@@ -74,6 +74,7 @@ Options:
   -p, --pidfile string                        Path to use for daemon PID file (default "/var/run/docker.pid")
       --raw-logs                              Full timestamps without ANSI coloring
       --registry-mirror value                 Preferred Docker registry mirror (default [])
+      --seccomp-profile value                 Path to seccomp profile
       --selinux-enabled                       Enable selinux support
       --shutdown-timeout=15                   Set the shutdown timeout value in seconds
   -s, --storage-driver string                 Storage driver to use
@@ -1195,6 +1196,7 @@ This is a full example of the allowed configuration options on Linux:
 	"icc": false,
 	"raw-logs": false,
 	"registry-mirrors": [],
+	"seccomp-profile": "",
 	"insecure-registries": [],
 	"disable-legacy-registry": false,
 	"default-runtime": "runc",

+ 1 - 1
integration-cli/docker_cli_info_unix_test.go

@@ -11,5 +11,5 @@ func (s *DockerSuite) TestInfoSecurityOptions(c *check.C) {
 	testRequires(c, SameHostDaemon, seccompEnabled, Apparmor, DaemonIsLinux)
 
 	out, _ := dockerCmd(c, "info")
-	c.Assert(out, checker.Contains, "Security Options: apparmor seccomp")
+	c.Assert(out, checker.Contains, "Security Options:\n apparmor\n seccomp\n  Profile: default\n")
 }

+ 34 - 0
integration-cli/docker_cli_run_unix_test.go

@@ -1375,3 +1375,37 @@ func (s *DockerDaemonSuite) TestRunSeccompJSONNoArchAndArchMap(c *check.C) {
 	c.Assert(err, check.NotNil)
 	c.Assert(out, checker.Contains, "'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
 }
+
+func (s *DockerDaemonSuite) TestRunWithDaemonDefaultSeccompProfile(c *check.C) {
+	testRequires(c, SameHostDaemon, seccompEnabled)
+
+	err := s.d.StartWithBusybox()
+	c.Assert(err, check.IsNil)
+
+	// 1) verify I can run containers with the Docker default shipped profile which allows chmod
+	_, err = s.d.Cmd("run", "busybox", "chmod", "777", ".")
+	c.Assert(err, check.IsNil)
+
+	jsonData := `{
+	"defaultAction": "SCMP_ACT_ALLOW",
+	"syscalls": [
+		{
+			"name": "chmod",
+			"action": "SCMP_ACT_ERRNO"
+		}
+	]
+}`
+	tmpFile, err := ioutil.TempFile("", "profile.json")
+	c.Assert(err, check.IsNil)
+	defer tmpFile.Close()
+	_, err = tmpFile.Write([]byte(jsonData))
+	c.Assert(err, check.IsNil)
+
+	// 2) restart the daemon and add a custom seccomp profile in which we deny chmod
+	err = s.d.Restart("--seccomp-profile=" + tmpFile.Name())
+	c.Assert(err, check.IsNil)
+
+	out, err := s.d.Cmd("run", "busybox", "chmod", "777", ".")
+	c.Assert(err, check.NotNil)
+	c.Assert(out, checker.Contains, "Operation not permitted")
+}

+ 4 - 0
man/dockerd.8.md

@@ -56,6 +56,7 @@ dockerd - Enable daemon mode
 [**--raw-logs**]
 [**--registry-mirror**[=*[]*]]
 [**-s**|**--storage-driver**[=*STORAGE-DRIVER*]]
+[**--seccomp-profile**[=*SECCOMP-PROFILE-PATH*]]
 [**--selinux-enabled**]
 [**--shutdown-timeout**[=*15*]]
 [**--storage-opt**[=*[]*]]
@@ -248,6 +249,9 @@ output otherwise.
 **-s**, **--storage-driver**=""
   Force the Docker runtime to use a specific storage driver.
 
+**--seccomp-profile**=""
+  Path to seccomp profile.
+
 **--selinux-enabled**=*true*|*false*
   Enable selinux support. Default is false.