mirror of
https://github.com/docker/docs.git
synced 2026-10-31 10:34:26 +03:00
fixing bootstrapClient to prefer cached root
Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
104
client/client.go
104
client/client.go
@@ -420,7 +420,7 @@ func (r *NotaryRepository) RemoveTarget(targetName string, roles ...string) erro
|
||||
// subtree and also the "targets/x" subtree, as we will defer parsing it until
|
||||
// we explicitly reach it in our iteration of the provided list of roles.
|
||||
func (r *NotaryRepository) ListTargets(roles ...string) ([]*TargetWithRole, error) {
|
||||
_, err := r.Update()
|
||||
_, err := r.Update(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -480,7 +480,7 @@ func (r *NotaryRepository) listSubtree(targets map[string]*TargetWithRole, role
|
||||
// will be returned
|
||||
// See the IMPORTANT section on ListTargets above. Those roles also apply here.
|
||||
func (r *NotaryRepository) GetTargetByName(name string, roles ...string) (*TargetWithRole, error) {
|
||||
c, err := r.Update()
|
||||
c, err := r.Update(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -515,7 +515,7 @@ func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) {
|
||||
func (r *NotaryRepository) Publish() error {
|
||||
var initialPublish bool
|
||||
// update first before publishing
|
||||
_, err := r.Update()
|
||||
_, err := r.Update(true)
|
||||
if err != nil {
|
||||
// If the remote is not aware of the repo, then this is being published
|
||||
// for the first time. Try to load from disk instead for publishing.
|
||||
@@ -717,8 +717,8 @@ func (r *NotaryRepository) saveMetadata(ignoreSnapshot bool) error {
|
||||
|
||||
// Update bootstraps a trust anchor (root.json) before updating all the
|
||||
// metadata from the repo.
|
||||
func (r *NotaryRepository) Update() (*tufclient.Client, error) {
|
||||
c, err := r.bootstrapClient()
|
||||
func (r *NotaryRepository) Update(forWrite bool) (*tufclient.Client, error) {
|
||||
c, err := r.bootstrapClient(forWrite)
|
||||
if err != nil {
|
||||
if _, ok := err.(store.ErrMetaNotFound); ok {
|
||||
host := r.baseURL
|
||||
@@ -737,53 +737,49 @@ func (r *NotaryRepository) Update() (*tufclient.Client, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
|
||||
var rootJSON []byte
|
||||
remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
|
||||
if err == nil {
|
||||
func (r *NotaryRepository) bootstrapClient(checkInitialized bool) (*tufclient.Client, error) {
|
||||
var (
|
||||
rootJSON []byte
|
||||
err error
|
||||
signedRoot *data.SignedRoot
|
||||
)
|
||||
// try to read root from cache first. We will trust this root
|
||||
// until we detect a problem during update which will cause
|
||||
// us to download a new root and perform a rotation.
|
||||
rootJSON, cachedRootErr := r.fileStore.GetMeta("root", maxSize)
|
||||
|
||||
if cachedRootErr == nil {
|
||||
signedRoot, cachedRootErr = r.validateRoot(rootJSON)
|
||||
}
|
||||
|
||||
remote, remoteErr := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
|
||||
if remoteErr != nil {
|
||||
logrus.Error(remoteErr)
|
||||
} else if cachedRootErr != nil || checkInitialized {
|
||||
// remoteErr was nil and we had a cachedRootErr (or are specifically
|
||||
// checking for initialization of the repo).
|
||||
|
||||
// if remote store successfully set up, try and get root from remote
|
||||
rootJSON, err = remote.GetMeta("root", maxSize)
|
||||
}
|
||||
|
||||
// if remote store couldn't be setup, or we failed to get a root from it
|
||||
// load the root from cache (offline operation)
|
||||
if err != nil {
|
||||
if err, ok := err.(store.ErrMetaNotFound); ok {
|
||||
// if the error was MetaNotFound then we successfully contacted
|
||||
// the store and it doesn't know about the repo.
|
||||
tmpJSON, err := remote.GetMeta("root", maxSize)
|
||||
if err != nil {
|
||||
// we didn't have a root in cache and were unable to load one from
|
||||
// the server. Nothing we can do but error.
|
||||
return nil, err
|
||||
}
|
||||
result, cacheErr := r.fileStore.GetMeta("root", maxSize)
|
||||
if cacheErr != nil {
|
||||
// if cache didn't return a root, we cannot proceed - just return
|
||||
// the original error.
|
||||
return nil, err
|
||||
if cachedRootErr != nil {
|
||||
// if we entered the remote download code because there was a cache
|
||||
// error and not simply to check the initialization state of the
|
||||
// repo, we want to use the downloaded data as our root.
|
||||
signedRoot, err = r.validateRoot(tmpJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
rootJSON = result
|
||||
logrus.Debugf(
|
||||
"Using local cache instead of remote due to failure: %s", err.Error())
|
||||
}
|
||||
// can't just unmarshal into SignedRoot because validate root
|
||||
// needs the root.Signed field to still be []byte for signature
|
||||
// validation
|
||||
root := &data.Signed{}
|
||||
err = json.Unmarshal(rootJSON, root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = r.CertManager.ValidateRoot(root, r.gun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kdb := keys.NewDB()
|
||||
r.tufRepo = tuf.NewRepo(kdb, r.CryptoService)
|
||||
|
||||
signedRoot, err := data.RootFromSigned(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = r.tufRepo.SetRoot(signedRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -797,6 +793,28 @@ func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
|
||||
), nil
|
||||
}
|
||||
|
||||
// validateRoot MUST only be used during bootstrapping. It will only validate
|
||||
// signatures of the root based on known keys, not expiry or other metadata.
|
||||
// This is so that an out of date root can be loaded to be used in a rotation
|
||||
// should the TUF update process detect a problem.
|
||||
func (r *NotaryRepository) validateRoot(rootJSON []byte) (*data.SignedRoot, error) {
|
||||
// can't just unmarshal into SignedRoot because validate root
|
||||
// needs the root.Signed field to still be []byte for signature
|
||||
// validation
|
||||
root := &data.Signed{}
|
||||
err := json.Unmarshal(rootJSON, root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = r.CertManager.ValidateRoot(root, r.gun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data.RootFromSigned(root)
|
||||
}
|
||||
|
||||
// RotateKey removes all existing keys associated with the role, and either
|
||||
// creates and adds one new key or delegates managing the key to the server.
|
||||
// These changes are staged in a changelist until publish is called.
|
||||
|
||||
Reference in New Issue
Block a user