Merge pull request #41817 from simonferquel/desktop-startup-hang

Fix a potential hang when starting after a non-clean shutdown
diff --git a/Dockerfile b/Dockerfile
index 672af8b..c748a07 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -96,10 +96,10 @@
         buildpack-deps:buster@sha256:d0abb4b1e5c664828b93e8b6ac84d10bce45ee469999bef88304be04a2709491 \
         busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 \
         busybox:glibc@sha256:1f81263701cddf6402afe9f33fca0266d9fff379e59b1748f33d3072da71ee85 \
-        debian:buster@sha256:46d659005ca1151087efa997f1039ae45a7bf7a2cbbe2d17d3dcbda632a3ee9a \
+        debian:bullseye@sha256:7190e972ab16aefea4d758ebe42a293f4e5c5be63595f4d03a5b9bf6839a4344 \
         hello-world:latest@sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9 \
         arm32v7/hello-world:latest@sha256:50b8560ad574c779908da71f7ce370c0a2471c098d44d1c8f6b513c5a55eeeb1
-# See also ensureFrozenImagesLinux() in "integration-cli/fixtures_linux_daemon_test.go" (which needs to be updated when adding images to this list)
+# See also frozenImages in "testutil/environment/protect.go" (which needs to be updated when adding images to this list)
 
 FROM base AS cross-false
 
diff --git a/Dockerfile.e2e b/Dockerfile.e2e
index 48dfb05..b798c86 100644
--- a/Dockerfile.e2e
+++ b/Dockerfile.e2e
@@ -21,9 +21,9 @@
 	buildpack-deps:buster@sha256:d0abb4b1e5c664828b93e8b6ac84d10bce45ee469999bef88304be04a2709491 \
 	busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 \
 	busybox:glibc@sha256:1f81263701cddf6402afe9f33fca0266d9fff379e59b1748f33d3072da71ee85 \
-	debian:buster@sha256:46d659005ca1151087efa997f1039ae45a7bf7a2cbbe2d17d3dcbda632a3ee9a \
+	debian:bullseye@sha256:7190e972ab16aefea4d758ebe42a293f4e5c5be63595f4d03a5b9bf6839a4344 \
 	hello-world:latest@sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9
-# See also ensureFrozenImagesLinux() in "integration-cli/fixtures_linux_daemon_test.go" (which needs to be updated when adding images to this list)
+# See also frozenImages in "testutil/environment/protect.go" (which needs to be updated when adding images to this list)
 
 FROM base AS dockercli
 ENV INSTALL_BINARY_NAME=dockercli
diff --git a/TESTING.md b/TESTING.md
index a1217ae..fb0e4e9 100644
--- a/TESTING.md
+++ b/TESTING.md
@@ -47,13 +47,20 @@
 A bug fix may also include new assertions in existing integration tests for the
 API endpoint.
 
+### Writing new integration tests
+
+Note the `integration-cli` tests are deprecated; new tests will be rejected by
+the CI.
+
+Instead, implement new tests under `integration/`.
+
 ### Integration tests environment considerations
 
 When adding new tests or modifying existing tests under `integration/`, testing
 environment should be properly considered. `skip.If` from 
 [gotest.tools/skip](https://godoc.org/gotest.tools/skip) can be used to make the 
 test run conditionally. Full testing environment conditions can be found at 
-[environment.go](https://github.com/moby/moby/blob/cb37987ee11655ed6bbef663d245e55922354c68/internal/test/environment/environment.go)
+[environment.go](https://github.com/moby/moby/blob/6b6eeed03b963a27085ea670f40cd5ff8a61f32e/testutil/environment/environment.go)
 
 Here is a quick example. If the test needs to interact with a docker daemon on 
 the same host, the following condition should be checked within the test code
diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go
index 96c3194..7fe8a6c 100644
--- a/cmd/dockerd/daemon.go
+++ b/cmd/dockerd/daemon.go
@@ -184,7 +184,7 @@
 	}, logrus.StandardLogger())
 
 	// Notify that the API is active, but before daemon is set up.
-	preNotifySystem()
+	preNotifyReady()
 
 	pluginStore := plugin.NewStore()
 
@@ -242,13 +242,15 @@
 	go cli.api.Wait(serveAPIWait)
 
 	// after the daemon is done setting up we can notify systemd api
-	notifySystem()
+	notifyReady()
 
 	// Daemon is fully initialized and handling API traffic
 	// Wait for serve API to complete
 	errAPI := <-serveAPIWait
 	c.Cleanup()
 
+	// notify systemd that we're shutting down
+	notifyStopping()
 	shutdownDaemon(d)
 
 	// Stop notification processing and any background processes
diff --git a/cmd/dockerd/daemon_freebsd.go b/cmd/dockerd/daemon_freebsd.go
index 6d013b8..1bb4904 100644
--- a/cmd/dockerd/daemon_freebsd.go
+++ b/cmd/dockerd/daemon_freebsd.go
@@ -1,9 +1,13 @@
 package main
 
-// preNotifySystem sends a message to the host when the API is active, but before the daemon is
-func preNotifySystem() {
+// preNotifyReady sends a message to the host when the API is active, but before the daemon is
+func preNotifyReady() {
 }
 
-// notifySystem sends a message to the host when the server is ready to be used
-func notifySystem() {
+// notifyReady sends a message to the host when the server is ready to be used
+func notifyReady() {
+}
+
+// notifyStopping sends a message to the host when the server is shutting down
+func notifyStopping() {
 }
diff --git a/cmd/dockerd/daemon_linux.go b/cmd/dockerd/daemon_linux.go
index 1e7a2ca..aade57a 100644
--- a/cmd/dockerd/daemon_linux.go
+++ b/cmd/dockerd/daemon_linux.go
@@ -2,12 +2,17 @@
 
 import systemdDaemon "github.com/coreos/go-systemd/v22/daemon"
 
-// preNotifySystem sends a message to the host when the API is active, but before the daemon is
-func preNotifySystem() {
+// preNotifyReady sends a message to the host when the API is active, but before the daemon is
+func preNotifyReady() {
 }
 
-// notifySystem sends a message to the host when the server is ready to be used
-func notifySystem() {
+// notifyReady sends a message to the host when the server is ready to be used
+func notifyReady() {
 	// Tell the init daemon we are accepting requests
 	go systemdDaemon.SdNotify(false, systemdDaemon.SdNotifyReady)
 }
+
+// notifyStopping sends a message to the host when the server is shutting down
+func notifyStopping() {
+	go systemdDaemon.SdNotify(false, systemdDaemon.SdNotifyStopping)
+}
diff --git a/cmd/dockerd/daemon_windows.go b/cmd/dockerd/daemon_windows.go
index 2d74531..1a9c50b 100644
--- a/cmd/dockerd/daemon_windows.go
+++ b/cmd/dockerd/daemon_windows.go
@@ -27,8 +27,8 @@
 	return filepath.Join(root, `\config`), nil
 }
 
-// preNotifySystem sends a message to the host when the API is active, but before the daemon is
-func preNotifySystem() {
+// preNotifyReady sends a message to the host when the API is active, but before the daemon is
+func preNotifyReady() {
 	// start the service now to prevent timeouts waiting for daemon to start
 	// but still (eventually) complete all requests that are sent after this
 	if service != nil {
@@ -39,8 +39,12 @@
 	}
 }
 
-// notifySystem sends a message to the host when the server is ready to be used
-func notifySystem() {
+// notifyReady sends a message to the host when the server is ready to be used
+func notifyReady() {
+}
+
+// notifyStopping sends a message to the host when the server is shutting down
+func notifyStopping() {
 }
 
 // notifyShutdown is called after the daemon shuts down but before the process exits.
diff --git a/daemon/graphdriver/overlay2/overlay.go b/daemon/graphdriver/overlay2/overlay.go
index 39e8cf2..11d5302 100644
--- a/daemon/graphdriver/overlay2/overlay.go
+++ b/daemon/graphdriver/overlay2/overlay.go
@@ -573,14 +573,14 @@
 	// the page size. The mount syscall fails if the mount data cannot
 	// fit within a page and relative links make the mount data much
 	// smaller at the expense of requiring a fork exec to chroot.
-	if len(mountData) > pageSize {
+	if len(mountData) > pageSize-1 {
 		if readonly {
 			opts = indexOff + "lowerdir=" + path.Join(id, diffDirName) + ":" + string(lowers)
 		} else {
 			opts = indexOff + "lowerdir=" + string(lowers) + ",upperdir=" + path.Join(id, diffDirName) + ",workdir=" + path.Join(id, workDirName)
 		}
 		mountData = label.FormatMountLabel(opts, mountLabel)
-		if len(mountData) > pageSize {
+		if len(mountData) > pageSize-1 {
 			return nil, fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData))
 		}
 
diff --git a/hack/dockerfile/install/proxy.installer b/hack/dockerfile/install/proxy.installer
index 1a48ec9..a9562df 100755
--- a/hack/dockerfile/install/proxy.installer
+++ b/hack/dockerfile/install/proxy.installer
@@ -3,7 +3,7 @@
 # LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When
 # updating the binary version, consider updating github.com/docker/libnetwork
 # in vendor.conf accordingly
-: "${LIBNETWORK_COMMIT:=5c6a95bfb20c61571a00f913c6b91959ede84e8d}"
+: "${LIBNETWORK_COMMIT:=fa125a3512ee0f6187721c88582bf8c4378bd4d7}"
 
 install_proxy() {
 	case "$1" in
diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go
index 81bc7e6..378b479 100644
--- a/integration-cli/docker_cli_daemon_test.go
+++ b/integration-cli/docker_cli_daemon_test.go
@@ -1776,7 +1776,7 @@
 	defer mount.Unmount(testDir)
 
 	// create a 3MiB image (with a 2MiB ext4 fs) and mount it as graph root
-	// Why in a container? Because `mount` sometimes behaves weirdly and often fails outright on this test in debian:buster (which is what the test suite runs under if run from the Makefile)
+	// Why in a container? Because `mount` sometimes behaves weirdly and often fails outright on this test in debian:bullseye (which is what the test suite runs under if run from the Makefile)
 	dockerCmd(c, "run", "--rm", "-v", testDir+":/test", "busybox", "sh", "-c", "dd of=/test/testfs.img bs=1M seek=3 count=0")
 	icmd.RunCommand("mkfs.ext4", "-F", filepath.Join(testDir, "testfs.img")).Assert(c, icmd.Success)
 
@@ -1787,7 +1787,7 @@
 	defer s.d.Stop(c)
 
 	// pull a repository large enough to overfill the mounted filesystem
-	pullOut, err := s.d.Cmd("pull", "debian:buster")
+	pullOut, err := s.d.Cmd("pull", "debian:bullseye")
 	assert.Assert(c, err != nil, pullOut)
 	assert.Assert(c, strings.Contains(pullOut, "no space left on device"))
 }
diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go
index b432133..eb9d46b 100644
--- a/integration-cli/docker_cli_network_unix_test.go
+++ b/integration-cli/docker_cli_network_unix_test.go
@@ -1574,7 +1574,7 @@
 	dockerCmd(c, "network", "create", "-d", "bridge", "nw1")
 
 	// Sending garbage to embedded DNS shouldn't crash the daemon
-	dockerCmd(c, "run", "-i", "--net=nw1", "--name=c1", "debian:buster", "bash", "-c", "echo InvalidQuery > /dev/udp/127.0.0.11/53")
+	dockerCmd(c, "run", "-i", "--net=nw1", "--name=c1", "debian:bullseye", "bash", "-c", "echo InvalidQuery > /dev/udp/127.0.0.11/53")
 }
 
 func (s *DockerSuite) TestDockerNetworkConnectFailsNoInspectChange(c *testing.T) {
diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go
index e0c1bbe..2fdc887 100644
--- a/integration-cli/docker_cli_run_test.go
+++ b/integration-cli/docker_cli_run_test.go
@@ -2927,7 +2927,7 @@
 
 	go func() {
 		name := "acidburn"
-		out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:buster", "unshare", "-p", "-m", "-f", "-r", "--mount-proc=/proc", "mount")
+		out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:bullseye", "unshare", "-p", "-m", "-f", "-r", "--mount-proc=/proc", "mount")
 		if err == nil ||
 			!(strings.Contains(strings.ToLower(out), "permission denied") ||
 				strings.Contains(strings.ToLower(out), "operation not permitted")) {
@@ -2939,7 +2939,7 @@
 
 	go func() {
 		name := "cereal"
-		out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:buster", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
+		out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:bullseye", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
 		if err == nil ||
 			!(strings.Contains(strings.ToLower(out), "mount: cannot mount none") ||
 				strings.Contains(strings.ToLower(out), "permission denied") ||
@@ -2953,7 +2953,7 @@
 	/* Ensure still fails if running privileged with the default policy */
 	go func() {
 		name := "crashoverride"
-		out, _, err := dockerCmdWithError("run", "--privileged", "--security-opt", "seccomp=unconfined", "--security-opt", "apparmor=docker-default", "--name", name, "debian:buster", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
+		out, _, err := dockerCmdWithError("run", "--privileged", "--security-opt", "seccomp=unconfined", "--security-opt", "apparmor=docker-default", "--name", name, "debian:bullseye", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
 		if err == nil ||
 			!(strings.Contains(strings.ToLower(out), "mount: cannot mount none") ||
 				strings.Contains(strings.ToLower(out), "permission denied") ||
diff --git a/integration-cli/docker_cli_run_unix_test.go b/integration-cli/docker_cli_run_unix_test.go
index a747f90..44de261 100644
--- a/integration-cli/docker_cli_run_unix_test.go
+++ b/integration-cli/docker_cli_run_unix_test.go
@@ -873,12 +873,12 @@
 		assert.Assert(c, strings.Contains(out, option))
 	}
 
-	// We use debian:buster as there is no findmnt in busybox. Also the output will be in the format of
+	// We use debian:bullseye as there is no findmnt in busybox. Also the output will be in the format of
 	// TARGET PROPAGATION
 	// /tmp   shared
 	// so we only capture `shared` here.
 	expectedOptions = []string{"shared"}
-	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:shared", "debian:buster", "findmnt", "-o", "TARGET,PROPAGATION", "/tmp")
+	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:shared", "debian:bullseye", "findmnt", "-o", "TARGET,PROPAGATION", "/tmp")
 	for _, option := range expectedOptions {
 		assert.Assert(c, strings.Contains(out, option))
 	}
@@ -914,7 +914,7 @@
 	})
 }
 
-// TestRunSeccompProfileDenyUnshare checks that 'docker run --security-opt seccomp=/tmp/profile.json debian:buster unshare' exits with operation not permitted.
+// TestRunSeccompProfileDenyUnshare checks that 'docker run --security-opt seccomp=/tmp/profile.json debian:bullseye unshare' exits with operation not permitted.
 func (s *DockerSuite) TestRunSeccompProfileDenyUnshare(c *testing.T) {
 	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotArm, Apparmor)
 	jsonData := `{
@@ -937,7 +937,7 @@
 	}
 	icmd.RunCommand(dockerBinary, "run", "--security-opt", "apparmor=unconfined",
 		"--security-opt", "seccomp="+tmpFile.Name(),
-		"debian:buster", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc").Assert(c, icmd.Expected{
+		"debian:bullseye", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc").Assert(c, icmd.Expected{
 		ExitCode: 1,
 		Err:      "Operation not permitted",
 	})
@@ -977,7 +977,7 @@
 	})
 }
 
-// TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:buster unshare --map-root-user --user sh -c whoami' with a specific profile to
+// TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:bullseye unshare --map-root-user --user sh -c whoami' with a specific profile to
 // deny unshare of a userns exits with operation not permitted.
 func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *testing.T) {
 	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotArm, Apparmor)
@@ -1009,7 +1009,7 @@
 	}
 	icmd.RunCommand(dockerBinary, "run",
 		"--security-opt", "apparmor=unconfined", "--security-opt", "seccomp="+tmpFile.Name(),
-		"debian:buster", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami").Assert(c, icmd.Expected{
+		"debian:bullseye", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami").Assert(c, icmd.Expected{
 		ExitCode: 1,
 		Err:      "Operation not permitted",
 	})
@@ -1061,12 +1061,12 @@
 	icmd.RunCommand(dockerBinary, "run", "syscall-test", "exit32-test").Assert(c, icmd.Success)
 }
 
-// TestRunSeccompAllowSetrlimit checks that 'docker run debian:buster ulimit -v 1048510' succeeds.
+// TestRunSeccompAllowSetrlimit checks that 'docker run debian:bullseye ulimit -v 1048510' succeeds.
 func (s *DockerSuite) TestRunSeccompAllowSetrlimit(c *testing.T) {
 	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
 
 	// ulimit uses setrlimit, so we want to make sure we don't break it
-	icmd.RunCommand(dockerBinary, "run", "debian:buster", "bash", "-c", "ulimit -v 1048510").Assert(c, icmd.Success)
+	icmd.RunCommand(dockerBinary, "run", "debian:bullseye", "bash", "-c", "ulimit -v 1048510").Assert(c, icmd.Success)
 }
 
 func (s *DockerSuite) TestRunSeccompDefaultProfileAcct(c *testing.T) {
@@ -1362,7 +1362,7 @@
 func (s *DockerSuite) TestRunSeccompWithDefaultProfile(c *testing.T) {
 	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
 
-	out, _, err := dockerCmdWithError("run", "--security-opt", "seccomp=../profiles/seccomp/default.json", "debian:buster", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami")
+	out, _, err := dockerCmdWithError("run", "--security-opt", "seccomp=../profiles/seccomp/default.json", "debian:bullseye", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami")
 	assert.ErrorContains(c, err, "", out)
 	assert.Equal(c, strings.TrimSpace(out), "unshare: unshare failed: Operation not permitted")
 }
diff --git a/integration-cli/fixtures_linux_daemon_test.go b/integration-cli/fixtures_linux_daemon_test.go
index f211a11..acd593d 100644
--- a/integration-cli/fixtures_linux_daemon_test.go
+++ b/integration-cli/fixtures_linux_daemon_test.go
@@ -49,7 +49,7 @@
 
 	dockerFile := filepath.Join(tmp, "Dockerfile")
 	content := []byte(`
-	FROM debian:buster
+	FROM debian:bullseye
 	COPY . /usr/bin/
 	`)
 	err = ioutil.WriteFile(dockerFile, content, 0600)
@@ -103,7 +103,7 @@
 
 	dockerfile := filepath.Join(tmp, "Dockerfile")
 	content := `
-	FROM debian:buster
+	FROM debian:bullseye
 	COPY . /usr/bin
 	RUN chmod +s /usr/bin/nnp-test
 	`
diff --git a/integration/build/build_userns_linux_test.go b/integration/build/build_userns_linux_test.go
new file mode 100644
index 0000000..5e0c4d4
--- /dev/null
+++ b/integration/build/build_userns_linux_test.go
@@ -0,0 +1,140 @@
+package build // import "github.com/docker/docker/integration/build"
+
+import (
+	"bufio"
+	"bytes"
+	"context"
+	"io"
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/pkg/stdcopy"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/fakecontext"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
+)
+
+// Implements a test for https://github.com/moby/moby/issues/41723
+// Images built in a user-namespaced daemon should have capabilities serialised in
+// VFS_CAP_REVISION_2 (no user-namespace root uid) format rather than V3 (that includes
+// the root uid).
+func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !testEnv.IsUserNamespaceInKernel())
+	skip.If(t, testEnv.IsRootless())
+
+	const imageTag = "capabilities:1.0"
+
+	tmp, err := ioutil.TempDir("", "integration-")
+	assert.NilError(t, err)
+	defer os.RemoveAll(tmp)
+
+	dUserRemap := daemon.New(t)
+	dUserRemap.StartWithBusybox(t, "--userns-remap", "default")
+	dUserRemapRunning := true
+	defer func() {
+		if dUserRemapRunning {
+			dUserRemap.Stop(t)
+		}
+	}()
+
+	dockerfile := `
+		FROM debian:bullseye
+		RUN setcap CAP_NET_BIND_SERVICE=+eip /bin/sleep
+	`
+
+	ctx := context.Background()
+	source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
+	defer source.Close()
+
+	clientUserRemap := dUserRemap.NewClientT(t)
+	resp, err := clientUserRemap.ImageBuild(ctx,
+		source.AsTarReader(t),
+		types.ImageBuildOptions{
+			Tags: []string{imageTag},
+		})
+	assert.NilError(t, err)
+	defer resp.Body.Close()
+	buf := make([]byte, 1024)
+	for {
+		n, err := resp.Body.Read(buf)
+		if err != nil && err != io.EOF {
+			t.Fatalf("Error reading ImageBuild response: %v", err)
+			break
+		}
+		if n == 0 {
+			break
+		}
+	}
+
+	reader, err := clientUserRemap.ImageSave(ctx, []string{imageTag})
+	assert.NilError(t, err, "failed to download capabilities image")
+	defer reader.Close()
+
+	tar, err := os.Create(tmp + "/image.tar")
+	assert.NilError(t, err, "failed to create image tar file")
+	defer tar.Close()
+
+	_, err = io.Copy(tar, reader)
+	assert.NilError(t, err, "failed to write image tar file")
+
+	dUserRemap.Stop(t)
+	dUserRemap.Cleanup(t)
+	dUserRemapRunning = false
+
+	dNoUserRemap := daemon.New(t)
+	dNoUserRemap.StartWithBusybox(t)
+	defer dNoUserRemap.Stop(t)
+
+	clientNoUserRemap := dNoUserRemap.NewClientT(t)
+
+	tarFile, err := os.Open(tmp + "/image.tar")
+	assert.NilError(t, err, "failed to open image tar file")
+
+	tarReader := bufio.NewReader(tarFile)
+	loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader, false)
+	assert.NilError(t, err, "failed to load image tar file")
+	defer loadResp.Body.Close()
+	for {
+		n, err := loadResp.Body.Read(buf)
+		if err != nil && err != io.EOF {
+			t.Fatalf("Error reading ImageLoad response: %v", err)
+			break
+		}
+		if n == 0 {
+			break
+		}
+	}
+
+	cid := container.Run(ctx, t, clientNoUserRemap,
+		container.WithImage(imageTag),
+		container.WithCmd("/sbin/getcap", "-n", "/bin/sleep"),
+	)
+	logReader, err := clientNoUserRemap.ContainerLogs(ctx, cid, types.ContainerLogsOptions{
+		ShowStdout: true,
+	})
+	assert.NilError(t, err)
+
+	actualStdout := new(bytes.Buffer)
+	actualStderr := ioutil.Discard
+	_, err = stdcopy.StdCopy(actualStdout, actualStderr, logReader)
+	assert.NilError(t, err)
+	if strings.TrimSpace(actualStdout.String()) != "/bin/sleep cap_net_bind_service=eip" {
+		// Activate when fix is merged: https://github.com/moby/moby/pull/41724
+		//t.Fatalf("run produced invalid output: %q, expected %q", actualStdout.String(), "/bin/sleep cap_net_bind_service=eip")
+		// t.Logf("run produced invalid output (expected until #41724 merges): %q, expected %q",
+		// 	actualStdout.String(),
+		// 	"/bin/sleep cap_net_bind_service=eip")
+	} else {
+		// Shouldn't happen until fix is merged: https://github.com/moby/moby/pull/41724
+		t.Fatalf("run produced valid output (unexpected until #41724 merges): %q, expected %q",
+			actualStdout.String(),
+			"/bin/sleep cap_net_bind_service=eip")
+	}
+}
diff --git a/testutil/environment/environment.go b/testutil/environment/environment.go
index f8df370..23ab204 100644
--- a/testutil/environment/environment.go
+++ b/testutil/environment/environment.go
@@ -167,6 +167,27 @@
 	return os.Getenv("DOCKER_ROOTLESS") != ""
 }
 
+// IsUserNamespaceInKernel returns whether the kernel supports user namespaces
+func (e *Execution) IsUserNamespaceInKernel() bool {
+	if _, err := os.Stat("/proc/self/uid_map"); os.IsNotExist(err) {
+		/*
+		 * This kernel-provided file only exists if user namespaces are
+		 * supported
+		 */
+		return false
+	}
+
+	// We need extra check on redhat based distributions
+	if f, err := os.Open("/sys/module/user_namespace/parameters/enable"); err == nil {
+		defer f.Close()
+		b := make([]byte, 1)
+		_, _ = f.Read(b)
+		return string(b) != "N"
+	}
+
+	return true
+}
+
 // HasExistingImage checks whether there is an image with the given reference.
 // Note that this is done by filtering and then checking whether there were any
 // results -- so ambiguous references might result in false-positives.
diff --git a/testutil/environment/protect.go b/testutil/environment/protect.go
index 282280e..1ea0e43 100644
--- a/testutil/environment/protect.go
+++ b/testutil/environment/protect.go
@@ -10,7 +10,7 @@
 	"gotest.tools/v3/assert"
 )
 
-var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:buster"}
+var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:bullseye"}
 
 type protectedElements struct {
 	containers map[string]struct{}
diff --git a/vendor.conf b/vendor.conf
index 5bdcd77..0c74186 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -47,7 +47,7 @@
 # libnetwork
 
 # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy.installer accordingly
-github.com/docker/libnetwork                        5c6a95bfb20c61571a00f913c6b91959ede84e8d 
+github.com/docker/libnetwork                        fa125a3512ee0f6187721c88582bf8c4378bd4d7 
 github.com/docker/go-events                         e31b211e4f1cd09aa76fe4ac244571fab96ae47f
 github.com/armon/go-radix                           e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
 github.com/armon/go-metrics                         eb0af217e5e9747e41dd5303755356b62d28e3ec
@@ -176,7 +176,7 @@
 # metrics
 github.com/docker/go-metrics                        b619b3592b65de4f087d9f16863a7e6ff905973c # v0.0.1
 
-github.com/opencontainers/selinux                   25504e34a9826d481f6e2903963ecaa881749124 # v1.6.0
+github.com/opencontainers/selinux                   63ad55b76fd78d4c76c2f5491f68516e60c9d523 # v1.7.0
 github.com/willf/bitset                             559910e8471e48d76d9e5a1ba15842dee77ad45d # v1.1.11
 
 
diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go b/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go
index 75b17d6..56a9271 100644
--- a/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go
+++ b/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go
@@ -11,72 +11,114 @@
 	"github.com/sirupsen/logrus"
 )
 
-var (
-	defaultBindingIP   = net.IPv4(0, 0, 0, 0)
-	defaultBindingIPV6 = net.ParseIP("::")
-)
-
 func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
 	if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil {
 		return nil, nil
 	}
 
-	defHostIP := defaultBindingIP
+	defHostIP := net.IPv4zero // 0.0.0.0
 	if reqDefBindIP != nil {
 		defHostIP = reqDefBindIP
 	}
 
-	// IPv4 port binding including user land proxy
-	pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled)
-	if err != nil {
-		return nil, err
+	var containerIPv6 net.IP
+	if ep.addrv6 != nil {
+		containerIPv6 = ep.addrv6.IP
 	}
 
-	// IPv6 port binding excluding user land proxy
-	if n.driver.config.EnableIP6Tables && ep.addrv6 != nil {
-		// TODO IPv6 custom default binding IP
-		pbv6, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addrv6.IP, defaultBindingIPV6, false)
-		if err != nil {
-			// ensure we clear the previous allocated IPv4 ports
-			n.releasePortsInternal(pb)
-			return nil, err
-		}
-
-		pb = append(pb, pbv6...)
+	pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, containerIPv6, defHostIP, ulPxyEnabled)
+	if err != nil {
+		return nil, err
 	}
 	return pb, nil
 }
 
-func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
+func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIPv4, containerIPv6, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
 	bs := make([]types.PortBinding, 0, len(bindings))
 	for _, c := range bindings {
-		b := c.GetCopy()
-		if err := n.allocatePort(&b, containerIP, defHostIP, ulPxyEnabled); err != nil {
-			// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
-			if cuErr := n.releasePortsInternal(bs); cuErr != nil {
-				logrus.Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr)
+		bIPv4 := c.GetCopy()
+		bIPv6 := c.GetCopy()
+		// Allocate IPv4 Port mappings
+		if ok := n.validatePortBindingIPv4(&bIPv4, containerIPv4, defHostIP); ok {
+			if err := n.allocatePort(&bIPv4, ulPxyEnabled); err != nil {
+				// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
+				if cuErr := n.releasePortsInternal(bs); cuErr != nil {
+					logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv4 port bindings: %v", bIPv4, cuErr)
+				}
+				return nil, err
 			}
-			return nil, err
+			bs = append(bs, bIPv4)
 		}
-		bs = append(bs, b)
+		// Allocate IPv6 Port mappings
+		if ok := n.validatePortBindingIPv6(&bIPv6, containerIPv6, defHostIP); ok {
+			if err := n.allocatePort(&bIPv6, ulPxyEnabled); err != nil {
+				// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
+				if cuErr := n.releasePortsInternal(bs); cuErr != nil {
+					logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv6 port bindings: %v", bIPv6, cuErr)
+				}
+				return nil, err
+			}
+			bs = append(bs, bIPv6)
+		}
 	}
 	return bs, nil
 }
 
-func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) error {
+// validatePortBindingIPv4 validates the port binding, populates the missing Host IP field and returns true
+// if this is a valid IPv4 binding, else returns false
+func (n *bridgeNetwork) validatePortBindingIPv4(bnd *types.PortBinding, containerIPv4, defHostIP net.IP) bool {
+	//Return early if there is a valid Host IP, but its not a IPv6 address
+	if len(bnd.HostIP) > 0 && bnd.HostIP.To4() == nil {
+		return false
+	}
+	// Adjust the host address in the operational binding
+	if len(bnd.HostIP) == 0 {
+		// Return early if the default binding address is an IPv6 address
+		if defHostIP.To4() == nil {
+			return false
+		}
+		bnd.HostIP = defHostIP
+	}
+	bnd.IP = containerIPv4
+	return true
+
+}
+
+// validatePortBindingIPv6 validates the port binding, populates the missing Host IP field and returns true
+// if this is a valid IP6v binding, else returns false
+func (n *bridgeNetwork) validatePortBindingIPv6(bnd *types.PortBinding, containerIPv6, defHostIP net.IP) bool {
+	// Return early if there is no IPv6 container endpoint
+	if containerIPv6 == nil {
+		return false
+	}
+	// Return early if there is a valid Host IP, which is a IPv4 address
+	if len(bnd.HostIP) > 0 && bnd.HostIP.To4() != nil {
+		return false
+	}
+
+	// Setup a binding to  "::" if Host IP is empty and the default binding IP is 0.0.0.0
+	if len(bnd.HostIP) == 0 {
+		if defHostIP.Equal(net.IPv4zero) {
+			bnd.HostIP = net.IPv6zero
+			// If the default binding IP is an IPv6 address, use it
+		} else if defHostIP.To4() == nil {
+			bnd.HostIP = defHostIP
+			// Return false if default binding ip is an IPv4 address
+		} else {
+			return false
+		}
+	}
+	bnd.IP = containerIPv6
+	return true
+
+}
+
+func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, ulPxyEnabled bool) error {
 	var (
 		host net.Addr
 		err  error
 	)
 
-	// Store the container interface address in the operational binding
-	bnd.IP = containerIP
-
-	// Adjust the host address in the operational binding
-	if len(bnd.HostIP) == 0 {
-		bnd.HostIP = defHostIP
-	}
-
 	// Adjust HostPortEnd if this is not a range.
 	if bnd.HostPortEnd == 0 {
 		bnd.HostPortEnd = bnd.HostPort
@@ -90,7 +132,7 @@
 
 	portmapper := n.portMapper
 
-	if containerIP.To4() == nil {
+	if bnd.IP.To4() == nil {
 		portmapper = n.portMapperV6
 	}
 
diff --git a/vendor/github.com/docker/libnetwork/portmapper/mapper.go b/vendor/github.com/docker/libnetwork/portmapper/mapper.go
index ae74145..33f4ec9 100644
--- a/vendor/github.com/docker/libnetwork/portmapper/mapper.go
+++ b/vendor/github.com/docker/libnetwork/portmapper/mapper.go
@@ -151,20 +151,16 @@
 	}
 
 	containerIP, containerPort := getIPAndPort(m.container)
-	if pm.checkIP(hostIP) {
-		if err := pm.AppendForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
-			return nil, err
-		}
+	if err := pm.AppendForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
+		return nil, err
 	}
 
 	cleanup := func() error {
 		// need to undo the iptables rules before we return
 		m.userlandProxy.Stop()
-		if pm.checkIP(hostIP) {
-			pm.DeleteForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
-			if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
-				return err
-			}
+		pm.DeleteForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
+		if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
+			return err
 		}
 
 		return nil
diff --git a/vendor/github.com/docker/libnetwork/portmapper/mapper_linux.go b/vendor/github.com/docker/libnetwork/portmapper/mapper_linux.go
index c565efd..0e76c54 100644
--- a/vendor/github.com/docker/libnetwork/portmapper/mapper_linux.go
+++ b/vendor/github.com/docker/libnetwork/portmapper/mapper_linux.go
@@ -44,11 +44,3 @@
 	}
 	return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName)
 }
-
-// checkIP checks if IP is valid and matching to chain version
-func (pm *PortMapper) checkIP(ip net.IP) bool {
-	if pm.chain == nil || pm.chain.IPTable.Version == iptables.IPv4 {
-		return ip.To4() != nil
-	}
-	return ip.To16() != nil
-}
diff --git a/vendor/github.com/docker/libnetwork/portmapper/proxy.go b/vendor/github.com/docker/libnetwork/portmapper/proxy.go
index 12e4122..f945851 100644
--- a/vendor/github.com/docker/libnetwork/portmapper/proxy.go
+++ b/vendor/github.com/docker/libnetwork/portmapper/proxy.go
@@ -19,6 +19,16 @@
 	Stop() error
 }
 
+// ipVersion refers to IP version - v4 or v6
+type ipVersion string
+
+const (
+	// IPv4 is version 4
+	ipv4 ipVersion = "4"
+	// IPv4 is version 6
+	ipv6 ipVersion = "6"
+)
+
 // proxyCommand wraps an exec.Cmd to run the userland TCP and UDP
 // proxies as separate processes.
 type proxyCommand struct {
@@ -77,21 +87,27 @@
 // port allocations on bound port, because without userland proxy we using
 // iptables rules and not net.Listen
 type dummyProxy struct {
-	listener io.Closer
-	addr     net.Addr
+	listener  io.Closer
+	addr      net.Addr
+	ipVersion ipVersion
 }
 
 func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, error) {
+	// detect version of hostIP to bind only to correct version
+	version := ipv4
+	if hostIP.To4() == nil {
+		version = ipv6
+	}
 	switch proto {
 	case "tcp":
 		addr := &net.TCPAddr{IP: hostIP, Port: hostPort}
-		return &dummyProxy{addr: addr}, nil
+		return &dummyProxy{addr: addr, ipVersion: version}, nil
 	case "udp":
 		addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
-		return &dummyProxy{addr: addr}, nil
+		return &dummyProxy{addr: addr, ipVersion: version}, nil
 	case "sctp":
 		addr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: hostPort}
-		return &dummyProxy{addr: addr}, nil
+		return &dummyProxy{addr: addr, ipVersion: version}, nil
 	default:
 		return nil, fmt.Errorf("Unknown addr type: %s", proto)
 	}
@@ -100,19 +116,19 @@
 func (p *dummyProxy) Start() error {
 	switch addr := p.addr.(type) {
 	case *net.TCPAddr:
-		l, err := net.ListenTCP("tcp", addr)
+		l, err := net.ListenTCP("tcp"+string(p.ipVersion), addr)
 		if err != nil {
 			return err
 		}
 		p.listener = l
 	case *net.UDPAddr:
-		l, err := net.ListenUDP("udp", addr)
+		l, err := net.ListenUDP("udp"+string(p.ipVersion), addr)
 		if err != nil {
 			return err
 		}
 		p.listener = l
 	case *sctp.SCTPAddr:
-		l, err := sctp.ListenSCTP("sctp", addr)
+		l, err := sctp.ListenSCTP("sctp"+string(p.ipVersion), addr)
 		if err != nil {
 			return err
 		}
diff --git a/vendor/github.com/opencontainers/selinux/README.md b/vendor/github.com/opencontainers/selinux/README.md
index a9f0cc1..41f4df7 100644
--- a/vendor/github.com/opencontainers/selinux/README.md
+++ b/vendor/github.com/opencontainers/selinux/README.md
@@ -18,5 +18,5 @@
 
 If you find an issue, please follow the [security][security] protocol to report it.
 
-[security]: https://github.com/opencontainers/org/blob/master/security
+[security]: https://github.com/opencontainers/org/blob/master/SECURITY.md
 [code-of-conduct]: https://github.com/opencontainers/org/blob/master/CODE_OF_CONDUCT.md
diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go
index 10ac15a..988adc8 100644
--- a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go
@@ -27,14 +27,14 @@
 // the container.  A list of options can be passed into this function to alter
 // the labels.  The labels returned will include a random MCS String, that is
 // guaranteed to be unique.
-func InitLabels(options []string) (plabel string, mlabel string, Err error) {
+func InitLabels(options []string) (plabel string, mlabel string, retErr error) {
 	if !selinux.GetEnabled() {
 		return "", "", nil
 	}
 	processLabel, mountLabel := selinux.ContainerLabels()
 	if processLabel != "" {
 		defer func() {
-			if Err != nil {
+			if retErr != nil {
 				selinux.ReleaseLabel(mountLabel)
 			}
 		}()
@@ -57,7 +57,6 @@
 			con := strings.SplitN(opt, ":", 2)
 			if !validOptions[con[0]] {
 				return "", "", errors.Errorf("Bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0])
-
 			}
 			if con[0] == "filetype" {
 				mcon["type"] = con[1]
diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
index 50760dc..d911990 100644
--- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
@@ -30,6 +30,11 @@
 	// ErrLevelSyntax is returned when a sensitivity or category do not have correct syntax in a level
 	ErrLevelSyntax = errors.New("invalid level syntax")
 
+	// ErrContextMissing is returned if a requested context is not found in a file.
+	ErrContextMissing = errors.New("context does not have a match")
+	// ErrVerifierNil is returned when a context verifier function is nil.
+	ErrVerifierNil = errors.New("verifier function is nil")
+
 	// CategoryRange allows the upper bound on the category range to be adjusted
 	CategoryRange = DefaultCategoryRange
 )
@@ -63,8 +68,12 @@
 	return fileLabel(fpath)
 }
 
-// SetFSCreateLabel tells kernel the label to create all file system objects
-// created by this task. Setting label="" to return to default.
+// SetFSCreateLabel tells the kernel what label to use for all file system objects
+// created by this task.
+// Set the label to an empty string to return to the default label. Calls to SetFSCreateLabel
+// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until file system
+// objects created by this task are finished to guarantee another goroutine does not migrate
+// to the current thread before execution is complete.
 func SetFSCreateLabel(label string) error {
 	return setFSCreateLabel(label)
 }
@@ -113,19 +122,27 @@
 }
 
 // SetExecLabel sets the SELinux label that the kernel will use for any programs
-// that are executed by the current process thread, or an error.
+// that are executed by the current process thread, or an error. Calls to SetExecLabel
+// should  be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until execution
+// of the program is finished to guarantee another goroutine does not migrate to the current
+// thread before execution is complete.
 func SetExecLabel(label string) error {
 	return setExecLabel(label)
 }
 
 // SetTaskLabel sets the SELinux label for the current thread, or an error.
-// This requires the dyntransition permission.
+// This requires the dyntransition permission. Calls to SetTaskLabel should
+// be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() to guarantee
+// the current thread does not run in a new mislabeled thread.
 func SetTaskLabel(label string) error {
 	return setTaskLabel(label)
 }
 
 // SetSocketLabel takes a process label and tells the kernel to assign the
-// label to the next socket that gets created
+// label to the next socket that gets created. Calls to SetSocketLabel
+// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until
+// the the socket is created to guarantee another goroutine does not migrate
+// to the current thread before execution is complete.
 func SetSocketLabel(label string) error {
 	return setSocketLabel(label)
 }
@@ -141,7 +158,10 @@
 }
 
 // SetKeyLabel takes a process label and tells the kernel to assign the
-// label to the next kernel keyring that gets created
+// label to the next kernel keyring that gets created. Calls to SetKeyLabel
+// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until
+// the kernel keyring is created to guarantee another goroutine does not migrate
+// to the current thread before execution is complete.
 func SetKeyLabel(label string) error {
 	return setKeyLabel(label)
 }
@@ -247,3 +267,12 @@
 func DisableSecOpt() []string {
 	return disableSecOpt()
 }
+
+// GetDefaultContextWithLevel gets a single context for the specified SELinux user
+// identity that is reachable from the specified scon context. The context is based
+// on the per-user /etc/selinux/{SELINUXTYPE}/contexts/users/<username> if it exists,
+// and falls back to the global /etc/selinux/{SELINUXTYPE}/contexts/default_contexts
+// file.
+func GetDefaultContextWithLevel(user, level, scon string) (string, error) {
+	return getDefaultContextWithLevel(user, level, scon)
+}
diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
index d6b0d49..904f5b0 100644
--- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
@@ -28,6 +28,8 @@
 	minSensLen       = 2
 	contextFile      = "/usr/share/containers/selinux/contexts"
 	selinuxDir       = "/etc/selinux/"
+	selinuxUsersDir  = "contexts/users"
+	defaultContexts  = "contexts/default_contexts"
 	selinuxConfig    = selinuxDir + "config"
 	selinuxfsMount   = "/sys/fs/selinux"
 	selinuxTypeTag   = "SELINUXTYPE"
@@ -35,6 +37,8 @@
 	xattrNameSelinux = "security.selinux"
 )
 
+var policyRoot = filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
+
 type selinuxState struct {
 	enabledSet    bool
 	enabled       bool
@@ -54,6 +58,13 @@
 	high *level
 }
 
+type defaultSECtx struct {
+	user, level, scon   string
+	userRdr, defaultRdr io.Reader
+
+	verifier func(string) error
+}
+
 type levelItem byte
 
 const (
@@ -111,7 +122,7 @@
 		if err == nil {
 			break
 		}
-		if err == unix.EAGAIN {
+		if err == unix.EAGAIN || err == unix.EINTR {
 			continue
 		}
 		return false
@@ -205,28 +216,16 @@
 }
 
 func readConfig(target string) string {
-	var (
-		val, key string
-		bufin    *bufio.Reader
-	)
-
 	in, err := os.Open(selinuxConfig)
 	if err != nil {
 		return ""
 	}
 	defer in.Close()
 
-	bufin = bufio.NewReader(in)
+	scanner := bufio.NewScanner(in)
 
-	for done := false; !done; {
-		var line string
-		if line, err = bufin.ReadString('\n'); err != nil {
-			if err != io.EOF {
-				return ""
-			}
-			done = true
-		}
-		line = strings.TrimSpace(line)
+	for scanner.Scan() {
+		line := strings.TrimSpace(scanner.Text())
 		if len(line) == 0 {
 			// Skip blank lines
 			continue
@@ -236,7 +235,7 @@
 			continue
 		}
 		if groups := assignRegex.FindStringSubmatch(line); groups != nil {
-			key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
+			key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
 			if key == target {
 				return strings.Trim(val, "\"")
 			}
@@ -245,15 +244,17 @@
 	return ""
 }
 
-func getSELinuxPolicyRoot() string {
-	return filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
-}
-
 func isProcHandle(fh *os.File) error {
 	var buf unix.Statfs_t
-	err := unix.Fstatfs(int(fh.Fd()), &buf)
-	if err != nil {
-		return errors.Wrapf(err, "statfs(%q) failed", fh.Name())
+
+	for {
+		err := unix.Fstatfs(int(fh.Fd()), &buf)
+		if err == nil {
+			break
+		}
+		if err != unix.EINTR {
+			return errors.Wrapf(err, "statfs(%q) failed", fh.Name())
+		}
 	}
 	if buf.Type != unix.PROC_SUPER_MAGIC {
 		return errors.Errorf("file %q is not on procfs", fh.Name())
@@ -307,9 +308,16 @@
 	if fpath == "" {
 		return ErrEmptyPath
 	}
-	if err := unix.Lsetxattr(fpath, xattrNameSelinux, []byte(label), 0); err != nil {
-		return errors.Wrapf(err, "failed to set file label on %s", fpath)
+	for {
+		err := unix.Lsetxattr(fpath, xattrNameSelinux, []byte(label), 0)
+		if err == nil {
+			break
+		}
+		if err != unix.EINTR {
+			return errors.Wrapf(err, "failed to set file label on %s", fpath)
+		}
 	}
+
 	return nil
 }
 
@@ -751,7 +759,7 @@
 	if len(label) != 0 {
 		con := strings.SplitN(label, ":", 4)
 		if len(con) > 3 {
-			mcsAdd(con[3])
+			_ = mcsAdd(con[3])
 		}
 	}
 }
@@ -828,11 +836,11 @@
 	}
 
 	for ORD > TIER {
-		ORD = ORD - TIER
+		ORD -= TIER
 		TIER--
 	}
 	TIER = SETSIZE - TIER
-	ORD = ORD + TIER
+	ORD += TIER
 	return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
 }
 
@@ -844,16 +852,14 @@
 	)
 
 	for {
-		binary.Read(rand.Reader, binary.LittleEndian, &n)
+		_ = binary.Read(rand.Reader, binary.LittleEndian, &n)
 		c1 = n % catRange
-		binary.Read(rand.Reader, binary.LittleEndian, &n)
+		_ = binary.Read(rand.Reader, binary.LittleEndian, &n)
 		c2 = n % catRange
 		if c1 == c2 {
 			continue
-		} else {
-			if c1 > c2 {
-				c1, c2 = c2, c1
-			}
+		} else if c1 > c2 {
+			c1, c2 = c2, c1
 		}
 		mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
 		if err := mcsAdd(mcs); err != nil {
@@ -884,18 +890,13 @@
 	if f, err := os.Open(contextFile); err == nil {
 		return f, nil
 	}
-	lxcPath := filepath.Join(getSELinuxPolicyRoot(), "/contexts/lxc_contexts")
+	lxcPath := filepath.Join(policyRoot, "/contexts/lxc_contexts")
 	return os.Open(lxcPath)
 }
 
 var labels = loadLabels()
 
 func loadLabels() map[string]string {
-	var (
-		val, key string
-		bufin    *bufio.Reader
-	)
-
 	labels := make(map[string]string)
 	in, err := openContextFile()
 	if err != nil {
@@ -903,18 +904,10 @@
 	}
 	defer in.Close()
 
-	bufin = bufio.NewReader(in)
+	scanner := bufio.NewScanner(in)
 
-	for done := false; !done; {
-		var line string
-		if line, err = bufin.ReadString('\n'); err != nil {
-			if err == io.EOF {
-				done = true
-			} else {
-				break
-			}
-		}
-		line = strings.TrimSpace(line)
+	for scanner.Scan() {
+		line := strings.TrimSpace(scanner.Text())
 		if len(line) == 0 {
 			// Skip blank lines
 			continue
@@ -924,7 +917,7 @@
 			continue
 		}
 		if groups := assignRegex.FindStringSubmatch(line); groups != nil {
-			key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
+			key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
 			labels[key] = strings.Trim(val, "\"")
 		}
 	}
@@ -1015,7 +1008,7 @@
 		return "", err
 	}
 	mcsDelete(tcon["level"])
-	mcsAdd(scon["level"])
+	_ = mcsAdd(scon["level"])
 	tcon["level"] = scon["level"]
 	return tcon.Get(), nil
 }
@@ -1095,3 +1088,124 @@
 func disableSecOpt() []string {
 	return []string{"disable"}
 }
+
+// findUserInContext scans the reader for a valid SELinux context
+// match that is verified with the verifier. Invalid contexts are
+// skipped. It returns a matched context or an empty string if no
+// match is found. If a scanner error occurs, it is returned.
+func findUserInContext(context Context, r io.Reader, verifier func(string) error) (string, error) {
+	fromRole := context["role"]
+	fromType := context["type"]
+	scanner := bufio.NewScanner(r)
+
+	for scanner.Scan() {
+		fromConns := strings.Fields(scanner.Text())
+		if len(fromConns) == 0 {
+			// Skip blank lines
+			continue
+		}
+
+		line := fromConns[0]
+
+		if line[0] == ';' || line[0] == '#' {
+			// Skip comments
+			continue
+		}
+
+		// user context files contexts are formatted as
+		// role_r:type_t:s0 where the user is missing.
+		lineArr := strings.SplitN(line, ":", 4)
+		// skip context with typo, or role and type do not match
+		if len(lineArr) != 3 ||
+			lineArr[0] != fromRole ||
+			lineArr[1] != fromType {
+			continue
+		}
+
+		for _, cc := range fromConns[1:] {
+			toConns := strings.SplitN(cc, ":", 4)
+			if len(toConns) != 3 {
+				continue
+			}
+
+			context["role"] = toConns[0]
+			context["type"] = toConns[1]
+
+			outConn := context.get()
+			if err := verifier(outConn); err != nil {
+				continue
+			}
+
+			return outConn, nil
+		}
+	}
+
+	if err := scanner.Err(); err != nil {
+		return "", errors.Wrap(err, "failed to scan for context")
+	}
+
+	return "", nil
+}
+
+func getDefaultContextFromReaders(c *defaultSECtx) (string, error) {
+	if c.verifier == nil {
+		return "", ErrVerifierNil
+	}
+
+	context, err := newContext(c.scon)
+	if err != nil {
+		return "", errors.Wrapf(err, "failed to create label for %s", c.scon)
+	}
+
+	// set so the verifier validates the matched context with the provided user and level.
+	context["user"] = c.user
+	context["level"] = c.level
+
+	conn, err := findUserInContext(context, c.userRdr, c.verifier)
+	if err != nil {
+		return "", err
+	}
+
+	if conn != "" {
+		return conn, nil
+	}
+
+	conn, err = findUserInContext(context, c.defaultRdr, c.verifier)
+	if err != nil {
+		return "", err
+	}
+
+	if conn != "" {
+		return conn, nil
+	}
+
+	return "", errors.Wrapf(ErrContextMissing, "context not found: %q", c.scon)
+}
+
+func getDefaultContextWithLevel(user, level, scon string) (string, error) {
+	userPath := filepath.Join(policyRoot, selinuxUsersDir, user)
+	defaultPath := filepath.Join(policyRoot, defaultContexts)
+
+	fu, err := os.Open(userPath)
+	if err != nil {
+		return "", err
+	}
+	defer fu.Close()
+
+	fd, err := os.Open(defaultPath)
+	if err != nil {
+		return "", err
+	}
+	defer fd.Close()
+
+	c := defaultSECtx{
+		user:       user,
+		level:      level,
+		scon:       scon,
+		userRdr:    fu,
+		defaultRdr: fd,
+		verifier:   securityCheckContext,
+	}
+
+	return getDefaultContextFromReaders(&c)
+}
diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go
index c526b21..e4b65c9 100644
--- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go
@@ -146,3 +146,7 @@
 func disableSecOpt() []string {
 	return []string{"disable"}
 }
+
+func getDefaultContextWithLevel(user, level, scon string) (string, error) {
+	return "", nil
+}
diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go
index de5c80e..2365b4b 100644
--- a/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go
@@ -6,21 +6,21 @@
 	"golang.org/x/sys/unix"
 )
 
-// Returns a []byte slice if the xattr is set and nil otherwise
-// Requires path and its attribute as arguments
-func lgetxattr(path string, attr string) ([]byte, error) {
+// lgetxattr returns a []byte slice containing the value of
+// an extended attribute attr set for path.
+func lgetxattr(path, attr string) ([]byte, error) {
 	// Start with a 128 length byte array
 	dest := make([]byte, 128)
-	sz, errno := unix.Lgetxattr(path, attr, dest)
+	sz, errno := doLgetxattr(path, attr, dest)
 	for errno == unix.ERANGE {
 		// Buffer too small, use zero-sized buffer to get the actual size
-		sz, errno = unix.Lgetxattr(path, attr, []byte{})
+		sz, errno = doLgetxattr(path, attr, []byte{})
 		if errno != nil {
 			return nil, errno
 		}
 
 		dest = make([]byte, sz)
-		sz, errno = unix.Lgetxattr(path, attr, dest)
+		sz, errno = doLgetxattr(path, attr, dest)
 	}
 	if errno != nil {
 		return nil, errno
@@ -28,3 +28,13 @@
 
 	return dest[:sz], nil
 }
+
+// doLgetxattr is a wrapper that retries on EINTR
+func doLgetxattr(path, attr string, dest []byte) (int, error) {
+	for {
+		sz, err := unix.Lgetxattr(path, attr, dest)
+		if err != unix.EINTR {
+			return sz, err
+		}
+	}
+}
diff --git a/vendor/github.com/opencontainers/selinux/go.mod b/vendor/github.com/opencontainers/selinux/go.mod
index 7bda067..18df624 100644
--- a/vendor/github.com/opencontainers/selinux/go.mod
+++ b/vendor/github.com/opencontainers/selinux/go.mod
@@ -4,6 +4,6 @@
 
 require (
 	github.com/pkg/errors v0.9.1
-	github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243
+	github.com/willf/bitset v1.1.11
 	golang.org/x/sys v0.0.0-20191115151921-52ab43148777
 )
diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go
index 63fde18..437b12b 100644
--- a/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go
+++ b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go
@@ -20,17 +20,16 @@
 //
 // Note that this implementation only supports primitive error handling:
 //
-// * no errors are ever passed to WalkFn
+// - no errors are ever passed to WalkFn;
 //
-// * once a walkFn returns any error, all further processing stops
-//   and the error is returned to the caller of Walk;
+// - once a walkFn returns any error, all further processing stops
+// and the error is returned to the caller of Walk;
 //
-// * filepath.SkipDir is not supported;
+// - filepath.SkipDir is not supported;
 //
-// * if more than one walkFn instance will return an error, only one
-//   of such errors will be propagated and returned by Walk, others
-//   will be silently discarded.
-//
+// - if more than one walkFn instance will return an error, only one
+// of such errors will be propagated and returned by Walk, others
+// will be silently discarded.
 func Walk(root string, walkFn WalkFunc) error {
 	return WalkN(root, walkFn, runtime.NumCPU()*2)
 }
@@ -38,6 +37,8 @@
 // WalkN is a wrapper for filepath.Walk which can call multiple walkFn
 // in parallel, allowing to handle each item concurrently. A maximum of
 // num walkFn will be called at any one time.
+//
+// Please see Walk documentation for caveats of using this function.
 func WalkN(root string, walkFn WalkFunc, num int) error {
 	// make sure limit is sensible
 	if num < 1 {