41bfa87b6c
manifest code calls TarLayer() and gets archive. This archive needs to be closed once caller is done using it to release the resrouces held by archive. For the devicemapper graphdriver, archive keeps a device mounted (device which is backing the layer). If archive.Close() is not called, that device remains mounted and later deletion of device fails leading to various other issues. Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
184 lines
4.9 KiB
Go
184 lines
4.9 KiB
Go
package graph
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/engine"
|
|
"github.com/docker/docker/pkg/tarsum"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/docker/docker/runconfig"
|
|
"github.com/docker/libtrust"
|
|
)
|
|
|
|
func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error) {
|
|
manifest := ®istry.ManifestData{
|
|
Name: remoteName,
|
|
Tag: tag,
|
|
SchemaVersion: 1,
|
|
}
|
|
localRepo, err := s.Get(localName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if localRepo == nil {
|
|
return nil, fmt.Errorf("Repo does not exist: %s", localName)
|
|
}
|
|
|
|
// Get the top-most layer id which the tag points to
|
|
layerId, exists := localRepo[tag]
|
|
if !exists {
|
|
return nil, fmt.Errorf("Tag does not exist for %s: %s", localName, tag)
|
|
}
|
|
layersSeen := make(map[string]bool)
|
|
|
|
layer, err := s.graph.Get(layerId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
manifest.Architecture = layer.Architecture
|
|
manifest.FSLayers = make([]*registry.FSLayer, 0, 4)
|
|
manifest.History = make([]*registry.ManifestHistory, 0, 4)
|
|
var metadata runconfig.Config
|
|
if layer.Config != nil {
|
|
metadata = *layer.Config
|
|
}
|
|
|
|
for ; layer != nil; layer, err = layer.GetParent() {
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if layersSeen[layer.ID] {
|
|
break
|
|
}
|
|
if layer.Config != nil && metadata.Image != layer.ID {
|
|
err = runconfig.Merge(&metadata, layer.Config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
checksum, err := layer.GetCheckSum(s.graph.ImageRoot(layer.ID))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error getting image checksum: %s", err)
|
|
}
|
|
if tarsum.VersionLabelForChecksum(checksum) != tarsum.Version1.String() {
|
|
archive, err := layer.TarLayer()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer archive.Close()
|
|
|
|
tarSum, err := tarsum.NewTarSum(archive, true, tarsum.Version1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
checksum = tarSum.Sum(nil)
|
|
|
|
// Save checksum value
|
|
if err := layer.SaveCheckSum(s.graph.ImageRoot(layer.ID), checksum); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
jsonData, err := layer.RawJson()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err)
|
|
}
|
|
|
|
manifest.FSLayers = append(manifest.FSLayers, ®istry.FSLayer{BlobSum: checksum})
|
|
|
|
layersSeen[layer.ID] = true
|
|
|
|
manifest.History = append(manifest.History, ®istry.ManifestHistory{V1Compatibility: string(jsonData)})
|
|
}
|
|
|
|
manifestBytes, err := json.MarshalIndent(manifest, "", " ")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return manifestBytes, nil
|
|
}
|
|
|
|
// loadManifest loads a manifest from a byte array and verifies its content.
|
|
// The signature must be verified or an error is returned. If the manifest
|
|
// contains no signatures by a trusted key for the name in the manifest, the
|
|
// image is not considered verified. The parsed manifest object and a boolean
|
|
// for whether the manifest is verified is returned.
|
|
func (s *TagStore) loadManifest(eng *engine.Engine, manifestBytes []byte) (*registry.ManifestData, bool, error) {
|
|
sig, err := libtrust.ParsePrettySignature(manifestBytes, "signatures")
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("error parsing payload: %s", err)
|
|
}
|
|
|
|
keys, err := sig.Verify()
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("error verifying payload: %s", err)
|
|
}
|
|
|
|
payload, err := sig.Payload()
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("error retrieving payload: %s", err)
|
|
}
|
|
|
|
var manifest registry.ManifestData
|
|
if err := json.Unmarshal(payload, &manifest); err != nil {
|
|
return nil, false, fmt.Errorf("error unmarshalling manifest: %s", err)
|
|
}
|
|
if manifest.SchemaVersion != 1 {
|
|
return nil, false, fmt.Errorf("unsupported schema version: %d", manifest.SchemaVersion)
|
|
}
|
|
|
|
var verified bool
|
|
for _, key := range keys {
|
|
job := eng.Job("trust_key_check")
|
|
b, err := key.MarshalJSON()
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("error marshalling public key: %s", err)
|
|
}
|
|
namespace := manifest.Name
|
|
if namespace[0] != '/' {
|
|
namespace = "/" + namespace
|
|
}
|
|
stdoutBuffer := bytes.NewBuffer(nil)
|
|
|
|
job.Args = append(job.Args, namespace)
|
|
job.Setenv("PublicKey", string(b))
|
|
// Check key has read/write permission (0x03)
|
|
job.SetenvInt("Permission", 0x03)
|
|
job.Stdout.Add(stdoutBuffer)
|
|
if err = job.Run(); err != nil {
|
|
return nil, false, fmt.Errorf("error running key check: %s", err)
|
|
}
|
|
result := engine.Tail(stdoutBuffer, 1)
|
|
log.Debugf("Key check result: %q", result)
|
|
if result == "verified" {
|
|
verified = true
|
|
}
|
|
}
|
|
|
|
return &manifest, verified, nil
|
|
}
|
|
|
|
func checkValidManifest(manifest *registry.ManifestData) error {
|
|
if len(manifest.FSLayers) != len(manifest.History) {
|
|
return fmt.Errorf("length of history not equal to number of layers")
|
|
}
|
|
|
|
if len(manifest.FSLayers) == 0 {
|
|
return fmt.Errorf("no FSLayers in manifest")
|
|
}
|
|
|
|
return nil
|
|
}
|