Skip to content

Commit

Permalink
Merge pull request #6 from dmcgowan/wip_provenance
Browse files Browse the repository at this point in the history
WIP support manifest signature
  • Loading branch information
vbatts committed Aug 29, 2014
2 parents 1a60206 + e821f58 commit bfdcdb2
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 46 deletions.
5 changes: 4 additions & 1 deletion api/client/cli.go
Expand Up @@ -13,6 +13,7 @@ import (
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/term"
"github.com/docker/docker/registry"
"github.com/docker/libtrust"
)

type DockerCli struct {
Expand All @@ -22,6 +23,7 @@ type DockerCli struct {
in io.ReadCloser
out io.Writer
err io.Writer
key libtrust.PrivateKey
isTerminal bool
terminalFd uintptr
tlsConfig *tls.Config
Expand Down Expand Up @@ -78,7 +80,7 @@ func (cli *DockerCli) LoadConfigFile() (err error) {
return err
}

func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsConfig *tls.Config) *DockerCli {
func NewDockerCli(in io.ReadCloser, out, err io.Writer, key libtrust.PrivateKey, proto, addr string, tlsConfig *tls.Config) *DockerCli {
var (
isTerminal = false
terminalFd uintptr
Expand All @@ -105,6 +107,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC
in: in,
out: out,
err: err,
key: key,
isTerminal: isTerminal,
terminalFd: terminalFd,
tlsConfig: tlsConfig,
Expand Down
23 changes: 22 additions & 1 deletion api/client/commands.go
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/docker/docker/registry"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
"github.com/docker/libtrust"
)

const (
Expand Down Expand Up @@ -1153,6 +1154,26 @@ func (cli *DockerCli) CmdPush(args ...string) error {

v := url.Values{}
v.Set("tag", tag)

body, _, err := readBody(cli.call("GET", "/images/"+remote+"/manifest?"+v.Encode(), nil, false))
if err != nil {
return err
}

js, err := libtrust.NewJSONSignature(body)
if err != nil {
return err
}
err = js.Sign(cli.key)
if err != nil {
return err
}

signedBody, err := js.PrettySignature("buildSignatures")
if err != nil {
return err
}

push := func(authConfig registry.AuthConfig) error {
buf, err := json.Marshal(authConfig)
if err != nil {
Expand All @@ -1162,7 +1183,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
base64.URLEncoding.EncodeToString(buf),
}

return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), bytes.NewReader(signedBody), cli.out, map[string][]string{
"X-Registry-Auth": registryAuthHeader,
})
}
Expand Down
19 changes: 19 additions & 0 deletions api/server/server.go
Expand Up @@ -555,6 +555,18 @@ func getImagesSearch(eng *engine.Engine, version version.Version, w http.Respons
return job.Run()
}

func getImageManifest(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}

job := eng.Job("image_manifest", vars["name"])
job.Setenv("tag", r.Form.Get("tag"))
job.Stdout.Add(utils.NewWriteFlusher(w))

return job.Run()
}

func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
Expand Down Expand Up @@ -586,9 +598,15 @@ func postImagesPush(eng *engine.Engine, version version.Version, w http.Response
}
}

manifest, err := ioutil.ReadAll(r.Body)
if err != nil {
return err
}

job := eng.Job("push", vars["name"])
job.SetenvJson("metaHeaders", metaHeaders)
job.SetenvJson("authConfig", authConfig)
job.Setenv("manifest", string(manifest))
job.Setenv("tag", r.Form.Get("tag"))
if version.GreaterThan("1.0") {
job.SetenvBool("json", true)
Expand Down Expand Up @@ -1105,6 +1123,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
"/images/json": getImagesJSON,
"/images/viz": getImagesViz,
"/images/search": getImagesSearch,
"/images/{name:.*}/manifest": getImageManifest,
"/images/{name:.*}/get": getImagesGet,
"/images/{name:.*}/history": getImagesHistory,
"/images/{name:.*}/json": getImagesByName,
Expand Down
1 change: 1 addition & 0 deletions docker/daemon.go
Expand Up @@ -74,6 +74,7 @@ func mainDaemon() {
job.Setenv("TlsCa", *flCa)
job.Setenv("TlsCert", *flCert)
job.Setenv("TlsKey", *flKey)
job.Setenv("TrustKey", *flTrustKey)
job.SetenvBool("BufferRequests", true)
if err := job.Run(); err != nil {
log.Fatal(err)
Expand Down
23 changes: 18 additions & 5 deletions docker/docker.go
Expand Up @@ -15,12 +15,14 @@ import (
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/reexec"
"github.com/docker/docker/utils"
"github.com/docker/libtrust"
)

const (
defaultCaFile = "ca.pem"
defaultKeyFile = "key.pem"
defaultCertFile = "cert.pem"
defaultCaFile = "ca.pem"
defaultKeyFile = "key.pem"
defaultCertFile = "cert.pem"
defaultTrustKeyFile = "key.json"
)

func main() {
Expand Down Expand Up @@ -60,6 +62,17 @@ func main() {
}
protoAddrParts := strings.SplitN(flHosts[0], "://", 2)

trustKey, keyErr := libtrust.LoadKeyFile(*flTrustKey)
if keyErr == libtrust.ErrKeyFileDoesNotExist {
trustKey, keyErr = libtrust.GenerateECP256PrivateKey()
if keyErr == nil {
keyErr = libtrust.SaveKey(*flTrustKey, trustKey)
}
}
if keyErr != nil {
log.Fatal(keyErr)
}

var (
cli *client.DockerCli
tlsConfig tls.Config
Expand Down Expand Up @@ -94,9 +107,9 @@ func main() {
}

if *flTls || *flTlsVerify {
cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], &tlsConfig)
cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, trustKey, protoAddrParts[0], protoAddrParts[1], &tlsConfig)
} else {
cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], nil)
cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, trustKey, protoAddrParts[0], protoAddrParts[1], nil)
}

if err := cli.Cmd(flag.Args()...); err != nil {
Expand Down
10 changes: 6 additions & 4 deletions docker/flags.go
Expand Up @@ -28,13 +28,15 @@ var (
flTlsVerify = flag.Bool([]string{"-tlsverify"}, false, "Use TLS and verify the remote (daemon: verify client, client: verify daemon)")

// these are initialized in init() below since their default values depend on dockerCertPath which isn't fully initialized until init() runs
flCa *string
flCert *string
flKey *string
flHosts []string
flTrustKey *string
flCa *string
flCert *string
flKey *string
flHosts []string
)

func init() {
flTrustKey = flag.String([]string{"i", "-identity"}, filepath.Join(dockerCertPath, defaultTrustKeyFile), "Path to libtrust key file")
flCa = flag.String([]string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust only remotes providing a certificate signed by the CA given here")
flCert = flag.String([]string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file")
flKey = flag.String([]string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file")
Expand Down
104 changes: 104 additions & 0 deletions graph/manifest.go
@@ -0,0 +1,104 @@
package graph

import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"path"

"github.com/docker/docker/engine"
"github.com/docker/docker/pkg/tarsum"
"github.com/docker/docker/registry"
"github.com/docker/docker/runconfig"
)

func (s *TagStore) CmdManifest(job *engine.Job) engine.Status {
if len(job.Args) != 1 {
return job.Errorf("usage: %s NAME", job.Name)
}
name := job.Args[0]
tag := job.Getenv("tag")
if tag == "" {
tag = "latest"
}

// Resolve the Repository name from fqn to endpoint + name
_, remoteName, err := registry.ResolveRepositoryName(name)
if err != nil {
return job.Error(err)
}

manifest := map[string]interface{}{
"name": remoteName,
"tag": tag,
}
localRepo, exists := s.Repositories[name]
if !exists {
return job.Errorf("Repo does not exist: %s", name)
}

layerId, exists := localRepo[tag]
if !exists {
return job.Errorf("Tag does not exist for %s: %s", name, tag)
}
tarsums := make([]string, 0, 4)
layersSeen := make(map[string]bool)

layer, err := s.graph.Get(layerId)
manifest["architecture"] = layer.Architecture
var metadata runconfig.Config
metadata = *layer.Config
history := make(map[string]string)

for ; layer != nil; layer, err = layer.GetParent() {
if err != nil {
return job.Error(err)
}

if layersSeen[layer.ID] {
break
}
if layer.Config != nil && metadata.Image != layer.ID {
err = runconfig.Merge(&metadata, layer.Config)
if err != nil {
return job.Error(err)
}
}
archive, err := layer.TarLayer()
if err != nil {
return job.Error(err)
}

tarSum := &tarsum.TarSum{Reader: archive, DisableCompression: true}
if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
return job.Error(err)
}

layersSeen[layer.ID] = true
tarId := tarSum.Sum(nil)
tarsums = append(tarsums, tarId)

jsonData, err := ioutil.ReadFile(path.Join(s.graph.Root, layer.ID, "json"))
if err != nil {
return job.Error(fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err))
}
history[tarId] = string(jsonData)
}

manifest["tarsum"] = tarsums
manifest["metadata"] = &metadata
manifest["history"] = history

manifestBytes, err := json.MarshalIndent(manifest, "", " ")
if err != nil {
return job.Error(err)
}

_, err = job.Stdout.Write(manifestBytes)
if err != nil {
return job.Error(err)
}

return engine.StatusOK
}

0 comments on commit bfdcdb2

Please sign in to comment.