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