mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
updating notary and gotuf with latest bugfixes
Signed-off-by: David Lawrence <david.lawrence@docker.com>
This commit is contained in:
parent
1052ff674c
commit
7ce9f02751
10 changed files with 199 additions and 57 deletions
|
@ -41,8 +41,8 @@ clone git github.com/boltdb/bolt v1.0
|
|||
clone git github.com/docker/distribution 20c4b7a1805a52753dfd593ee1cc35558722a0ce # docker/1.9 branch
|
||||
clone git github.com/vbatts/tar-split v0.9.10
|
||||
|
||||
clone git github.com/docker/notary ac05822d7d71ef077df3fc24f506672282a1feea
|
||||
clone git github.com/endophage/gotuf 9bcdad0308e34a49f38448b8ad436ad8860825ce
|
||||
clone git github.com/docker/notary 089d8450d8928aa1c58fd03f09cabbde9bcb4590
|
||||
clone git github.com/endophage/gotuf 876c31a61bc4aa0dae09bb8ef3946dc26dd04924
|
||||
clone git github.com/jfrazelle/go 6e461eb70cb4187b41a84e9a567d7137bdbe0f16
|
||||
clone git github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package changelist
|
||||
|
||||
import (
|
||||
"github.com/endophage/gotuf/data"
|
||||
)
|
||||
|
||||
// Scopes for TufChanges are simply the TUF roles.
|
||||
// Unfortunately because of targets delegations, we can only
|
||||
// cover the base roles.
|
||||
|
@ -10,6 +14,17 @@ const (
|
|||
ScopeTimestamp = "timestamp"
|
||||
)
|
||||
|
||||
// Types for TufChanges are namespaced by the Role they
|
||||
// are relevant for. The Root and Targets roles are the
|
||||
// only ones for which user action can cause a change, as
|
||||
// all changes in Snapshot and Timestamp are programatically
|
||||
// generated base on Root and Targets changes.
|
||||
const (
|
||||
TypeRootRole = "role"
|
||||
TypeTargetsTarget = "target"
|
||||
TypeTargetsDelegation = "delegation"
|
||||
)
|
||||
|
||||
// TufChange represents a change to a TUF repo
|
||||
type TufChange struct {
|
||||
// Abbreviated because Go doesn't permit a field and method of the same name
|
||||
|
@ -20,6 +35,13 @@ type TufChange struct {
|
|||
Data []byte `json:"data"`
|
||||
}
|
||||
|
||||
// TufRootData represents a modification of the keys associated
|
||||
// with a role that appears in the root.json
|
||||
type TufRootData struct {
|
||||
Keys []data.TUFKey `json:"keys"`
|
||||
RoleName string `json:"role"`
|
||||
}
|
||||
|
||||
// NewTufChange initializes a tufChange object
|
||||
func NewTufChange(action string, role, changeType, changePath string, content []byte) *TufChange {
|
||||
return &TufChange{
|
||||
|
|
|
@ -245,6 +245,7 @@ func (r *NotaryRepository) AddTarget(target *Target) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cl.Close()
|
||||
logrus.Debugf("Adding target \"%s\" with sha256 \"%x\" and size %d bytes.\n", target.Name, target.Hashes["sha256"], target.Length)
|
||||
|
||||
meta := data.FileMeta{Length: target.Length, Hashes: target.Hashes}
|
||||
|
@ -258,7 +259,7 @@ func (r *NotaryRepository) AddTarget(target *Target) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cl.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveTarget creates a new changelist entry to remove a target from the repository
|
||||
|
@ -326,6 +327,17 @@ func (r *NotaryRepository) GetTargetByName(name string) (*Target, error) {
|
|||
return &Target{Name: name, Hashes: meta.Hashes, Length: meta.Length}, nil
|
||||
}
|
||||
|
||||
// GetChangelist returns the list of the repository's unpublished changes
|
||||
func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) {
|
||||
changelistDir := filepath.Join(r.tufRepoPath, "changelist")
|
||||
cl, err := changelist.NewFileChangelist(changelistDir)
|
||||
if err != nil {
|
||||
logrus.Debug("Error initializing changelist")
|
||||
return nil, err
|
||||
}
|
||||
return cl, nil
|
||||
}
|
||||
|
||||
// Publish pushes the local changes in signed material to the remote notary-server
|
||||
// Conceptually it performs an operation similar to a `git rebase`
|
||||
func (r *NotaryRepository) Publish() error {
|
||||
|
@ -371,11 +383,8 @@ func (r *NotaryRepository) Publish() error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
// load the changelist for this repo
|
||||
changelistDir := filepath.Join(r.tufRepoPath, "changelist")
|
||||
cl, err := changelist.NewFileChangelist(changelistDir)
|
||||
cl, err := r.GetChangelist()
|
||||
if err != nil {
|
||||
logrus.Debug("Error initializing changelist")
|
||||
return err
|
||||
}
|
||||
// apply the changelist to the repo
|
||||
|
@ -445,7 +454,7 @@ func (r *NotaryRepository) Publish() error {
|
|||
// This is not a critical problem when only a single host is pushing
|
||||
// but will cause weird behaviour if changelist cleanup is failing
|
||||
// and there are multiple hosts writing to the repo.
|
||||
logrus.Warn("Unable to clear changelist. You may want to manually delete the folder ", changelistDir)
|
||||
logrus.Warn("Unable to clear changelist. You may want to manually delete the folder ", filepath.Join(r.tufRepoPath, "changelist"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -596,3 +605,55 @@ func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
|
|||
r.fileStore,
|
||||
), nil
|
||||
}
|
||||
|
||||
// RotateKeys removes all existing keys associated with role and adds
|
||||
// the keys specified by keyIDs to the role. These changes are staged
|
||||
// in a changelist until publish is called.
|
||||
func (r *NotaryRepository) RotateKeys() error {
|
||||
for _, role := range []string{"targets", "snapshot"} {
|
||||
key, err := r.cryptoService.Create(role, data.ECDSAKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.rootFileKeyChange(role, changelist.ActionCreate, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *NotaryRepository) rootFileKeyChange(role, action string, key data.PublicKey) error {
|
||||
cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cl.Close()
|
||||
|
||||
k, ok := key.(*data.TUFKey)
|
||||
if !ok {
|
||||
return errors.New("Invalid key type found during rotation.")
|
||||
}
|
||||
|
||||
meta := changelist.TufRootData{
|
||||
RoleName: role,
|
||||
Keys: []data.TUFKey{*k},
|
||||
}
|
||||
metaJSON, err := json.Marshal(meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c := changelist.NewTufChange(
|
||||
action,
|
||||
changelist.ScopeRoot,
|
||||
changelist.TypeRootRole,
|
||||
role,
|
||||
metaJSON,
|
||||
)
|
||||
err = cl.Add(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/notary/client/changelist"
|
||||
"github.com/endophage/gotuf"
|
||||
tuf "github.com/endophage/gotuf"
|
||||
"github.com/endophage/gotuf/data"
|
||||
"github.com/endophage/gotuf/keys"
|
||||
"github.com/endophage/gotuf/store"
|
||||
|
@ -38,14 +38,16 @@ func applyChangelist(repo *tuf.TufRepo, cl changelist.Changelist) error {
|
|||
}
|
||||
switch c.Scope() {
|
||||
case changelist.ScopeTargets:
|
||||
err := applyTargetsChange(repo, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = applyTargetsChange(repo, c)
|
||||
case changelist.ScopeRoot:
|
||||
err = applyRootChange(repo, c)
|
||||
default:
|
||||
logrus.Debug("scope not supported: ", c.Scope())
|
||||
}
|
||||
index++
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
logrus.Debugf("applied %d change(s)", index)
|
||||
return nil
|
||||
|
@ -75,6 +77,40 @@ func applyTargetsChange(repo *tuf.TufRepo, c changelist.Change) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func applyRootChange(repo *tuf.TufRepo, c changelist.Change) error {
|
||||
var err error
|
||||
switch c.Type() {
|
||||
case changelist.TypeRootRole:
|
||||
err = applyRootRoleChange(repo, c)
|
||||
default:
|
||||
logrus.Debug("type of root change not yet supported: ", c.Type())
|
||||
}
|
||||
return err // might be nil
|
||||
}
|
||||
|
||||
func applyRootRoleChange(repo *tuf.TufRepo, c changelist.Change) error {
|
||||
switch c.Action() {
|
||||
case changelist.ActionCreate:
|
||||
// replaces all keys for a role
|
||||
d := &changelist.TufRootData{}
|
||||
err := json.Unmarshal(c.Content(), d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k := []data.PublicKey{}
|
||||
for _, key := range d.Keys {
|
||||
k = append(k, data.NewPublicKey(key.Algorithm(), key.Public()))
|
||||
}
|
||||
err = repo.ReplaceBaseKeys(d.RoleName, k...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
logrus.Debug("action not yet supported for root: ", c.Action())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func nearExpiry(r *data.SignedRoot) bool {
|
||||
plus6mo := time.Now().AddDate(0, 6, 0)
|
||||
return r.Signed.Expires.Before(plus6mo)
|
||||
|
|
|
@ -175,19 +175,7 @@ func (c *Client) downloadRoot() error {
|
|||
var s *data.Signed
|
||||
var raw []byte
|
||||
if download {
|
||||
logrus.Debug("downloading new root")
|
||||
raw, err = c.remote.GetMeta(role, size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hash := sha256.Sum256(raw)
|
||||
if expectedSha256 != nil && !bytes.Equal(hash[:], expectedSha256) {
|
||||
// if we don't have an expected sha256, we're going to trust the root
|
||||
// based purely on signature and expiry time validation
|
||||
return fmt.Errorf("Remote root sha256 did not match snapshot root sha256: %#x vs. %#x", hash, []byte(expectedSha256))
|
||||
}
|
||||
s = &data.Signed{}
|
||||
err = json.Unmarshal(raw, s)
|
||||
raw, s, err = c.downloadSigned(role, size, expectedSha256)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -247,6 +235,8 @@ func (c Client) verifyRoot(role string, s *data.Signed, minVersion int) error {
|
|||
}
|
||||
|
||||
// downloadTimestamp is responsible for downloading the timestamp.json
|
||||
// Timestamps are special in that we ALWAYS attempt to download and only
|
||||
// use cache if the download fails (and the cache is still valid).
|
||||
func (c *Client) downloadTimestamp() error {
|
||||
logrus.Debug("downloadTimestamp")
|
||||
role := data.RoleName("timestamp")
|
||||
|
@ -271,7 +261,6 @@ func (c *Client) downloadTimestamp() error {
|
|||
}
|
||||
// unlike root, targets and snapshot, always try and download timestamps
|
||||
// from remote, only using the cache one if we couldn't reach remote.
|
||||
logrus.Debug("Downloading timestamp")
|
||||
raw, err := c.remote.GetMeta(role, maxSize)
|
||||
var s *data.Signed
|
||||
if err != nil || len(raw) == 0 {
|
||||
|
@ -286,6 +275,7 @@ func (c *Client) downloadTimestamp() error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
logrus.Debug("using cached timestamp")
|
||||
s = old
|
||||
} else {
|
||||
download = true
|
||||
|
@ -351,17 +341,7 @@ func (c *Client) downloadSnapshot() error {
|
|||
}
|
||||
var s *data.Signed
|
||||
if download {
|
||||
logrus.Debug("downloading new snapshot")
|
||||
raw, err = c.remote.GetMeta(role, size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
genHash := sha256.Sum256(raw)
|
||||
if !bytes.Equal(genHash[:], expectedSha256) {
|
||||
return fmt.Errorf("Retrieved snapshot did not verify against hash in timestamp.")
|
||||
}
|
||||
s = &data.Signed{}
|
||||
err = json.Unmarshal(raw, s)
|
||||
raw, s, err = c.downloadSigned(role, size, expectedSha256)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -390,8 +370,7 @@ func (c *Client) downloadSnapshot() error {
|
|||
}
|
||||
|
||||
// downloadTargets is responsible for downloading any targets file
|
||||
// including delegates roles. It will download the whole tree of
|
||||
// delegated roles below the given one
|
||||
// including delegates roles.
|
||||
func (c *Client) downloadTargets(role string) error {
|
||||
role = data.RoleName(role) // this will really only do something for base targets role
|
||||
snap := c.local.Snapshot.Signed
|
||||
|
@ -418,6 +397,24 @@ func (c *Client) downloadTargets(role string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) downloadSigned(role string, size int64, expectedSha256 []byte) ([]byte, *data.Signed, error) {
|
||||
logrus.Debugf("downloading new %s", role)
|
||||
raw, err := c.remote.GetMeta(role, size)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
genHash := sha256.Sum256(raw)
|
||||
if !bytes.Equal(genHash[:], expectedSha256) {
|
||||
return nil, nil, ErrChecksumMismatch{role: role}
|
||||
}
|
||||
s := &data.Signed{}
|
||||
err = json.Unmarshal(raw, s)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return raw, s, nil
|
||||
}
|
||||
|
||||
func (c Client) GetTargetsFile(role string, keyIDs []string, snapshotMeta data.Files, consistent bool, threshold int) (*data.Signed, error) {
|
||||
// require role exists in snapshots
|
||||
roleMeta, ok := snapshotMeta[role]
|
||||
|
@ -454,25 +451,19 @@ func (c Client) GetTargetsFile(role string, keyIDs []string, snapshotMeta data.F
|
|||
} else {
|
||||
download = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
size := snapshotMeta[role].Length
|
||||
var s *data.Signed
|
||||
if download {
|
||||
rolePath, err := c.RoleTargetsPath(role, hex.EncodeToString(expectedSha256), consistent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw, err = c.remote.GetMeta(rolePath, snapshotMeta[role].Length)
|
||||
raw, s, err = c.downloadSigned(rolePath, size, expectedSha256)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s = &data.Signed{}
|
||||
err = json.Unmarshal(raw, s)
|
||||
if err != nil {
|
||||
logrus.Error("Error unmarshalling targets file:", err)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
logrus.Debug("using cached ", role)
|
||||
s = old
|
||||
|
|
|
@ -10,6 +10,14 @@ var (
|
|||
ErrInsufficientKeys = errors.New("tuf: insufficient keys to meet threshold")
|
||||
)
|
||||
|
||||
type ErrChecksumMismatch struct {
|
||||
role string
|
||||
}
|
||||
|
||||
func (e ErrChecksumMismatch) Error() string {
|
||||
return fmt.Sprintf("tuf: checksum for %s did not match", e.role)
|
||||
}
|
||||
|
||||
type ErrMissingRemoteMetadata struct {
|
||||
Name string
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ func (k TUFKey) Public() []byte {
|
|||
return k.Value.Public
|
||||
}
|
||||
|
||||
func (k *TUFKey) Private() []byte {
|
||||
func (k TUFKey) Private() []byte {
|
||||
return k.Value.Private
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ var ValidRoles = map[string]string{
|
|||
|
||||
func SetValidRoles(rs map[string]string) {
|
||||
// iterate ValidRoles
|
||||
for k, _ := range ValidRoles {
|
||||
for k := range ValidRoles {
|
||||
if v, ok := rs[k]; ok {
|
||||
ValidRoles[k] = v
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ type Role struct {
|
|||
Name string `json:"name"`
|
||||
Paths []string `json:"paths,omitempty"`
|
||||
PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
func NewRole(name string, threshold int, keyIDs, paths, pathHashPrefixes []string) (*Role, error) {
|
||||
|
|
|
@ -90,6 +90,7 @@ func (s HTTPStore) GetMeta(name string, size int64) ([]byte, error) {
|
|||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, ErrMetaNotFound{}
|
||||
} else if resp.StatusCode != http.StatusOK {
|
||||
logrus.Debugf("received HTTP status %d when requesting %s.", resp.StatusCode, name)
|
||||
return nil, ErrServerUnavailable{code: resp.StatusCode}
|
||||
}
|
||||
if resp.ContentLength > size {
|
||||
|
|
36
vendor/src/github.com/endophage/gotuf/tuf.go
vendored
36
vendor/src/github.com/endophage/gotuf/tuf.go
vendored
|
@ -71,24 +71,46 @@ func NewTufRepo(keysDB *keys.KeyDB, cryptoService signed.CryptoService) *TufRepo
|
|||
}
|
||||
|
||||
// AddBaseKeys is used to add keys to the role in root.json
|
||||
func (tr *TufRepo) AddBaseKeys(role string, keys ...*data.TUFKey) error {
|
||||
func (tr *TufRepo) AddBaseKeys(role string, keys ...data.PublicKey) error {
|
||||
if tr.Root == nil {
|
||||
return ErrNotLoaded{role: "root"}
|
||||
}
|
||||
ids := []string{}
|
||||
for _, k := range keys {
|
||||
// Store only the public portion
|
||||
pubKey := *k
|
||||
pubKey.Value.Private = nil
|
||||
tr.Root.Signed.Keys[pubKey.ID()] = &pubKey
|
||||
tr.keysDB.AddKey(&pubKey)
|
||||
pubKey := data.NewPrivateKey(k.Algorithm(), k.Public(), nil)
|
||||
tr.Root.Signed.Keys[pubKey.ID()] = pubKey
|
||||
tr.keysDB.AddKey(k)
|
||||
tr.Root.Signed.Roles[role].KeyIDs = append(tr.Root.Signed.Roles[role].KeyIDs, pubKey.ID())
|
||||
ids = append(ids, pubKey.ID())
|
||||
}
|
||||
r, err := data.NewRole(
|
||||
role,
|
||||
tr.Root.Signed.Roles[role].Threshold,
|
||||
ids,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tr.keysDB.AddRole(r)
|
||||
tr.Root.Dirty = true
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// RemoveKeys is used to remove keys from the roles in root.json
|
||||
// ReplaceBaseKeys is used to replace all keys for the given role with the new keys
|
||||
func (tr *TufRepo) ReplaceBaseKeys(role string, keys ...data.PublicKey) error {
|
||||
r := tr.keysDB.GetRole(role)
|
||||
err := tr.RemoveBaseKeys(role, r.KeyIDs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tr.AddBaseKeys(role, keys...)
|
||||
}
|
||||
|
||||
// RemoveBaseKeys is used to remove keys from the roles in root.json
|
||||
func (tr *TufRepo) RemoveBaseKeys(role string, keyIDs ...string) error {
|
||||
if tr.Root == nil {
|
||||
return ErrNotLoaded{role: "root"}
|
||||
|
@ -119,7 +141,7 @@ func (tr *TufRepo) RemoveBaseKeys(role string, keyIDs ...string) error {
|
|||
}
|
||||
|
||||
// remove keys no longer in use by any roles
|
||||
for k, _ := range toDelete {
|
||||
for k := range toDelete {
|
||||
delete(tr.Root.Signed.Keys, k)
|
||||
}
|
||||
tr.Root.Dirty = true
|
||||
|
|
Loading…
Reference in a new issue