From 68894bdc62afa5097cbc865151184eaa607abe9b Mon Sep 17 00:00:00 2001 From: Seth Hoenig Date: Mon, 6 Feb 2023 14:09:19 -0600 Subject: [PATCH] docker: disable driver when running as non-root on cgroups v2 hosts (#16063) * docker: disable driver when running as non-root on cgroups v2 hosts This PR modifies the docker driver to behave like exec when being run as a non-root user on a host machine with cgroups v2 enabled. Because of how cpu resources are managed by the Nomad client, the nomad agent must be run as root to manage docker-created cgroups. * cl: update cl --- .changelog/7794.txt | 3 +++ client/testutil/driver_compatible.go | 8 ++++++++ drivers/docker/fingerprint.go | 13 ++++++++++++- drivers/docker/fingerprint_test.go | 22 ++++++++++++++++++++-- 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 .changelog/7794.txt diff --git a/.changelog/7794.txt b/.changelog/7794.txt new file mode 100644 index 000000000..9d40bf328 --- /dev/null +++ b/.changelog/7794.txt @@ -0,0 +1,3 @@ +```release-note:bug +docker: disable driver when running as non-root on cgv2 hosts +``` diff --git a/client/testutil/driver_compatible.go b/client/testutil/driver_compatible.go index f0f84d5e5..590c913f5 100644 --- a/client/testutil/driver_compatible.go +++ b/client/testutil/driver_compatible.go @@ -15,6 +15,14 @@ func RequireRoot(t *testing.T) { } } +// RequireNonRoot skips tests unless: +// - running as non-root +func RequireNonRoot(t *testing.T) { + if syscall.Geteuid() == 0 { + t.Skip("Test requires non-root") + } +} + // RequireConsul skips tests unless: // - "consul" executable is detected on $PATH func RequireConsul(t *testing.T) { diff --git a/drivers/docker/fingerprint.go b/drivers/docker/fingerprint.go index d6b260d38..b9690b939 100644 --- a/drivers/docker/fingerprint.go +++ b/drivers/docker/fingerprint.go @@ -7,8 +7,10 @@ import ( "strings" "time" + "github.com/hashicorp/nomad/client/lib/cgutil" "github.com/hashicorp/nomad/helper/pointer" "github.com/hashicorp/nomad/plugins/drivers" + "github.com/hashicorp/nomad/plugins/drivers/utils" pstructs "github.com/hashicorp/nomad/plugins/shared/structs" ) @@ -80,10 +82,19 @@ func (d *Driver) handleFingerprint(ctx context.Context, ch chan *drivers.Fingerp func (d *Driver) buildFingerprint() *drivers.Fingerprint { fp := &drivers.Fingerprint{ - Attributes: map[string]*pstructs.Attribute{}, + Attributes: make(map[string]*pstructs.Attribute, 8), Health: drivers.HealthStateHealthy, HealthDescription: drivers.DriverHealthy, } + + // disable if cgv2 && non-root + if cgutil.UseV2 && !utils.IsUnixRoot() { + fp.Health = drivers.HealthStateUndetected + fp.HealthDescription = drivers.DriverRequiresRootMessage + d.setFingerprintFailure() + return fp + } + client, _, err := d.dockerClients() if err != nil { if d.fingerprintSuccessful() { diff --git a/drivers/docker/fingerprint_test.go b/drivers/docker/fingerprint_test.go index b6303164d..de356f18a 100644 --- a/drivers/docker/fingerprint_test.go +++ b/drivers/docker/fingerprint_test.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/nomad/client/testutil" "github.com/hashicorp/nomad/helper/testlog" "github.com/hashicorp/nomad/plugins/drivers" - "github.com/stretchr/testify/require" + "github.com/shoenig/test/must" ) // TestDockerDriver_FingerprintHealth asserts that docker reports healthy @@ -25,5 +25,23 @@ func TestDockerDriver_FingerprintHealth(t *testing.T) { d := NewDockerDriver(ctx, testlog.HCLogger(t)).(*Driver) fp := d.buildFingerprint() - require.Equal(t, drivers.HealthStateHealthy, fp.Health) + must.Eq(t, drivers.HealthStateHealthy, fp.Health) +} + +// TestDockerDriver_NonRoot_CGV2 tests that the docker drivers is not enabled +// when running as a non-root user on a machine with a v2 cgroups controller. +func TestDockerDriver_NonRoot_CGV2(t *testing.T) { + ci.Parallel(t) + testutil.DockerCompatible(t) + testutil.CgroupsCompatibleV2(t) + testutil.RequireNonRoot(t) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + d := NewDockerDriver(ctx, testlog.HCLogger(t)).(*Driver) + + fp := d.buildFingerprint() + must.Eq(t, drivers.HealthStateUndetected, fp.Health) + must.Eq(t, drivers.DriverRequiresRootMessage, fp.HealthDescription) }