mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
daemon: add a flag to override the default seccomp profile
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
parent
ecd806cdf1
commit
b237189e6c
15 changed files with 150 additions and 23 deletions
|
@ -42,10 +42,20 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
|
||||||
if versions.LessThan(httputils.VersionFromContext(ctx), "1.25") {
|
if versions.LessThan(httputils.VersionFromContext(ctx), "1.25") {
|
||||||
// TODO: handle this conversion in engine-api
|
// TODO: handle this conversion in engine-api
|
||||||
type oldInfo struct {
|
type oldInfo struct {
|
||||||
*types.Info
|
*types.InfoBase
|
||||||
ExecutionDriver string
|
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)
|
return httputils.WriteJSON(w, http.StatusOK, info)
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,9 +142,9 @@ type Version struct {
|
||||||
BuildTime string `json:",omitempty"`
|
BuildTime string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info contains response of Remote API:
|
// InfoBase contains the base response of Remote API:
|
||||||
// GET "/info"
|
// GET "/info"
|
||||||
type Info struct {
|
type InfoBase struct {
|
||||||
ID string
|
ID string
|
||||||
Containers int
|
Containers int
|
||||||
ContainersRunning int
|
ContainersRunning int
|
||||||
|
@ -191,7 +191,6 @@ type Info struct {
|
||||||
ServerVersion string
|
ServerVersion string
|
||||||
ClusterStore string
|
ClusterStore string
|
||||||
ClusterAdvertise string
|
ClusterAdvertise string
|
||||||
SecurityOptions []string
|
|
||||||
Runtimes map[string]Runtime
|
Runtimes map[string]Runtime
|
||||||
DefaultRuntime string
|
DefaultRuntime string
|
||||||
Swarm swarm.Info
|
Swarm swarm.Info
|
||||||
|
@ -202,6 +201,18 @@ type Info struct {
|
||||||
Isolation container.Isolation
|
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
|
// PluginsInfo is a temp struct holding Plugins name
|
||||||
// registered with docker daemon. It is used by Info struct
|
// registered with docker daemon. It is used by Info struct
|
||||||
type PluginsInfo struct {
|
type PluginsInfo struct {
|
||||||
|
|
|
@ -140,9 +140,20 @@ func prettyPrintInfo(dockerCli *command.DockerCli, info types.Info) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.OSType == "linux" {
|
if info.OSType == "linux" {
|
||||||
fmt.Fprintf(dockerCli.Out(), "Security Options:")
|
if len(info.SecurityOptions) != 0 {
|
||||||
ioutils.FprintfIfNotEmpty(dockerCli.Out(), " %s", strings.Join(info.SecurityOptions, " "))
|
fmt.Fprintf(dockerCli.Out(), "Security Options:\n")
|
||||||
fmt.Fprintf(dockerCli.Out(), "\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.
|
// Isolation only has meaning on a Windows daemon.
|
||||||
|
|
|
@ -46,8 +46,10 @@ func TestInfo(t *testing.T) {
|
||||||
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
||||||
}
|
}
|
||||||
info := &types.Info{
|
info := &types.Info{
|
||||||
|
InfoBase: &types.InfoBase{
|
||||||
ID: "daemonID",
|
ID: "daemonID",
|
||||||
Containers: 3,
|
Containers: 3,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(info)
|
b, err := json.Marshal(info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -39,6 +39,7 @@ type Config struct {
|
||||||
OOMScoreAdjust int `json:"oom-score-adjust,omitempty"`
|
OOMScoreAdjust int `json:"oom-score-adjust,omitempty"`
|
||||||
Init bool `json:"init,omitempty"`
|
Init bool `json:"init,omitempty"`
|
||||||
InitPath string `json:"init-path,omitempty"`
|
InitPath string `json:"init-path,omitempty"`
|
||||||
|
SeccompProfile string `json:"seccomp-profile,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// bridgeConfig stores all the bridge driver specific
|
// 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.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.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.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)
|
config.attachExperimentalFlags(flags)
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,9 @@ type Daemon struct {
|
||||||
defaultIsolation containertypes.Isolation // Default isolation mode on Windows
|
defaultIsolation containertypes.Isolation // Default isolation mode on Windows
|
||||||
clusterProvider cluster.Provider
|
clusterProvider cluster.Provider
|
||||||
cluster Cluster
|
cluster Cluster
|
||||||
|
|
||||||
|
seccompProfile []byte
|
||||||
|
seccompProfilePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasExperimental returns whether the experimental features of the daemon are enabled or not
|
// 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)
|
// Set the default isolation mode (only applicable on Windows)
|
||||||
if err := d.setDefaultIsolation(); err != nil {
|
if err := d.setDefaultIsolation(); err != nil {
|
||||||
return nil, fmt.Errorf("error setting default isolation mode: %v", err)
|
return nil, fmt.Errorf("error setting default isolation mode: %v", err)
|
||||||
|
|
|
@ -177,3 +177,7 @@ func setupDaemonProcess(config *Config) error {
|
||||||
func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
|
func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (daemon *Daemon) setupSeccompProfile() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
@ -1242,6 +1243,23 @@ func (daemon *Daemon) initCgroupsPath(path string) error {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -532,3 +532,7 @@ func setupDaemonProcess(config *Config) error {
|
||||||
func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
|
func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (daemon *Daemon) setupSeccompProfile() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -68,22 +68,29 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
var securityOptions []string
|
securityOptions := []types.SecurityOpt{}
|
||||||
if sysInfo.AppArmor {
|
if sysInfo.AppArmor {
|
||||||
securityOptions = append(securityOptions, "apparmor")
|
securityOptions = append(securityOptions, types.SecurityOpt{Key: "Name", Value: "apparmor"})
|
||||||
}
|
}
|
||||||
if sysInfo.Seccomp && supportsSeccomp {
|
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() {
|
if selinuxEnabled() {
|
||||||
securityOptions = append(securityOptions, "selinux")
|
securityOptions = append(securityOptions, types.SecurityOpt{Key: "Name", Value: "selinux"})
|
||||||
}
|
}
|
||||||
uid, gid := daemon.GetRemappedUIDGID()
|
uid, gid := daemon.GetRemappedUIDGID()
|
||||||
if uid != 0 || gid != 0 {
|
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,
|
ID: daemon.ID,
|
||||||
Containers: int(cRunning + cPaused + cStopped),
|
Containers: int(cRunning + cPaused + cStopped),
|
||||||
ContainersRunning: int(cRunning),
|
ContainersRunning: int(cRunning),
|
||||||
|
@ -120,7 +127,6 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
|
||||||
HTTPProxy: sockets.GetProxyEnv("http_proxy"),
|
HTTPProxy: sockets.GetProxyEnv("http_proxy"),
|
||||||
HTTPSProxy: sockets.GetProxyEnv("https_proxy"),
|
HTTPSProxy: sockets.GetProxyEnv("https_proxy"),
|
||||||
NoProxy: sockets.GetProxyEnv("no_proxy"),
|
NoProxy: sockets.GetProxyEnv("no_proxy"),
|
||||||
SecurityOptions: securityOptions,
|
|
||||||
LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled,
|
LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled,
|
||||||
Isolation: daemon.defaultIsolation,
|
Isolation: daemon.defaultIsolation,
|
||||||
}
|
}
|
||||||
|
@ -150,7 +156,12 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
|
||||||
}
|
}
|
||||||
v.Name = hostname
|
v.Name = hostname
|
||||||
|
|
||||||
return v, nil
|
i := &types.Info{
|
||||||
|
InfoBase: v,
|
||||||
|
SecurityOptions: securityOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SystemVersion returns version information about the daemon.
|
// SystemVersion returns version information about the daemon.
|
||||||
|
|
|
@ -36,12 +36,19 @@ func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if daemon.seccompProfile != nil {
|
||||||
|
profile, err = seccomp.LoadProfile(string(daemon.seccompProfile), rs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
profile, err = seccomp.GetDefaultProfile(rs)
|
profile, err = seccomp.GetDefaultProfile(rs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rs.Linux.Seccomp = profile
|
rs.Linux.Seccomp = profile
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -74,6 +74,7 @@ Options:
|
||||||
-p, --pidfile string Path to use for daemon PID file (default "/var/run/docker.pid")
|
-p, --pidfile string Path to use for daemon PID file (default "/var/run/docker.pid")
|
||||||
--raw-logs Full timestamps without ANSI coloring
|
--raw-logs Full timestamps without ANSI coloring
|
||||||
--registry-mirror value Preferred Docker registry mirror (default [])
|
--registry-mirror value Preferred Docker registry mirror (default [])
|
||||||
|
--seccomp-profile value Path to seccomp profile
|
||||||
--selinux-enabled Enable selinux support
|
--selinux-enabled Enable selinux support
|
||||||
--shutdown-timeout=15 Set the shutdown timeout value in seconds
|
--shutdown-timeout=15 Set the shutdown timeout value in seconds
|
||||||
-s, --storage-driver string Storage driver to use
|
-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,
|
"icc": false,
|
||||||
"raw-logs": false,
|
"raw-logs": false,
|
||||||
"registry-mirrors": [],
|
"registry-mirrors": [],
|
||||||
|
"seccomp-profile": "",
|
||||||
"insecure-registries": [],
|
"insecure-registries": [],
|
||||||
"disable-legacy-registry": false,
|
"disable-legacy-registry": false,
|
||||||
"default-runtime": "runc",
|
"default-runtime": "runc",
|
||||||
|
|
|
@ -11,5 +11,5 @@ func (s *DockerSuite) TestInfoSecurityOptions(c *check.C) {
|
||||||
testRequires(c, SameHostDaemon, seccompEnabled, Apparmor, DaemonIsLinux)
|
testRequires(c, SameHostDaemon, seccompEnabled, Apparmor, DaemonIsLinux)
|
||||||
|
|
||||||
out, _ := dockerCmd(c, "info")
|
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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1375,3 +1375,37 @@ func (s *DockerDaemonSuite) TestRunSeccompJSONNoArchAndArchMap(c *check.C) {
|
||||||
c.Assert(err, check.NotNil)
|
c.Assert(err, check.NotNil)
|
||||||
c.Assert(out, checker.Contains, "'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
|
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")
|
||||||
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ dockerd - Enable daemon mode
|
||||||
[**--raw-logs**]
|
[**--raw-logs**]
|
||||||
[**--registry-mirror**[=*[]*]]
|
[**--registry-mirror**[=*[]*]]
|
||||||
[**-s**|**--storage-driver**[=*STORAGE-DRIVER*]]
|
[**-s**|**--storage-driver**[=*STORAGE-DRIVER*]]
|
||||||
|
[**--seccomp-profile**[=*SECCOMP-PROFILE-PATH*]]
|
||||||
[**--selinux-enabled**]
|
[**--selinux-enabled**]
|
||||||
[**--shutdown-timeout**[=*15*]]
|
[**--shutdown-timeout**[=*15*]]
|
||||||
[**--storage-opt**[=*[]*]]
|
[**--storage-opt**[=*[]*]]
|
||||||
|
@ -248,6 +249,9 @@ output otherwise.
|
||||||
**-s**, **--storage-driver**=""
|
**-s**, **--storage-driver**=""
|
||||||
Force the Docker runtime to use a specific storage driver.
|
Force the Docker runtime to use a specific storage driver.
|
||||||
|
|
||||||
|
**--seccomp-profile**=""
|
||||||
|
Path to seccomp profile.
|
||||||
|
|
||||||
**--selinux-enabled**=*true*|*false*
|
**--selinux-enabled**=*true*|*false*
|
||||||
Enable selinux support. Default is false.
|
Enable selinux support. Default is false.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue