moby--moby/daemon/info.go

308 lines
9.8 KiB
Go
Raw Normal View History

package daemon // import "github.com/docker/docker/daemon"
import (
"fmt"
"os"
"runtime"
"strings"
"time"
"github.com/docker/docker/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cli/debug"
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/daemon/logger"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/pkg/fileutils"
"github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/docker/pkg/parsers/operatingsystem"
"github.com/docker/docker/pkg/platform"
"github.com/docker/docker/pkg/sysinfo"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/registry"
metrics "github.com/docker/go-metrics"
"github.com/opencontainers/selinux/go-selinux"
"github.com/sirupsen/logrus"
)
// SystemInfo returns information about the host server the daemon is running on.
func (daemon *Daemon) SystemInfo() *types.Info {
defer metrics.StartTimer(hostInfoFunctions.WithValues("system_info"))()
sysInfo := daemon.RawSysInfo()
v := &types.Info{
ID: daemon.id,
Images: daemon.imageService.CountImages(),
IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled,
BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled,
BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled,
Name: hostName(),
SystemTime: time.Now().Format(time.RFC3339Nano),
LoggingDriver: daemon.defaultLogConfig.Type,
KernelVersion: kernelVersion(),
OperatingSystem: operatingSystem(),
OSVersion: osVersion(),
IndexServerAddress: registry.IndexServer,
OSType: platform.OSType,
Architecture: platform.Architecture,
RegistryConfig: daemon.registryService.ServiceConfig(),
NCPU: sysinfo.NumCPU(),
MemTotal: memInfo().MemTotal,
GenericResources: daemon.genericResources,
DockerRootDir: daemon.configStore.Root,
Labels: daemon.configStore.Labels,
ExperimentalBuild: daemon.configStore.Experimental,
ServerVersion: dockerversion.Version,
Add http(s) proxy properties to daemon configuration This allows configuring the daemon's proxy server through the daemon.json con- figuration file or command-line flags configuration file, in addition to the existing option (through environment variables). Configuring environment variables on Windows to configure a service is more complicated than on Linux, and adding alternatives for this to the daemon con- figuration makes the configuration more transparent and easier to use. The configuration as set through command-line flags or through the daemon.json configuration file takes precedence over env-vars in the daemon's environment, which allows the daemon to use a different proxy. If both command-line flags and a daemon.json configuration option is set, an error is produced when starting the daemon. Note that this configuration is not "live reloadable" due to Golang's use of `sync.Once()` for proxy configuration, which means that changing the proxy configuration requires a restart of the daemon (reload / SIGHUP will not update the configuration. With this patch: cat /etc/docker/daemon.json { "http-proxy": "http://proxytest.example.com:80", "https-proxy": "https://proxytest.example.com:443" } docker pull busybox Using default tag: latest Error response from daemon: Get "https://registry-1.docker.io/v2/": proxyconnect tcp: dial tcp: lookup proxytest.example.com on 127.0.0.11:53: no such host docker build . Sending build context to Docker daemon 89.28MB Step 1/3 : FROM golang:1.16-alpine AS base Get "https://registry-1.docker.io/v2/": proxyconnect tcp: dial tcp: lookup proxytest.example.com on 127.0.0.11:53: no such host Integration tests were added to test the behavior: - verify that the configuration through all means are used (env-var, command-line flags, damon.json), and used in the expected order of preference. - verify that conflicting options produce an error. Signed-off-by: Anca Iordache <anca.iordache@docker.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-07-16 07:33:00 +00:00
HTTPProxy: config.MaskCredentials(getConfigOrEnv(daemon.configStore.HTTPProxy, "HTTP_PROXY", "http_proxy")),
HTTPSProxy: config.MaskCredentials(getConfigOrEnv(daemon.configStore.HTTPSProxy, "HTTPS_PROXY", "https_proxy")),
NoProxy: getConfigOrEnv(daemon.configStore.NoProxy, "NO_PROXY", "no_proxy"),
LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled,
Isolation: daemon.defaultIsolation,
}
daemon.fillContainerStates(v)
daemon.fillDebugInfo(v)
daemon.fillAPIInfo(v)
// Retrieve platform specific info
daemon.fillPlatformInfo(v, sysInfo)
daemon.fillDriverInfo(v)
daemon.fillPluginsInfo(v)
daemon.fillSecurityOptions(v, sysInfo)
daemon.fillLicense(v)
daemon.fillDefaultAddressPools(v)
return v
}
// SystemVersion returns version information about the daemon.
func (daemon *Daemon) SystemVersion() types.Version {
defer metrics.StartTimer(hostInfoFunctions.WithValues("system_version"))()
kernelVersion := kernelVersion()
v := types.Version{
Components: []types.ComponentVersion{
{
Name: "Engine",
Version: dockerversion.Version,
Details: map[string]string{
"GitCommit": dockerversion.GitCommit,
"ApiVersion": api.DefaultVersion,
"MinAPIVersion": api.MinVersion,
"GoVersion": runtime.Version(),
"Os": runtime.GOOS,
"Arch": runtime.GOARCH,
"BuildTime": dockerversion.BuildTime,
"KernelVersion": kernelVersion,
"Experimental": fmt.Sprintf("%t", daemon.configStore.Experimental),
},
},
},
// Populate deprecated fields for older clients
Version: dockerversion.Version,
GitCommit: dockerversion.GitCommit,
APIVersion: api.DefaultVersion,
MinAPIVersion: api.MinVersion,
GoVersion: runtime.Version(),
Os: runtime.GOOS,
Arch: runtime.GOARCH,
BuildTime: dockerversion.BuildTime,
KernelVersion: kernelVersion,
Experimental: daemon.configStore.Experimental,
}
v.Platform.Name = dockerversion.PlatformName
Add containerd, runc, and docker-init versions to /version This patch adds version information about the containerd, runc, and docker-init components to the /version endpoint. With this patch applied, running: ``` curl --unix-socket /var/run/docker.sock http://localhost/version | jq . ``` Will produce this response: ```json { "Platform": { "Name": "" }, "Components": [ { "Name": "Engine", "Version": "dev", "Details": { "ApiVersion": "1.40", "Arch": "amd64", "BuildTime": "2018-11-08T10:23:42.000000000+00:00", "Experimental": "false", "GitCommit": "7d02782d2f", "GoVersion": "go1.11.2", "KernelVersion": "4.9.93-linuxkit-aufs", "MinAPIVersion": "1.12", "Os": "linux" } }, { "Name": "containerd", "Version": "v1.1.4", "Details": { "GitCommit": "9f2e07b1fc1342d1c48fe4d7bbb94cb6d1bf278b" } }, { "Name": "runc", "Version": "1.0.0-rc5+dev", "Details": { "GitCommit": "a00bf0190895aa465a5fbed0268888e2c8ddfe85" } }, { "Name": "docker-init", "Version": "0.18.0", "Details": { "GitCommit": "fec3683" } } ], "Version": "dev", "ApiVersion": "1.40", "MinAPIVersion": "1.12", "GitCommit": "7d02782d2f", "GoVersion": "go1.11.2", "Os": "linux", "Arch": "amd64", "KernelVersion": "4.9.93-linuxkit-aufs", "BuildTime": "2018-11-08T10:23:42.000000000+00:00" } ``` When using a recent version of the CLI, that information is included in the output of `docker version`: ``` Client: Docker Engine - Community Version: 18.09.0 API version: 1.39 Go version: go1.10.4 Git commit: 4d60db4 Built: Wed Nov 7 00:46:51 2018 OS/Arch: linux/amd64 Experimental: false Server: Engine: Version: dev API version: 1.40 (minimum version 1.12) Go version: go1.11.2 Git commit: 7d02782d2f Built: Thu Nov 8 10:23:42 2018 OS/Arch: linux/amd64 Experimental: false containerd: Version: v1.1.4 GitCommit: 9f2e07b1fc1342d1c48fe4d7bbb94cb6d1bf278b runc: Version: 1.0.0-rc5+dev GitCommit: a00bf0190895aa465a5fbed0268888e2c8ddfe85 docker-init: Version: 0.18.0 GitCommit: fec3683 ``` Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-10-05 10:30:10 +00:00
daemon.fillPlatformVersion(&v)
return v
}
func (daemon *Daemon) fillDriverInfo(v *types.Info) {
daemon: require storage-driver to be set if the driver is deprecated Previously, we only printed a warning if a storage driver was deprecated. The intent was to continue supporting these drivers, to allow users to migrate to a different storage driver. This patch changes the behavior; if the user has no storage driver specified in the daemon configuration (so if we try to detect the previous storage driver based on what's present in /var/lib/docker), we now produce an error, informing the user that the storage driver is deprecated (and to be removed), as well as instructing them to change the daemon configuration to explicitly select the storage driver (to allow them to migrate). This should make the deprecation more visible; this will be disruptive, but it's better to have the failure happening *now* (while the drivers are still there), than for users to discover the storage driver is no longer there (which would require them to *downgrade* the daemon in order to migrate to a different driver). With this change, `docker info` includes a link in the warnings that: / # docker info Client: Context: default Debug Mode: false Server: ... Live Restore Enabled: false WARNING: The overlay storage-driver is deprecated, and will be removed in a future release. Refer to the documentation for more information: https://docs.docker.com/go/storage-driver/ When starting the daemon without a storage driver configured explicitly, but previous state was using a deprecated driver, the error is both logged and printed: ... ERRO[2022-03-25T14:14:06.032014013Z] [graphdriver] prior storage driver overlay is deprecated and will be removed in a future release; update the the daemon configuration and explicitly choose this storage driver to continue using it; visit https://docs.docker.com/go/storage-driver/ for more information ... failed to start daemon: error initializing graphdriver: prior storage driver overlay is deprecated and will be removed in a future release; update the the daemon configuration and explicitly choose this storage driver to continue using it; visit https://docs.docker.com/go/storage-driver/ for more information When starting the daemon and explicitly configuring it with a deprecated storage driver: WARN[2022-03-25T14:15:59.042335412Z] [graphdriver] WARNING: the overlay storage-driver is deprecated and will be removed in a future release; visit https://docs.docker.com/go/storage-driver/ for more information Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-11 16:51:12 +00:00
const warnMsg = `
WARNING: The %s storage-driver is deprecated, and will be removed in a future release.
Refer to the documentation for more information: https://docs.docker.com/go/storage-driver/`
switch daemon.graphDriver {
case "aufs", "devicemapper", "overlay":
daemon: require storage-driver to be set if the driver is deprecated Previously, we only printed a warning if a storage driver was deprecated. The intent was to continue supporting these drivers, to allow users to migrate to a different storage driver. This patch changes the behavior; if the user has no storage driver specified in the daemon configuration (so if we try to detect the previous storage driver based on what's present in /var/lib/docker), we now produce an error, informing the user that the storage driver is deprecated (and to be removed), as well as instructing them to change the daemon configuration to explicitly select the storage driver (to allow them to migrate). This should make the deprecation more visible; this will be disruptive, but it's better to have the failure happening *now* (while the drivers are still there), than for users to discover the storage driver is no longer there (which would require them to *downgrade* the daemon in order to migrate to a different driver). With this change, `docker info` includes a link in the warnings that: / # docker info Client: Context: default Debug Mode: false Server: ... Live Restore Enabled: false WARNING: The overlay storage-driver is deprecated, and will be removed in a future release. Refer to the documentation for more information: https://docs.docker.com/go/storage-driver/ When starting the daemon without a storage driver configured explicitly, but previous state was using a deprecated driver, the error is both logged and printed: ... ERRO[2022-03-25T14:14:06.032014013Z] [graphdriver] prior storage driver overlay is deprecated and will be removed in a future release; update the the daemon configuration and explicitly choose this storage driver to continue using it; visit https://docs.docker.com/go/storage-driver/ for more information ... failed to start daemon: error initializing graphdriver: prior storage driver overlay is deprecated and will be removed in a future release; update the the daemon configuration and explicitly choose this storage driver to continue using it; visit https://docs.docker.com/go/storage-driver/ for more information When starting the daemon and explicitly configuring it with a deprecated storage driver: WARN[2022-03-25T14:15:59.042335412Z] [graphdriver] WARNING: the overlay storage-driver is deprecated and will be removed in a future release; visit https://docs.docker.com/go/storage-driver/ for more information Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-11 16:51:12 +00:00
v.Warnings = append(v.Warnings, fmt.Sprintf(warnMsg, daemon.graphDriver))
}
v.Driver = daemon.graphDriver
v.DriverStatus = daemon.imageService.LayerStoreStatus()
Add "Warnings" to /info endpoint, and move detection to the daemon When requesting information about the daemon's configuration through the `/info` endpoint, missing features (or non-recommended settings) may have to be presented to the user. Detecting these situations, and printing warnings currently is handled by the cli, which results in some complications: - duplicated effort: each client has to re-implement detection and warnings. - it's not possible to generate warnings for reasons outside of the information returned in the `/info` response. - cli-side detection has to be updated for new conditions. This means that an older cli connecting to a new daemon may not print all warnings (due to it not detecting the new conditions) - some warnings (in particular, warnings about storage-drivers) depend on driver-status (`DriverStatus`) information. The format of the information returned in this field is not part of the API specification and can change over time, resulting in cli-side detection no longer being functional. This patch adds a new `Warnings` field to the `/info` response. This field is to return warnings to be presented by the user. Existing warnings that are currently handled by the CLI are copied to the daemon as part of this patch; This change is backward-compatible with existing clients; old client can continue to use the client-side warnings, whereas new clients can skip client-side detection, and print warnings that are returned by the daemon. Example response with this patch applied; ```bash curl --unix-socket /var/run/docker.sock http://localhost/info | jq .Warnings ``` ```json [ "WARNING: bridge-nf-call-iptables is disabled", "WARNING: bridge-nf-call-ip6tables is disabled" ] ``` Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-19 11:45:32 +00:00
fillDriverWarnings(v)
}
func (daemon *Daemon) fillPluginsInfo(v *types.Info) {
v.Plugins = types.PluginsInfo{
Volume: daemon.volumes.GetDriverList(),
Network: daemon.GetNetworkDriverList(),
// The authorization plugins are returned in the order they are
// used as they constitute a request/response modification chain.
Authorization: daemon.configStore.AuthorizationPlugins,
Log: logger.ListDrivers(),
}
}
func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInfo) {
var securityOptions []string
if sysInfo.AppArmor {
securityOptions = append(securityOptions, "name=apparmor")
}
if sysInfo.Seccomp && supportsSeccomp {
if daemon.seccompProfilePath != config.SeccompProfileDefault {
v.Warnings = append(v.Warnings, "WARNING: daemon is not using the default seccomp profile")
}
securityOptions = append(securityOptions, "name=seccomp,profile="+daemon.seccompProfilePath)
}
if selinux.GetEnabled() {
securityOptions = append(securityOptions, "name=selinux")
}
if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 {
securityOptions = append(securityOptions, "name=userns")
}
if daemon.Rootless() {
securityOptions = append(securityOptions, "name=rootless")
}
if daemon.cgroupNamespacesEnabled(sysInfo) {
securityOptions = append(securityOptions, "name=cgroupns")
}
v.SecurityOptions = securityOptions
}
func (daemon *Daemon) fillContainerStates(v *types.Info) {
cRunning, cPaused, cStopped := stateCtr.get()
v.Containers = cRunning + cPaused + cStopped
v.ContainersPaused = cPaused
v.ContainersRunning = cRunning
v.ContainersStopped = cStopped
}
// fillDebugInfo sets the current debugging state of the daemon, and additional
// debugging information, such as the number of Go-routines, and file descriptors.
//
// Note that this currently always collects the information, but the CLI only
// prints it if the daemon has debug enabled. We should consider to either make
// this information optional (cli to request "with debugging information"), or
// only collect it if the daemon has debug enabled. For the CLI code, see
// https://github.com/docker/cli/blob/v20.10.12/cli/command/system/info.go#L239-L244
func (daemon *Daemon) fillDebugInfo(v *types.Info) {
v.Debug = debug.IsEnabled()
v.NFd = fileutils.GetTotalUsedFds()
v.NGoroutines = runtime.NumGoroutine()
v.NEventsListener = daemon.EventsService.SubscribersCount()
}
func (daemon *Daemon) fillAPIInfo(v *types.Info) {
const warn string = `
Access to the remote API is equivalent to root access on the host. Refer
to the 'Docker daemon attack surface' section in the documentation for
more information: https://docs.docker.com/go/attack-surface/`
cfg := daemon.configStore
for _, host := range cfg.Hosts {
// cnf.Hosts is normalized during startup, so should always have a scheme/proto
h := strings.SplitN(host, "://", 2)
proto := h[0]
addr := h[1]
if proto != "tcp" {
continue
}
if cfg.TLS == nil || !*cfg.TLS {
v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: API is accessible on http://%s without encryption.%s", addr, warn))
continue
}
if cfg.TLSVerify == nil || !*cfg.TLSVerify {
v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: API is accessible on https://%s without TLS client verification.%s", addr, warn))
continue
}
}
}
func (daemon *Daemon) fillDefaultAddressPools(v *types.Info) {
for _, pool := range daemon.configStore.DefaultAddressPools.Value() {
v.DefaultAddressPools = append(v.DefaultAddressPools, types.NetworkAddressPool{
Base: pool.Base,
Size: pool.Size,
})
}
}
func hostName() string {
hostname := ""
if hn, err := os.Hostname(); err != nil {
logrus.Warnf("Could not get hostname: %v", err)
} else {
hostname = hn
}
return hostname
}
func kernelVersion() string {
var kernelVersion string
if kv, err := kernel.GetKernelVersion(); err != nil {
logrus.Warnf("Could not get kernel version: %v", err)
} else {
kernelVersion = kv.String()
}
return kernelVersion
}
func memInfo() *system.MemInfo {
memInfo, err := system.ReadMemInfo()
if err != nil {
logrus.Errorf("Could not read system memory info: %v", err)
memInfo = &system.MemInfo{}
}
return memInfo
}
func operatingSystem() (operatingSystem string) {
defer metrics.StartTimer(hostInfoFunctions.WithValues("operating_system"))()
if s, err := operatingsystem.GetOperatingSystem(); err != nil {
logrus.Warnf("Could not get operating system name: %v", err)
} else {
operatingSystem = s
}
if inContainer, err := operatingsystem.IsContainerized(); err != nil {
logrus.Errorf("Could not determine if daemon is containerized: %v", err)
operatingSystem += " (error determining if containerized)"
} else if inContainer {
operatingSystem += " (containerized)"
}
return operatingSystem
}
func osVersion() (version string) {
defer metrics.StartTimer(hostInfoFunctions.WithValues("os_version"))()
version, err := operatingsystem.GetOperatingSystemVersion()
if err != nil {
logrus.Warnf("Could not get operating system version: %v", err)
}
return version
}
func getEnvAny(names ...string) string {
for _, n := range names {
if val := os.Getenv(n); val != "" {
return val
}
}
return ""
}
Add http(s) proxy properties to daemon configuration This allows configuring the daemon's proxy server through the daemon.json con- figuration file or command-line flags configuration file, in addition to the existing option (through environment variables). Configuring environment variables on Windows to configure a service is more complicated than on Linux, and adding alternatives for this to the daemon con- figuration makes the configuration more transparent and easier to use. The configuration as set through command-line flags or through the daemon.json configuration file takes precedence over env-vars in the daemon's environment, which allows the daemon to use a different proxy. If both command-line flags and a daemon.json configuration option is set, an error is produced when starting the daemon. Note that this configuration is not "live reloadable" due to Golang's use of `sync.Once()` for proxy configuration, which means that changing the proxy configuration requires a restart of the daemon (reload / SIGHUP will not update the configuration. With this patch: cat /etc/docker/daemon.json { "http-proxy": "http://proxytest.example.com:80", "https-proxy": "https://proxytest.example.com:443" } docker pull busybox Using default tag: latest Error response from daemon: Get "https://registry-1.docker.io/v2/": proxyconnect tcp: dial tcp: lookup proxytest.example.com on 127.0.0.11:53: no such host docker build . Sending build context to Docker daemon 89.28MB Step 1/3 : FROM golang:1.16-alpine AS base Get "https://registry-1.docker.io/v2/": proxyconnect tcp: dial tcp: lookup proxytest.example.com on 127.0.0.11:53: no such host Integration tests were added to test the behavior: - verify that the configuration through all means are used (env-var, command-line flags, damon.json), and used in the expected order of preference. - verify that conflicting options produce an error. Signed-off-by: Anca Iordache <anca.iordache@docker.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-07-16 07:33:00 +00:00
func getConfigOrEnv(config string, env ...string) string {
if config != "" {
return config
}
return getEnvAny(env...)
}