From 8a7ccfb4b04c95b678f2cff0f15c9cca64a95f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Gro=C3=9Fmann?= Date: Thu, 25 Jun 2026 14:50:27 +0200 Subject: [PATCH] refactor(api): use abstract Unix socket for daemon on Linux Split the non-Windows DaemonSocketPath into a darwin-only filesystem path and a Linux implementation that returns an abstract Unix domain socket address (leading "@", which Go maps to a NUL byte). Abstract sockets live in the abstract namespace rather than on the filesystem, so there are no stale socket files, no directory permission concerns, and the socket is reclaimed automatically when the daemon exits. The address is namespaced by UID to avoid collisions between users. The plugin config test skips filesystem setup for abstract addresses. --- plugin/config_test.go | 10 ++++-- .../{defaults_unix.go => defaults_darwin.go} | 2 +- x/api/defaults_linux.go | 35 +++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) rename x/api/{defaults_unix.go => defaults_darwin.go} (97%) create mode 100644 x/api/defaults_linux.go diff --git a/plugin/config_test.go b/plugin/config_test.go index 0b1c6cdb..58b6cc3e 100644 --- a/plugin/config_test.go +++ b/plugin/config_test.go @@ -21,6 +21,7 @@ import ( "net/http" "os" "path/filepath" + "strings" "testing" nriNet "github.com/containerd/nri/pkg/net" @@ -58,8 +59,13 @@ func Test_newCfgForManualLaunch(t *testing.T) { os.Args = []string{"test-plugin"} t.Setenv("XDG_RUNTIME_DIR", os.TempDir()) socketPath := api.DaemonSocketPath() - os.Remove(socketPath) - require.NoError(t, os.MkdirAll(filepath.Dir(socketPath), 0o755)) + // Abstract sockets (leading "@", Linux) live in the abstract + // namespace, not on the filesystem, so they need no directory + // and leave nothing to clean up. + if !strings.HasPrefix(socketPath, "@") { + os.Remove(socketPath) + require.NoError(t, os.MkdirAll(filepath.Dir(socketPath), 0o755)) + } listener, err := net.Listen("unix", socketPath) if err != nil { t.Fatalf("listen failed: %v", err) diff --git a/x/api/defaults_unix.go b/x/api/defaults_darwin.go similarity index 97% rename from x/api/defaults_unix.go rename to x/api/defaults_darwin.go index e0727063..78fb0449 100644 --- a/x/api/defaults_unix.go +++ b/x/api/defaults_darwin.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !windows +//go:build darwin package api diff --git a/x/api/defaults_linux.go b/x/api/defaults_linux.go new file mode 100644 index 00000000..469fc23b --- /dev/null +++ b/x/api/defaults_linux.go @@ -0,0 +1,35 @@ +// Copyright 2025-2026 Docker, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build linux + +package api + +import ( + "fmt" + "os" +) + +// DaemonSocketPath returns the address of the daemon's listening socket. +// +// On Linux it is an abstract Unix domain socket: the address has a leading +// "@", which Go's net package maps to a NUL byte, placing the socket in the +// abstract namespace instead of on the filesystem. +// +// The address is namespaced by the user's UID so daemons run by different +// users on the same host do not collide (the abstract namespace is shared per +// network namespace, not per user as a filesystem path would be). +func DaemonSocketPath() string { + return fmt.Sprintf("@docker-secrets-engine/%d/daemon.sock", os.Getuid()) +}