From 483aa6294b457ad4f60df91e46c0038a0e953dad Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 7 Jan 2022 12:54:47 +0100 Subject: [PATCH] daemon: load and cache sysInfo on initialization The `daemon.RawSysInfo()` function can be a heavy operation, as it collects information about all cgroups on the host, networking, AppArmor, Seccomp, etc. While looking at our code, I noticed that various parts in the code call this function, potentially even _multiple times_ per container, for example, it is called from: - `verifyPlatformContainerSettings()` - `oci.WithCgroups()` if the daemon has `cpu-rt-period` or `cpu-rt-runtime` configured - in `ContainerDecoder.DecodeConfig()`, which is called on boith `container create` and `container commit` Given that this information is not expected to change during the daemon's lifecycle, and various information coming from this (such as seccomp and apparmor status) was already cached, we may as well load it once, and cache the results in the daemon instance. This patch updates `daemon.RawSysInfo()` to use a `sync.Once()` so that it's only executed once for the daemon's lifecycle. Signed-off-by: Sebastiaan van Stijn --- daemon/container_linux.go | 11 ++++------- daemon/daemon.go | 20 ++++++++++++++++---- daemon/daemon_unix.go | 13 ++++++------- daemon/daemon_unsupported.go | 5 ++--- daemon/daemon_windows.go | 5 ++--- daemon/seccomp_linux.go | 2 +- daemon/seccomp_linux_test.go | 20 +++++++++++--------- 7 files changed, 42 insertions(+), 34 deletions(-) diff --git a/daemon/container_linux.go b/daemon/container_linux.go index 8d449f700b..61a6d0ba6a 100644 --- a/daemon/container_linux.go +++ b/daemon/container_linux.go @@ -11,7 +11,7 @@ import ( func (daemon *Daemon) saveAppArmorConfig(container *container.Container) error { container.AppArmorProfile = "" // we don't care about the previous value. - if !daemon.apparmorEnabled { + if !daemon.RawSysInfo().AppArmor { return nil // if apparmor is disabled there is nothing to do here. } @@ -19,13 +19,10 @@ func (daemon *Daemon) saveAppArmorConfig(container *container.Container) error { return errdefs.InvalidParameter(err) } - if !container.HostConfig.Privileged { - if container.AppArmorProfile == "" { - container.AppArmorProfile = defaultAppArmorProfile - } - - } else { + if container.HostConfig.Privileged { container.AppArmorProfile = unconfinedAppArmorProfile + } else if container.AppArmorProfile == "" { + container.AppArmorProfile = defaultAppArmorProfile } return nil } diff --git a/daemon/daemon.go b/daemon/daemon.go index fce36eef82..4d72984d81 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -47,6 +47,7 @@ import ( "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/plugingetter" + "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" "github.com/docker/docker/plugin" @@ -92,8 +93,8 @@ type Daemon struct { netController libnetwork.NetworkController volumes *volumesservice.VolumesService root string - seccompEnabled bool - apparmorEnabled bool + sysInfoOnce sync.Once + sysInfo *sysinfo.SysInfo shutdown bool idMapping *idtools.IdentityMapping graphDriver string // TODO: move graphDriver field to an InfoService @@ -1034,8 +1035,6 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S d.EventsService = events.New() d.root = config.Root d.idMapping = idMapping - d.seccompEnabled = sysInfo.Seccomp - d.apparmorEnabled = sysInfo.AppArmor d.linkIndex = newLinkIndex() @@ -1472,3 +1471,16 @@ func (daemon *Daemon) BuilderBackend() builder.Backend { *images.ImageService }{daemon, daemon.imageService} } + +// RawSysInfo returns *sysinfo.SysInfo . +func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo { + daemon.sysInfoOnce.Do(func() { + // We check if sysInfo is not set here, to allow some test to + // override the actual sysInfo. + if daemon.sysInfo == nil { + daemon.loadSysInfo() + } + }) + + return daemon.sysInfo +} diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 19f8ad8ca8..ba6703c7f5 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -1731,19 +1731,14 @@ func (daemon *Daemon) setupSeccompProfile() error { return nil } -// RawSysInfo returns *sysinfo.SysInfo . -func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo { +func (daemon *Daemon) loadSysInfo() { var siOpts []sysinfo.Opt if daemon.getCgroupDriver() == cgroupSystemdDriver { if euid := os.Getenv("ROOTLESSKIT_PARENT_EUID"); euid != "" { siOpts = append(siOpts, sysinfo.WithCgroup2GroupPath("/user.slice/user-"+euid+".slice")) } } - return sysinfo.New(siOpts...) -} - -func recursiveUnmount(target string) error { - return mount.RecursiveUnmount(target) + daemon.sysInfo = sysinfo.New(siOpts...) } func (daemon *Daemon) initLibcontainerd(ctx context.Context) error { @@ -1757,3 +1752,7 @@ func (daemon *Daemon) initLibcontainerd(ctx context.Context) error { ) return err } + +func recursiveUnmount(target string) error { + return mount.RecursiveUnmount(target) +} diff --git a/daemon/daemon_unsupported.go b/daemon/daemon_unsupported.go index 7666d14f6e..b2a1b5be9b 100644 --- a/daemon/daemon_unsupported.go +++ b/daemon/daemon_unsupported.go @@ -13,7 +13,6 @@ const platformSupported = false func setupResolvConf(config *config.Config) { } -// RawSysInfo returns *sysinfo.SysInfo . -func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo { - return sysinfo.New() +func (daemon *Daemon) loadSysInfo() { + daemon.sysInfo = sysinfo.New() } diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index b9e932f0ec..6487c391f2 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -650,9 +650,8 @@ func (daemon *Daemon) initRuntimes(_ map[string]types.Runtime) error { func setupResolvConf(config *config.Config) { } -// RawSysInfo returns *sysinfo.SysInfo . -func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo { - return sysinfo.New() +func (daemon *Daemon) loadSysInfo() { + daemon.sysInfo = sysinfo.New() } func (daemon *Daemon) initLibcontainerd(ctx context.Context) error { diff --git a/daemon/seccomp_linux.go b/daemon/seccomp_linux.go index 1f4ecb576d..860635e2c1 100644 --- a/daemon/seccomp_linux.go +++ b/daemon/seccomp_linux.go @@ -26,7 +26,7 @@ func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts { if c.HostConfig.Privileged { return nil } - if !daemon.seccompEnabled { + if !daemon.RawSysInfo().Seccomp { if c.SeccompProfile != "" && c.SeccompProfile != dconfig.SeccompProfileDefault { return fmt.Errorf("seccomp is not enabled in your kernel, cannot run a custom seccomp profile") } diff --git a/daemon/seccomp_linux_test.go b/daemon/seccomp_linux_test.go index bb5331da37..22add8393c 100644 --- a/daemon/seccomp_linux_test.go +++ b/daemon/seccomp_linux_test.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/container" dconfig "github.com/docker/docker/daemon/config" doci "github.com/docker/docker/oci" + "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/profiles/seccomp" specs "github.com/opencontainers/runtime-spec/specs-go" "gotest.tools/v3/assert" @@ -31,7 +32,7 @@ func TestWithSeccomp(t *testing.T) { { comment: "unconfined seccompProfile runs unconfined", daemon: &Daemon{ - seccompEnabled: true, + sysInfo: &sysinfo.SysInfo{Seccomp: true}, }, c: &container.Container{ SeccompProfile: dconfig.SeccompProfileUnconfined, @@ -45,7 +46,7 @@ func TestWithSeccomp(t *testing.T) { { comment: "privileged container w/ custom profile runs unconfined", daemon: &Daemon{ - seccompEnabled: true, + sysInfo: &sysinfo.SysInfo{Seccomp: true}, }, c: &container.Container{ SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_LOG\" }", @@ -59,7 +60,7 @@ func TestWithSeccomp(t *testing.T) { { comment: "privileged container w/ default runs unconfined", daemon: &Daemon{ - seccompEnabled: true, + sysInfo: &sysinfo.SysInfo{Seccomp: true}, }, c: &container.Container{ SeccompProfile: "", @@ -73,7 +74,7 @@ func TestWithSeccomp(t *testing.T) { { comment: "privileged container w/ daemon profile runs unconfined", daemon: &Daemon{ - seccompEnabled: true, + sysInfo: &sysinfo.SysInfo{Seccomp: true}, seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"), }, c: &container.Container{ @@ -88,7 +89,7 @@ func TestWithSeccomp(t *testing.T) { { comment: "custom profile when seccomp is disabled returns error", daemon: &Daemon{ - seccompEnabled: false, + sysInfo: &sysinfo.SysInfo{Seccomp: false}, }, c: &container.Container{ SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }", @@ -103,7 +104,7 @@ func TestWithSeccomp(t *testing.T) { { comment: "empty profile name loads default profile", daemon: &Daemon{ - seccompEnabled: true, + sysInfo: &sysinfo.SysInfo{Seccomp: true}, }, c: &container.Container{ SeccompProfile: "", @@ -122,7 +123,7 @@ func TestWithSeccomp(t *testing.T) { { comment: "load container's profile", daemon: &Daemon{ - seccompEnabled: true, + sysInfo: &sysinfo.SysInfo{Seccomp: true}, }, c: &container.Container{ SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }", @@ -143,7 +144,7 @@ func TestWithSeccomp(t *testing.T) { { comment: "load daemon's profile", daemon: &Daemon{ - seccompEnabled: true, + sysInfo: &sysinfo.SysInfo{Seccomp: true}, seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"), }, c: &container.Container{ @@ -165,7 +166,7 @@ func TestWithSeccomp(t *testing.T) { { comment: "load prioritise container profile over daemon's", daemon: &Daemon{ - seccompEnabled: true, + sysInfo: &sysinfo.SysInfo{Seccomp: true}, seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"), }, c: &container.Container{ @@ -185,6 +186,7 @@ func TestWithSeccomp(t *testing.T) { }(), }, } { + x := x t.Run(x.comment, func(t *testing.T) { opts := WithSeccomp(x.daemon, x.c) err := opts(nil, nil, nil, &x.inSpec)