Prefer loading docker-init from an appropriate "libexec" directory

The `docker-init` binary is not intended to be a user-facing command, and as such it is more appropriate for it to be found in `/usr/libexec` (or similar) than in `PATH` (see the FHS, especially https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s07.html and https://refspecs.linuxfoundation.org/FHS_2.3/fhs-2.3.html#USRLIBLIBRARIESFORPROGRAMMINGANDPA).

This adjusts the logic for using that configuration option to take this into account and appropriately search for `docker-init` (or the user's configured alternative) in these directories before falling back to the existing `PATH` lookup behavior.

This behavior _used_ to exist for the old `dockerinit` binary (of a similar name and used in a similar way but for an alternative purpose), but that behavior was removed in 4357ed4a73 when that older `dockerinit` was also removed.

Most of this reasoning _also_ applies to `docker-proxy` (and various `containerd-xxx` binaries such as the shims), but this change does not affect those.  It would be relatively straightforward to adapt `LookupInitPath` to be a more generic function such as `libexecLookupPath` or similar if we wanted to explore that.

See 14482589df/cli-plugins/manager/manager_unix.go for the related path list in the CLI which loads CLI plugins from a similar set of paths (with a similar rationale - plugin binaries are not typically intended to be run directly by users but rather invoked _via_ the CLI binary).

Signed-off-by: Tianon Gravi <admwiggin@gmail.com>
This commit is contained in:
Tianon Gravi 2023-03-22 13:26:43 -07:00
parent 1855a55d8c
commit 6caaa8cadc
3 changed files with 43 additions and 17 deletions

View file

@ -118,6 +118,34 @@ func (conf *Config) GetInitPath() string {
return DefaultInitBinary
}
// LookupInitPath returns an absolute path to the "docker-init" binary by searching relevant "libexec" directories (per FHS 3.0 & 2.3) followed by PATH
func (conf *Config) LookupInitPath() (string, error) {
binary := conf.GetInitPath()
if filepath.IsAbs(binary) {
return binary, nil
}
for _, dir := range []string{
// FHS 3.0: "/usr/libexec includes internal binaries that are not intended to be executed directly by users or shell scripts. Applications may use a single subdirectory under /usr/libexec."
// https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s07.html
"/usr/local/libexec/docker",
"/usr/libexec/docker",
// FHS 2.3: "/usr/lib includes object files, libraries, and internal binaries that are not intended to be executed directly by users or shell scripts."
// https://refspecs.linuxfoundation.org/FHS_2.3/fhs-2.3.html#USRLIBLIBRARIESFORPROGRAMMINGANDPA
"/usr/local/lib/docker",
"/usr/lib/docker",
} {
// exec.LookPath has a fast-path short-circuit for paths that contain "/" (skipping the PATH lookup) that then verifies whether the given path is likely to be an actual executable binary (so we invoke that instead of reimplementing the same checks)
if file, err := exec.LookPath(filepath.Join(dir, binary)); err == nil {
return file, nil
}
}
// if we checked all the "libexec" directories and found no matches, fall back to PATH
return exec.LookPath(binary)
}
// GetResolvConf returns the appropriate resolv.conf
// Check setupResolvConf on how this is selected
func (conf *Config) GetResolvConf() string {

View file

@ -40,7 +40,6 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
}
v.Runtimes = daemon.configStore.GetAllRuntimes()
v.DefaultRuntime = daemon.configStore.GetDefaultRuntimeName()
v.InitBinary = daemon.configStore.GetInitPath()
v.RuncCommit.ID = "N/A"
v.ContainerdCommit.ID = "N/A"
v.InitCommit.ID = "N/A"
@ -63,15 +62,17 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
logrus.Warnf("failed to retrieve containerd version: %v", err)
}
defaultInitBinary := daemon.configStore.GetInitPath()
if rv, err := exec.Command(defaultInitBinary, "--version").Output(); err == nil {
v.InitBinary = daemon.configStore.GetInitPath()
if initBinary, err := daemon.configStore.LookupInitPath(); err != nil {
logrus.Warnf("failed to find docker-init: %s", err)
} else if rv, err := exec.Command(initBinary, "--version").Output(); err == nil {
if _, commit, err := parseInitVersion(string(rv)); err != nil {
logrus.Warnf("failed to parse %s version: %s", defaultInitBinary, err)
logrus.Warnf("failed to parse %s version: %s", initBinary, err)
} else {
v.InitCommit.ID = commit
}
} else {
logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err)
logrus.Warnf("failed to retrieve %s version: %s", initBinary, err)
}
// Set expected and actual commits to the same value to prevent the client
@ -195,13 +196,14 @@ func (daemon *Daemon) fillPlatformVersion(v *types.Version) {
}
}
defaultInitBinary := daemon.configStore.GetInitPath()
if rv, err := exec.Command(defaultInitBinary, "--version").Output(); err == nil {
if initBinary, err := daemon.configStore.LookupInitPath(); err != nil {
logrus.Warnf("failed to find docker-init: %s", err)
} else if rv, err := exec.Command(initBinary, "--version").Output(); err == nil {
if ver, commit, err := parseInitVersion(string(rv)); err != nil {
logrus.Warnf("failed to parse %s version: %s", defaultInitBinary, err)
logrus.Warnf("failed to parse %s version: %s", initBinary, err)
} else {
v.Components = append(v.Components, types.ComponentVersion{
Name: filepath.Base(defaultInitBinary),
Name: filepath.Base(initBinary),
Version: ver,
Details: map[string]string{
"GitCommit": commit,
@ -209,7 +211,7 @@ func (daemon *Daemon) fillPlatformVersion(v *types.Version) {
})
}
} else {
logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err)
logrus.Warnf("failed to retrieve %s version: %s", initBinary, err)
}
daemon.fillRootlessVersion(v)

View file

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"sort"
"strconv"
@ -748,12 +747,9 @@ func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
if (c.HostConfig.Init != nil && *c.HostConfig.Init) ||
(c.HostConfig.Init == nil && daemon.configStore.Init) {
s.Process.Args = append([]string{inContainerInitPath, "--", c.Path}, c.Args...)
path := daemon.configStore.InitPath
if path == "" {
path, err = exec.LookPath(dconfig.DefaultInitBinary)
if err != nil {
return err
}
path, err := daemon.configStore.LookupInitPath() // this will fall back to DefaultInitBinary and return an absolute path
if err != nil {
return err
}
s.Mounts = append(s.Mounts, specs.Mount{
Destination: inContainerInitPath,