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  }