start_windows.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package daemon
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "path/filepath"
  6. "strings"
  7. "github.com/docker/docker/container"
  8. "github.com/docker/docker/layer"
  9. "github.com/docker/docker/libcontainerd"
  10. "github.com/docker/docker/pkg/system"
  11. "golang.org/x/sys/windows/registry"
  12. )
  13. const (
  14. credentialSpecRegistryLocation = `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs`
  15. credentialSpecFileLocation = "CredentialSpecs"
  16. )
  17. func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) ([]libcontainerd.CreateOption, error) {
  18. createOptions := []libcontainerd.CreateOption{}
  19. // Are we going to run as a Hyper-V container?
  20. hvOpts := &libcontainerd.HyperVIsolationOption{}
  21. if container.HostConfig.Isolation.IsDefault() {
  22. // Container is set to use the default, so take the default from the daemon configuration
  23. hvOpts.IsHyperV = daemon.defaultIsolation.IsHyperV()
  24. } else {
  25. // Container is requesting an isolation mode. Honour it.
  26. hvOpts.IsHyperV = container.HostConfig.Isolation.IsHyperV()
  27. }
  28. dnsSearch := daemon.getDNSSearchSettings(container)
  29. if dnsSearch != nil {
  30. osv := system.GetOSVersion()
  31. if osv.Build < 14997 {
  32. return nil, fmt.Errorf("dns-search option is not supported on the current platform")
  33. }
  34. }
  35. // Generate the layer folder of the layer options
  36. layerOpts := &libcontainerd.LayerOption{}
  37. m, err := container.RWLayer.Metadata()
  38. if err != nil {
  39. return nil, fmt.Errorf("failed to get layer metadata - %s", err)
  40. }
  41. if hvOpts.IsHyperV {
  42. hvOpts.SandboxPath = filepath.Dir(m["dir"])
  43. }
  44. layerOpts.LayerFolderPath = m["dir"]
  45. // Generate the layer paths of the layer options
  46. img, err := daemon.imageStore.Get(container.ImageID)
  47. if err != nil {
  48. return nil, fmt.Errorf("failed to graph.Get on ImageID %s - %s", container.ImageID, err)
  49. }
  50. // Get the layer path for each layer.
  51. max := len(img.RootFS.DiffIDs)
  52. for i := 1; i <= max; i++ {
  53. img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i]
  54. layerPath, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID())
  55. if err != nil {
  56. return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err)
  57. }
  58. // Reverse order, expecting parent most first
  59. layerOpts.LayerPaths = append([]string{layerPath}, layerOpts.LayerPaths...)
  60. }
  61. // Get endpoints for the libnetwork allocated networks to the container
  62. var epList []string
  63. AllowUnqualifiedDNSQuery := false
  64. gwHNSID := ""
  65. if container.NetworkSettings != nil {
  66. for n := range container.NetworkSettings.Networks {
  67. sn, err := daemon.FindNetwork(n)
  68. if err != nil {
  69. continue
  70. }
  71. ep, err := container.GetEndpointInNetwork(sn)
  72. if err != nil {
  73. continue
  74. }
  75. data, err := ep.DriverInfo()
  76. if err != nil {
  77. continue
  78. }
  79. if data["GW_INFO"] != nil {
  80. gwInfo := data["GW_INFO"].(map[string]interface{})
  81. if gwInfo["hnsid"] != nil {
  82. gwHNSID = gwInfo["hnsid"].(string)
  83. }
  84. }
  85. if data["hnsid"] != nil {
  86. epList = append(epList, data["hnsid"].(string))
  87. }
  88. if data["AllowUnqualifiedDNSQuery"] != nil {
  89. AllowUnqualifiedDNSQuery = true
  90. }
  91. }
  92. }
  93. if gwHNSID != "" {
  94. epList = append(epList, gwHNSID)
  95. }
  96. // Read and add credentials from the security options if a credential spec has been provided.
  97. if container.HostConfig.SecurityOpt != nil {
  98. for _, sOpt := range container.HostConfig.SecurityOpt {
  99. sOpt = strings.ToLower(sOpt)
  100. if !strings.Contains(sOpt, "=") {
  101. return nil, fmt.Errorf("invalid security option: no equals sign in supplied value %s", sOpt)
  102. }
  103. var splitsOpt []string
  104. splitsOpt = strings.SplitN(sOpt, "=", 2)
  105. if len(splitsOpt) != 2 {
  106. return nil, fmt.Errorf("invalid security option: %s", sOpt)
  107. }
  108. if splitsOpt[0] != "credentialspec" {
  109. return nil, fmt.Errorf("security option not supported: %s", splitsOpt[0])
  110. }
  111. credentialsOpts := &libcontainerd.CredentialsOption{}
  112. var (
  113. match bool
  114. csValue string
  115. err error
  116. )
  117. if match, csValue = getCredentialSpec("file://", splitsOpt[1]); match {
  118. if csValue == "" {
  119. return nil, fmt.Errorf("no value supplied for file:// credential spec security option")
  120. }
  121. if credentialsOpts.Credentials, err = readCredentialSpecFile(container.ID, daemon.root, filepath.Clean(csValue)); err != nil {
  122. return nil, err
  123. }
  124. } else if match, csValue = getCredentialSpec("registry://", splitsOpt[1]); match {
  125. if csValue == "" {
  126. return nil, fmt.Errorf("no value supplied for registry:// credential spec security option")
  127. }
  128. if credentialsOpts.Credentials, err = readCredentialSpecRegistry(container.ID, csValue); err != nil {
  129. return nil, err
  130. }
  131. } else {
  132. return nil, fmt.Errorf("invalid credential spec security option - value must be prefixed file:// or registry:// followed by a value")
  133. }
  134. createOptions = append(createOptions, credentialsOpts)
  135. }
  136. }
  137. // Now add the remaining options.
  138. createOptions = append(createOptions, &libcontainerd.FlushOption{IgnoreFlushesDuringBoot: !container.HasBeenStartedBefore})
  139. createOptions = append(createOptions, hvOpts)
  140. createOptions = append(createOptions, layerOpts)
  141. if epList != nil {
  142. createOptions = append(createOptions, &libcontainerd.NetworkEndpointsOption{
  143. Endpoints: epList,
  144. AllowUnqualifiedDNSQuery: AllowUnqualifiedDNSQuery,
  145. DNSSearchList: dnsSearch,
  146. })
  147. }
  148. return createOptions, nil
  149. }
  150. // getCredentialSpec is a helper function to get the value of a credential spec supplied
  151. // on the CLI, stripping the prefix
  152. func getCredentialSpec(prefix, value string) (bool, string) {
  153. if strings.HasPrefix(value, prefix) {
  154. return true, strings.TrimPrefix(value, prefix)
  155. }
  156. return false, ""
  157. }
  158. // readCredentialSpecRegistry is a helper function to read a credential spec from
  159. // the registry. If not found, we return an empty string and warn in the log.
  160. // This allows for staging on machines which do not have the necessary components.
  161. func readCredentialSpecRegistry(id, name string) (string, error) {
  162. var (
  163. k registry.Key
  164. err error
  165. val string
  166. )
  167. if k, err = registry.OpenKey(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.QUERY_VALUE); err != nil {
  168. return "", fmt.Errorf("failed handling spec %q for container %s - %s could not be opened", name, id, credentialSpecRegistryLocation)
  169. }
  170. if val, _, err = k.GetStringValue(name); err != nil {
  171. if err == registry.ErrNotExist {
  172. return "", fmt.Errorf("credential spec %q for container %s as it was not found", name, id)
  173. }
  174. return "", fmt.Errorf("error %v reading credential spec %q from registry for container %s", err, name, id)
  175. }
  176. return val, nil
  177. }
  178. // readCredentialSpecFile is a helper function to read a credential spec from
  179. // a file. If not found, we return an empty string and warn in the log.
  180. // This allows for staging on machines which do not have the necessary components.
  181. func readCredentialSpecFile(id, root, location string) (string, error) {
  182. if filepath.IsAbs(location) {
  183. return "", fmt.Errorf("invalid credential spec - file:// path cannot be absolute")
  184. }
  185. base := filepath.Join(root, credentialSpecFileLocation)
  186. full := filepath.Join(base, location)
  187. if !strings.HasPrefix(full, base) {
  188. return "", fmt.Errorf("invalid credential spec - file:// path must be under %s", base)
  189. }
  190. bcontents, err := ioutil.ReadFile(full)
  191. if err != nil {
  192. return "", fmt.Errorf("credential spec '%s' for container %s as the file could not be read: %q", full, id, err)
  193. }
  194. return string(bcontents[:]), nil
  195. }