info.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. package daemon // import "github.com/docker/docker/daemon"
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "runtime"
  7. "strings"
  8. "time"
  9. "github.com/containerd/containerd/tracing"
  10. "github.com/containerd/log"
  11. "github.com/docker/docker/api"
  12. "github.com/docker/docker/api/types"
  13. "github.com/docker/docker/api/types/system"
  14. "github.com/docker/docker/cli/debug"
  15. "github.com/docker/docker/daemon/config"
  16. "github.com/docker/docker/daemon/logger"
  17. "github.com/docker/docker/dockerversion"
  18. "github.com/docker/docker/pkg/fileutils"
  19. "github.com/docker/docker/pkg/meminfo"
  20. "github.com/docker/docker/pkg/parsers/kernel"
  21. "github.com/docker/docker/pkg/parsers/operatingsystem"
  22. "github.com/docker/docker/pkg/platform"
  23. "github.com/docker/docker/pkg/sysinfo"
  24. "github.com/docker/docker/registry"
  25. metrics "github.com/docker/go-metrics"
  26. "github.com/opencontainers/selinux/go-selinux"
  27. )
  28. func doWithTrace[T any](ctx context.Context, name string, f func() T) T {
  29. _, span := tracing.StartSpan(ctx, name)
  30. defer span.End()
  31. return f()
  32. }
  33. // SystemInfo returns information about the host server the daemon is running on.
  34. //
  35. // The only error this should return is due to context cancellation/deadline.
  36. // Anything else should be logged and ignored because this is looking up
  37. // multiple things and is often used for debugging.
  38. // The only case valid early return is when the caller doesn't want the result anymore (ie context cancelled).
  39. func (daemon *Daemon) SystemInfo(ctx context.Context) (*system.Info, error) {
  40. defer metrics.StartTimer(hostInfoFunctions.WithValues("system_info"))()
  41. sysInfo := daemon.RawSysInfo()
  42. cfg := daemon.config()
  43. v := &system.Info{
  44. ID: daemon.id,
  45. Images: daemon.imageService.CountImages(ctx),
  46. IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled,
  47. BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled,
  48. BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled,
  49. Name: hostName(ctx),
  50. SystemTime: time.Now().Format(time.RFC3339Nano),
  51. LoggingDriver: daemon.defaultLogConfig.Type,
  52. KernelVersion: kernelVersion(ctx),
  53. OperatingSystem: operatingSystem(ctx),
  54. OSVersion: osVersion(ctx),
  55. IndexServerAddress: registry.IndexServer,
  56. OSType: runtime.GOOS,
  57. Architecture: platform.Architecture,
  58. RegistryConfig: doWithTrace(ctx, "registry.ServiceConfig", daemon.registryService.ServiceConfig),
  59. NCPU: doWithTrace(ctx, "sysinfo.NumCPU", sysinfo.NumCPU),
  60. MemTotal: memInfo(ctx).MemTotal,
  61. GenericResources: daemon.genericResources,
  62. DockerRootDir: cfg.Root,
  63. Labels: cfg.Labels,
  64. ExperimentalBuild: cfg.Experimental,
  65. ServerVersion: dockerversion.Version,
  66. HTTPProxy: config.MaskCredentials(getConfigOrEnv(cfg.HTTPProxy, "HTTP_PROXY", "http_proxy")),
  67. HTTPSProxy: config.MaskCredentials(getConfigOrEnv(cfg.HTTPSProxy, "HTTPS_PROXY", "https_proxy")),
  68. NoProxy: getConfigOrEnv(cfg.NoProxy, "NO_PROXY", "no_proxy"),
  69. LiveRestoreEnabled: cfg.LiveRestoreEnabled,
  70. Isolation: daemon.defaultIsolation,
  71. CDISpecDirs: promoteNil(cfg.CDISpecDirs),
  72. }
  73. daemon.fillContainerStates(v)
  74. daemon.fillDebugInfo(ctx, v)
  75. daemon.fillAPIInfo(v, &cfg.Config)
  76. // Retrieve platform specific info
  77. if err := daemon.fillPlatformInfo(ctx, v, sysInfo, cfg); err != nil {
  78. return nil, err
  79. }
  80. daemon.fillDriverInfo(v)
  81. daemon.fillPluginsInfo(ctx, v, &cfg.Config)
  82. daemon.fillSecurityOptions(v, sysInfo, &cfg.Config)
  83. daemon.fillLicense(v)
  84. daemon.fillDefaultAddressPools(ctx, v, &cfg.Config)
  85. return v, nil
  86. }
  87. // SystemVersion returns version information about the daemon.
  88. //
  89. // The only error this should return is due to context cancellation/deadline.
  90. // Anything else should be logged and ignored because this is looking up
  91. // multiple things and is often used for debugging.
  92. // The only case valid early return is when the caller doesn't want the result anymore (ie context cancelled).
  93. func (daemon *Daemon) SystemVersion(ctx context.Context) (types.Version, error) {
  94. defer metrics.StartTimer(hostInfoFunctions.WithValues("system_version"))()
  95. kernelVersion := kernelVersion(ctx)
  96. cfg := daemon.config()
  97. v := types.Version{
  98. Components: []types.ComponentVersion{
  99. {
  100. Name: "Engine",
  101. Version: dockerversion.Version,
  102. Details: map[string]string{
  103. "GitCommit": dockerversion.GitCommit,
  104. "ApiVersion": api.DefaultVersion,
  105. "MinAPIVersion": cfg.MinAPIVersion,
  106. "GoVersion": runtime.Version(),
  107. "Os": runtime.GOOS,
  108. "Arch": runtime.GOARCH,
  109. "BuildTime": dockerversion.BuildTime,
  110. "KernelVersion": kernelVersion,
  111. "Experimental": fmt.Sprintf("%t", cfg.Experimental),
  112. },
  113. },
  114. },
  115. // Populate deprecated fields for older clients
  116. Version: dockerversion.Version,
  117. GitCommit: dockerversion.GitCommit,
  118. APIVersion: api.DefaultVersion,
  119. MinAPIVersion: cfg.MinAPIVersion,
  120. GoVersion: runtime.Version(),
  121. Os: runtime.GOOS,
  122. Arch: runtime.GOARCH,
  123. BuildTime: dockerversion.BuildTime,
  124. KernelVersion: kernelVersion,
  125. Experimental: cfg.Experimental,
  126. }
  127. v.Platform.Name = dockerversion.PlatformName
  128. if err := daemon.fillPlatformVersion(ctx, &v, cfg); err != nil {
  129. return v, err
  130. }
  131. return v, nil
  132. }
  133. func (daemon *Daemon) fillDriverInfo(v *system.Info) {
  134. v.Driver = daemon.imageService.StorageDriver()
  135. v.DriverStatus = daemon.imageService.LayerStoreStatus()
  136. const warnMsg = `
  137. WARNING: The %s storage-driver is deprecated, and will be removed in a future release.
  138. Refer to the documentation for more information: https://docs.docker.com/go/storage-driver/`
  139. switch v.Driver {
  140. case "overlay":
  141. v.Warnings = append(v.Warnings, fmt.Sprintf(warnMsg, v.Driver))
  142. }
  143. fillDriverWarnings(v)
  144. }
  145. func (daemon *Daemon) fillPluginsInfo(ctx context.Context, v *system.Info, cfg *config.Config) {
  146. v.Plugins = system.PluginsInfo{
  147. Volume: daemon.volumes.GetDriverList(),
  148. Network: daemon.GetNetworkDriverList(ctx),
  149. // The authorization plugins are returned in the order they are
  150. // used as they constitute a request/response modification chain.
  151. Authorization: cfg.AuthorizationPlugins,
  152. Log: logger.ListDrivers(),
  153. }
  154. }
  155. func (daemon *Daemon) fillSecurityOptions(v *system.Info, sysInfo *sysinfo.SysInfo, cfg *config.Config) {
  156. var securityOptions []string
  157. if sysInfo.AppArmor {
  158. securityOptions = append(securityOptions, "name=apparmor")
  159. }
  160. if sysInfo.Seccomp && supportsSeccomp {
  161. if daemon.seccompProfilePath != config.SeccompProfileDefault {
  162. v.Warnings = append(v.Warnings, "WARNING: daemon is not using the default seccomp profile")
  163. }
  164. securityOptions = append(securityOptions, "name=seccomp,profile="+daemon.seccompProfilePath)
  165. }
  166. if selinux.GetEnabled() {
  167. securityOptions = append(securityOptions, "name=selinux")
  168. }
  169. if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 {
  170. securityOptions = append(securityOptions, "name=userns")
  171. }
  172. if Rootless(cfg) {
  173. securityOptions = append(securityOptions, "name=rootless")
  174. }
  175. if cgroupNamespacesEnabled(sysInfo, cfg) {
  176. securityOptions = append(securityOptions, "name=cgroupns")
  177. }
  178. if noNewPrivileges(cfg) {
  179. securityOptions = append(securityOptions, "name=no-new-privileges")
  180. }
  181. v.SecurityOptions = securityOptions
  182. }
  183. func (daemon *Daemon) fillContainerStates(v *system.Info) {
  184. cRunning, cPaused, cStopped := stateCtr.get()
  185. v.Containers = cRunning + cPaused + cStopped
  186. v.ContainersPaused = cPaused
  187. v.ContainersRunning = cRunning
  188. v.ContainersStopped = cStopped
  189. }
  190. // fillDebugInfo sets the current debugging state of the daemon, and additional
  191. // debugging information, such as the number of Go-routines, and file descriptors.
  192. //
  193. // Note that this currently always collects the information, but the CLI only
  194. // prints it if the daemon has debug enabled. We should consider to either make
  195. // this information optional (cli to request "with debugging information"), or
  196. // only collect it if the daemon has debug enabled. For the CLI code, see
  197. // https://github.com/docker/cli/blob/v20.10.12/cli/command/system/info.go#L239-L244
  198. func (daemon *Daemon) fillDebugInfo(ctx context.Context, v *system.Info) {
  199. v.Debug = debug.IsEnabled()
  200. v.NFd = fileutils.GetTotalUsedFds(ctx)
  201. v.NGoroutines = runtime.NumGoroutine()
  202. v.NEventsListener = daemon.EventsService.SubscribersCount()
  203. }
  204. func (daemon *Daemon) fillAPIInfo(v *system.Info, cfg *config.Config) {
  205. const warn string = `
  206. Access to the remote API is equivalent to root access on the host. Refer
  207. to the 'Docker daemon attack surface' section in the documentation for
  208. more information: https://docs.docker.com/go/attack-surface/`
  209. for _, host := range cfg.Hosts {
  210. // cnf.Hosts is normalized during startup, so should always have a scheme/proto
  211. proto, addr, _ := strings.Cut(host, "://")
  212. if proto != "tcp" {
  213. continue
  214. }
  215. if cfg.TLS == nil || !*cfg.TLS {
  216. v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: API is accessible on http://%s without encryption.%s", addr, warn))
  217. continue
  218. }
  219. if cfg.TLSVerify == nil || !*cfg.TLSVerify {
  220. v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: API is accessible on https://%s without TLS client verification.%s", addr, warn))
  221. continue
  222. }
  223. }
  224. }
  225. func (daemon *Daemon) fillDefaultAddressPools(ctx context.Context, v *system.Info, cfg *config.Config) {
  226. _, span := tracing.StartSpan(ctx, "fillDefaultAddressPools")
  227. defer span.End()
  228. for _, pool := range cfg.DefaultAddressPools.Value() {
  229. v.DefaultAddressPools = append(v.DefaultAddressPools, system.NetworkAddressPool{
  230. Base: pool.Base,
  231. Size: pool.Size,
  232. })
  233. }
  234. }
  235. func hostName(ctx context.Context) string {
  236. ctx, span := tracing.StartSpan(ctx, "hostName")
  237. defer span.End()
  238. hostname := ""
  239. if hn, err := os.Hostname(); err != nil {
  240. log.G(ctx).Warnf("Could not get hostname: %v", err)
  241. } else {
  242. hostname = hn
  243. }
  244. return hostname
  245. }
  246. func kernelVersion(ctx context.Context) string {
  247. ctx, span := tracing.StartSpan(ctx, "kernelVersion")
  248. defer span.End()
  249. var kernelVersion string
  250. if kv, err := kernel.GetKernelVersion(); err != nil {
  251. log.G(ctx).Warnf("Could not get kernel version: %v", err)
  252. } else {
  253. kernelVersion = kv.String()
  254. }
  255. return kernelVersion
  256. }
  257. func memInfo(ctx context.Context) *meminfo.Memory {
  258. ctx, span := tracing.StartSpan(ctx, "memInfo")
  259. defer span.End()
  260. memInfo, err := meminfo.Read()
  261. if err != nil {
  262. log.G(ctx).Errorf("Could not read system memory info: %v", err)
  263. memInfo = &meminfo.Memory{}
  264. }
  265. return memInfo
  266. }
  267. func operatingSystem(ctx context.Context) (operatingSystem string) {
  268. ctx, span := tracing.StartSpan(ctx, "operatingSystem")
  269. defer span.End()
  270. defer metrics.StartTimer(hostInfoFunctions.WithValues("operating_system"))()
  271. if s, err := operatingsystem.GetOperatingSystem(); err != nil {
  272. log.G(ctx).Warnf("Could not get operating system name: %v", err)
  273. } else {
  274. operatingSystem = s
  275. }
  276. if inContainer, err := operatingsystem.IsContainerized(); err != nil {
  277. log.G(ctx).Errorf("Could not determine if daemon is containerized: %v", err)
  278. operatingSystem += " (error determining if containerized)"
  279. } else if inContainer {
  280. operatingSystem += " (containerized)"
  281. }
  282. return operatingSystem
  283. }
  284. func osVersion(ctx context.Context) (version string) {
  285. ctx, span := tracing.StartSpan(ctx, "osVersion")
  286. defer span.End()
  287. defer metrics.StartTimer(hostInfoFunctions.WithValues("os_version"))()
  288. version, err := operatingsystem.GetOperatingSystemVersion()
  289. if err != nil {
  290. log.G(ctx).Warnf("Could not get operating system version: %v", err)
  291. }
  292. return version
  293. }
  294. func getEnvAny(names ...string) string {
  295. for _, n := range names {
  296. if val := os.Getenv(n); val != "" {
  297. return val
  298. }
  299. }
  300. return ""
  301. }
  302. func getConfigOrEnv(config string, env ...string) string {
  303. if config != "" {
  304. return config
  305. }
  306. return getEnvAny(env...)
  307. }
  308. // promoteNil converts a nil slice to an empty slice of that type.
  309. // A non-nil slice is returned as is.
  310. func promoteNil[S ~[]E, E any](s S) S {
  311. if s == nil {
  312. return S{}
  313. }
  314. return s
  315. }