github.com/microsoft/docker@v1.5.0-rc2/graph/manifest.go (about)

     1  package graph
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  
    10  	log "github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/engine"
    12  	"github.com/docker/docker/pkg/tarsum"
    13  	"github.com/docker/docker/registry"
    14  	"github.com/docker/docker/runconfig"
    15  	"github.com/docker/libtrust"
    16  )
    17  
    18  func (s *TagStore) CmdManifest(job *engine.Job) engine.Status {
    19  	if len(job.Args) != 1 {
    20  		return job.Errorf("usage: %s NAME", job.Name)
    21  	}
    22  	name := job.Args[0]
    23  	tag := job.Getenv("tag")
    24  	if tag == "" {
    25  		tag = "latest"
    26  	}
    27  
    28  	// Resolve the Repository name from fqn to endpoint + name
    29  	repoInfo, err := registry.ParseRepositoryInfo(name)
    30  	if err != nil {
    31  		return job.Error(err)
    32  	}
    33  
    34  	manifestBytes, err := s.newManifest(name, repoInfo.RemoteName, tag)
    35  	if err != nil {
    36  		return job.Error(err)
    37  	}
    38  
    39  	_, err = job.Stdout.Write(manifestBytes)
    40  	if err != nil {
    41  		return job.Error(err)
    42  	}
    43  
    44  	return engine.StatusOK
    45  }
    46  
    47  func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error) {
    48  	manifest := &registry.ManifestData{
    49  		Name:          remoteName,
    50  		Tag:           tag,
    51  		SchemaVersion: 1,
    52  	}
    53  	localRepo, err := s.Get(localName)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	if localRepo == nil {
    58  		return nil, fmt.Errorf("Repo does not exist: %s", localName)
    59  	}
    60  
    61  	// Get the top-most layer id which the tag points to
    62  	layerId, exists := localRepo[tag]
    63  	if !exists {
    64  		return nil, fmt.Errorf("Tag does not exist for %s: %s", localName, tag)
    65  	}
    66  	layersSeen := make(map[string]bool)
    67  
    68  	layer, err := s.graph.Get(layerId)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	manifest.Architecture = layer.Architecture
    73  	manifest.FSLayers = make([]*registry.FSLayer, 0, 4)
    74  	manifest.History = make([]*registry.ManifestHistory, 0, 4)
    75  	var metadata runconfig.Config
    76  	if layer.Config != nil {
    77  		metadata = *layer.Config
    78  	}
    79  
    80  	for ; layer != nil; layer, err = layer.GetParent() {
    81  		if err != nil {
    82  			return nil, err
    83  		}
    84  
    85  		if layersSeen[layer.ID] {
    86  			break
    87  		}
    88  		if layer.Config != nil && metadata.Image != layer.ID {
    89  			err = runconfig.Merge(&metadata, layer.Config)
    90  			if err != nil {
    91  				return nil, err
    92  			}
    93  		}
    94  
    95  		checksum := layer.Checksum
    96  		if tarsum.VersionLabelForChecksum(checksum) != tarsum.Version1.String() {
    97  			archive, err := layer.TarLayer()
    98  			if err != nil {
    99  				return nil, err
   100  			}
   101  
   102  			tarSum, err := tarsum.NewTarSum(archive, true, tarsum.Version1)
   103  			if err != nil {
   104  				return nil, err
   105  			}
   106  			if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
   107  				return nil, err
   108  			}
   109  
   110  			checksum = tarSum.Sum(nil)
   111  		}
   112  
   113  		jsonData, err := layer.RawJson()
   114  		if err != nil {
   115  			return nil, fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err)
   116  		}
   117  
   118  		manifest.FSLayers = append(manifest.FSLayers, &registry.FSLayer{BlobSum: checksum})
   119  
   120  		layersSeen[layer.ID] = true
   121  
   122  		manifest.History = append(manifest.History, &registry.ManifestHistory{V1Compatibility: string(jsonData)})
   123  	}
   124  
   125  	manifestBytes, err := json.MarshalIndent(manifest, "", "   ")
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	return manifestBytes, nil
   131  }
   132  
   133  func (s *TagStore) verifyManifest(eng *engine.Engine, manifestBytes []byte) (*registry.ManifestData, bool, error) {
   134  	sig, err := libtrust.ParsePrettySignature(manifestBytes, "signatures")
   135  	if err != nil {
   136  		return nil, false, fmt.Errorf("error parsing payload: %s", err)
   137  	}
   138  
   139  	keys, err := sig.Verify()
   140  	if err != nil {
   141  		return nil, false, fmt.Errorf("error verifying payload: %s", err)
   142  	}
   143  
   144  	payload, err := sig.Payload()
   145  	if err != nil {
   146  		return nil, false, fmt.Errorf("error retrieving payload: %s", err)
   147  	}
   148  
   149  	var manifest registry.ManifestData
   150  	if err := json.Unmarshal(payload, &manifest); err != nil {
   151  		return nil, false, fmt.Errorf("error unmarshalling manifest: %s", err)
   152  	}
   153  	if manifest.SchemaVersion != 1 {
   154  		return nil, false, fmt.Errorf("unsupported schema version: %d", manifest.SchemaVersion)
   155  	}
   156  
   157  	var verified bool
   158  	for _, key := range keys {
   159  		job := eng.Job("trust_key_check")
   160  		b, err := key.MarshalJSON()
   161  		if err != nil {
   162  			return nil, false, fmt.Errorf("error marshalling public key: %s", err)
   163  		}
   164  		namespace := manifest.Name
   165  		if namespace[0] != '/' {
   166  			namespace = "/" + namespace
   167  		}
   168  		stdoutBuffer := bytes.NewBuffer(nil)
   169  
   170  		job.Args = append(job.Args, namespace)
   171  		job.Setenv("PublicKey", string(b))
   172  		// Check key has read/write permission (0x03)
   173  		job.SetenvInt("Permission", 0x03)
   174  		job.Stdout.Add(stdoutBuffer)
   175  		if err = job.Run(); err != nil {
   176  			return nil, false, fmt.Errorf("error running key check: %s", err)
   177  		}
   178  		result := engine.Tail(stdoutBuffer, 1)
   179  		log.Debugf("Key check result: %q", result)
   180  		if result == "verified" {
   181  			verified = true
   182  		}
   183  	}
   184  
   185  	return &manifest, verified, nil
   186  }
   187  
   188  func checkValidManifest(manifest *registry.ManifestData) error {
   189  	if len(manifest.FSLayers) != len(manifest.History) {
   190  		return fmt.Errorf("length of history not equal to number of layers")
   191  	}
   192  
   193  	if len(manifest.FSLayers) == 0 {
   194  		return fmt.Errorf("no FSLayers in manifest")
   195  	}
   196  
   197  	return nil
   198  }