Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.

Commit 4c89b1f

Browse files
author
Jess Frazelle
committed
Merge pull request #19355 from riyazdf/notary-revendor
notary revendor into docker
2 parents bca9415 + dd7436c commit 4c89b1f

22 files changed

+245
-82
lines changed

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ RUN set -x \
168168
&& rm -rf "$GOPATH"
169169

170170
# Install notary server
171-
ENV NOTARY_VERSION docker-v1.10-2
171+
ENV NOTARY_VERSION docker-v1.10-3
172172
RUN set -x \
173173
&& export GOPATH="$(mktemp -d)" \
174174
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \

api/client/trust.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -284,13 +284,15 @@ func notaryError(repoName string, err error) error {
284284
case signed.ErrInvalidKeyType:
285285
return fmt.Errorf("Warning: potential malicious behavior - trust data mismatch for remote repository %s: %v", repoName, err)
286286
case signed.ErrNoKeys:
287-
return fmt.Errorf("Error: could not find signing keys for remote repository %s: %v", repoName, err)
287+
return fmt.Errorf("Error: could not find signing keys for remote repository %s, or could not decrypt signing key: %v", repoName, err)
288288
case signed.ErrLowVersion:
289289
return fmt.Errorf("Warning: potential malicious behavior - trust data version is lower than expected for remote repository %s: %v", repoName, err)
290-
case signed.ErrInsufficientSignatures:
290+
case signed.ErrRoleThreshold:
291291
return fmt.Errorf("Warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v", repoName, err)
292292
case client.ErrRepositoryNotExist:
293-
return fmt.Errorf("Error: remote trust data repository not initialized for %s: %v", repoName, err)
293+
return fmt.Errorf("Error: remote trust data does not exist for %s: %v", repoName, err)
294+
case signed.ErrInsufficientSignatures:
295+
return fmt.Errorf("Error: could not produce valid signature for %s. If Yubikey was used, was touch input provided?: %v", repoName, err)
294296
}
295297

296298
return err

hack/vendor.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ clone git github.com/docker/distribution cb08de17d74bef86ce6c5abe8b240e282f5750b
5050
clone git github.com/vbatts/tar-split v0.9.11
5151

5252
# get desired notary commit, might also need to be updated in Dockerfile
53-
clone git github.com/docker/notary docker-v1.10-2
53+
clone git github.com/docker/notary docker-v1.10-3
5454

5555
clone git google.golang.org/grpc 174192fc93efcb188fc8f46ca447f0da606b6885 https://github.com/grpc/grpc-go.git
5656
clone git github.com/miekg/pkcs11 80f102b5cac759de406949c47f0928b99bd64cdf

integration-cli/docker_cli_create_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ func (s *DockerTrustSuite) TestUntrustedCreate(c *check.C) {
312312
s.trustedCmd(createCmd)
313313
out, _, err := runCommandWithOutput(createCmd)
314314
c.Assert(err, check.Not(check.IsNil))
315-
c.Assert(string(out), checker.Contains, "does not have trust data for", check.Commentf("Missing expected output on trusted create:\n%s", out))
315+
c.Assert(string(out), checker.Contains, "trust data unavailable. Has a notary repository been initialized?", check.Commentf("Missing expected output on trusted create:\n%s", out))
316316

317317
}
318318

@@ -402,7 +402,7 @@ func (s *DockerTrustSuite) TestTrustedCreateFromBadTrustServer(c *check.C) {
402402
s.trustedCmd(createCmd)
403403
out, _, err = runCommandWithOutput(createCmd)
404404
c.Assert(err, check.Not(check.IsNil))
405-
c.Assert(string(out), checker.Contains, "failed to validate data with current trusted certificates", check.Commentf("Missing expected output on trusted push:\n%s", out))
405+
c.Assert(string(out), checker.Contains, "valid signatures did not meet threshold", check.Commentf("Missing expected output on trusted push:\n%s", out))
406406

407407
}
408408

integration-cli/docker_cli_pull_trusted_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (s *DockerTrustSuite) TestUntrustedPull(c *check.C) {
5959
out, _, err := runCommandWithOutput(pullCmd)
6060

6161
c.Assert(err, check.NotNil, check.Commentf(out))
62-
c.Assert(string(out), checker.Contains, "Error: remote trust data repository not initialized", check.Commentf(out))
62+
c.Assert(string(out), checker.Contains, "Error: remote trust data does not exist", check.Commentf(out))
6363
}
6464

6565
func (s *DockerTrustSuite) TestPullWhenCertExpired(c *check.C) {
@@ -141,7 +141,7 @@ func (s *DockerTrustSuite) TestTrustedPullFromBadTrustServer(c *check.C) {
141141
out, _, err = runCommandWithOutput(pullCmd)
142142

143143
c.Assert(err, check.NotNil, check.Commentf(out))
144-
c.Assert(string(out), checker.Contains, "failed to validate data with current trusted certificates", check.Commentf(out))
144+
c.Assert(string(out), checker.Contains, "valid signatures did not meet threshold", check.Commentf(out))
145145
}
146146

147147
func (s *DockerTrustSuite) TestTrustedPullWithExpiredSnapshot(c *check.C) {

integration-cli/docker_cli_run_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -3303,7 +3303,7 @@ func (s *DockerTrustSuite) TestTrustedRunFromBadTrustServer(c *check.C) {
33033303
c.Fatalf("Expected to fail on this run due to different remote data: %s\n%s", err, out)
33043304
}
33053305

3306-
if !strings.Contains(string(out), "failed to validate data with current trusted certificates") {
3306+
if !strings.Contains(string(out), "valid signatures did not meet threshold") {
33073307
c.Fatalf("Missing expected output on trusted push:\n%s", out)
33083308
}
33093309
}

vendor/src/github.com/docker/notary/CONTRIBUTING.md

-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ Then please do not report your issue here - you should instead report it to [htt
1919
Then please do not open an issue here yet - you should first try one of the following support forums:
2020

2121
- irc: #docker-trust on freenode
22-
- mailing-list: <trust@dockerproject.org> or https://groups.google.com/a/dockerproject.org/forum/#!forum/trust
2322

2423
## Reporting an issue properly
2524

vendor/src/github.com/docker/notary/MAINTAINERS

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"dmcgowan",
1717
"endophage",
1818
"nathanmccauley",
19+
"riyazdf",
1920
]
2021

2122
[people]
@@ -50,3 +51,8 @@
5051
Name = "Nathan McCauley"
5152
Email = "nathan.mccauley@docker.com"
5253
GitHub = "nathanmccauley"
54+
55+
[people.riyazdf]
56+
Name = "Riyaz Faizullabhoy"
57+
Email = "riyaz@docker.com"
58+
GitHub = "riyazdf"

vendor/src/github.com/docker/notary/Makefile

+15-4
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,21 @@ build: go_version
9292
@echo "+ $@"
9393
@go build -tags "${NOTARY_BUILDTAGS}" -v ${GO_LDFLAGS} ./...
9494

95+
# When running `go test ./...`, it runs all the suites in parallel, which causes
96+
# problems when running with a yubikey
9597
test: TESTOPTS =
9698
test: go_version
99+
@echo Note: when testing with a yubikey plugged in, make sure to include 'TESTOPTS="-p 1"'
97100
@echo "+ $@ $(TESTOPTS)"
101+
@echo
98102
go test -tags "${NOTARY_BUILDTAGS}" $(TESTOPTS) ./...
99103

104+
test-full: TESTOPTS =
100105
test-full: vet lint
106+
@echo Note: when testing with a yubikey plugged in, make sure to include 'TESTOPTS="-p 1"'
101107
@echo "+ $@"
102-
go test -tags "${NOTARY_BUILDTAGS}" -v ./...
108+
@echo
109+
go test -tags "${NOTARY_BUILDTAGS}" $(TESTOPTS) -v ./...
103110

104111
protos:
105112
@protoc --go_out=plugins=grpc:. proto/*.proto
@@ -118,14 +125,18 @@ gen-cover: go_version
118125
@mkdir -p "$(COVERDIR)"
119126
$(foreach PKG,$(PKGS),$(call gocover,$(PKG)))
120127

128+
# Generates the cover binaries and runs them all in serial, so this can be used
129+
# run all tests with a yubikey without any problems
121130
cover: GO_EXC := go
122131
OPTS = -tags "${NOTARY_BUILDTAGS}" -coverpkg "$(shell ./coverpkg.sh $(1) $(NOTARY_PKG))"
123132
cover: gen-cover covmerge
124133
@go tool cover -html="$(COVERPROFILE)"
125134

126-
# Codecov knows how to merge multiple coverage files
135+
# Generates the cover binaries and runs them all in serial, so this can be used
136+
# run all tests with a yubikey without any problems
127137
ci: OPTS = -tags "${NOTARY_BUILDTAGS}" -race -coverpkg "$(shell ./coverpkg.sh $(1) $(NOTARY_PKG))"
128138
GO_EXC := godep go
139+
# Codecov knows how to merge multiple coverage files, so covmerge is not needed
129140
ci: gen-cover
130141

131142
covmerge:
@@ -151,10 +162,10 @@ notary-dockerfile:
151162
@docker build --rm --force-rm -t notary .
152163

153164
server-dockerfile:
154-
@docker build --rm --force-rm -f Dockerfile.server -t notary-server .
165+
@docker build --rm --force-rm -f server.Dockerfile -t notary-server .
155166

156167
signer-dockerfile:
157-
@docker build --rm --force-rm -f Dockerfile.signer -t notary-signer .
168+
@docker build --rm --force-rm -f signer.Dockerfile -t notary-signer .
158169

159170
docker-images: notary-dockerfile server-dockerfile signer-dockerfile
160171

vendor/src/github.com/docker/notary/client/client.go

+98-49
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ func (r *NotaryRepository) RemoveTarget(targetName string, roles ...string) erro
419419
// subtree and also the "targets/x" subtree, as we will defer parsing it until
420420
// we explicitly reach it in our iteration of the provided list of roles.
421421
func (r *NotaryRepository) ListTargets(roles ...string) ([]*TargetWithRole, error) {
422-
_, err := r.Update()
422+
_, err := r.Update(false)
423423
if err != nil {
424424
return nil, err
425425
}
@@ -479,7 +479,7 @@ func (r *NotaryRepository) listSubtree(targets map[string]*TargetWithRole, role
479479
// will be returned
480480
// See the IMPORTANT section on ListTargets above. Those roles also apply here.
481481
func (r *NotaryRepository) GetTargetByName(name string, roles ...string) (*TargetWithRole, error) {
482-
c, err := r.Update()
482+
c, err := r.Update(false)
483483
if err != nil {
484484
return nil, err
485485
}
@@ -514,7 +514,7 @@ func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) {
514514
func (r *NotaryRepository) Publish() error {
515515
var initialPublish bool
516516
// update first before publishing
517-
_, err := r.Update()
517+
_, err := r.Update(true)
518518
if err != nil {
519519
// If the remote is not aware of the repo, then this is being published
520520
// for the first time. Try to load from disk instead for publishing.
@@ -555,13 +555,21 @@ func (r *NotaryRepository) Publish() error {
555555
// we send anything to remote
556556
updatedFiles := make(map[string][]byte)
557557

558-
// check if our root file is nearing expiry. Resign if it is.
559-
if nearExpiry(r.tufRepo.Root) || r.tufRepo.Root.Dirty || initialPublish {
558+
// check if our root file is nearing expiry or dirty. Resign if it is. If
559+
// root is not dirty but we are publishing for the first time, then just
560+
// publish the existing root we have.
561+
if nearExpiry(r.tufRepo.Root) || r.tufRepo.Root.Dirty {
560562
rootJSON, err := serializeCanonicalRole(r.tufRepo, data.CanonicalRootRole)
561563
if err != nil {
562564
return err
563565
}
564566
updatedFiles[data.CanonicalRootRole] = rootJSON
567+
} else if initialPublish {
568+
rootJSON, err := r.tufRepo.Root.MarshalJSON()
569+
if err != nil {
570+
return err
571+
}
572+
updatedFiles[data.CanonicalRootRole] = rootJSON
565573
}
566574

567575
// iterate through all the targets files - if they are dirty, sign and update
@@ -714,75 +722,94 @@ func (r *NotaryRepository) saveMetadata(ignoreSnapshot bool) error {
714722
return r.fileStore.SetMeta(data.CanonicalSnapshotRole, snapshotJSON)
715723
}
716724

725+
// returns a properly constructed ErrRepositoryNotExist error based on this
726+
// repo's information
727+
func (r *NotaryRepository) errRepositoryNotExist() error {
728+
host := r.baseURL
729+
parsed, err := url.Parse(r.baseURL)
730+
if err == nil {
731+
host = parsed.Host // try to exclude the scheme and any paths
732+
}
733+
return ErrRepositoryNotExist{remote: host, gun: r.gun}
734+
}
735+
717736
// Update bootstraps a trust anchor (root.json) before updating all the
718737
// metadata from the repo.
719-
func (r *NotaryRepository) Update() (*tufclient.Client, error) {
720-
c, err := r.bootstrapClient()
738+
func (r *NotaryRepository) Update(forWrite bool) (*tufclient.Client, error) {
739+
c, err := r.bootstrapClient(forWrite)
721740
if err != nil {
722741
if _, ok := err.(store.ErrMetaNotFound); ok {
723-
host := r.baseURL
724-
parsed, err := url.Parse(r.baseURL)
725-
if err == nil {
726-
host = parsed.Host // try to exclude the scheme and any paths
727-
}
728-
return nil, ErrRepositoryNotExist{remote: host, gun: r.gun}
742+
return nil, r.errRepositoryNotExist()
729743
}
730744
return nil, err
731745
}
732746
err = c.Update()
733747
if err != nil {
748+
if notFound, ok := err.(store.ErrMetaNotFound); ok && notFound.Resource == data.CanonicalRootRole {
749+
return nil, r.errRepositoryNotExist()
750+
}
734751
return nil, err
735752
}
736753
return c, nil
737754
}
738755

739-
func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
740-
var rootJSON []byte
741-
remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
742-
if err == nil {
743-
// if remote store successfully set up, try and get root from remote
744-
rootJSON, err = remote.GetMeta("root", maxSize)
756+
// bootstrapClient attempts to bootstrap a root.json to be used as the trust
757+
// anchor for a repository. The checkInitialized argument indicates whether
758+
// we should always attempt to contact the server to determine if the repository
759+
// is initialized or not. If set to true, we will always attempt to download
760+
// and return an error if the remote repository errors.
761+
func (r *NotaryRepository) bootstrapClient(checkInitialized bool) (*tufclient.Client, error) {
762+
var (
763+
rootJSON []byte
764+
err error
765+
signedRoot *data.SignedRoot
766+
)
767+
// try to read root from cache first. We will trust this root
768+
// until we detect a problem during update which will cause
769+
// us to download a new root and perform a rotation.
770+
rootJSON, cachedRootErr := r.fileStore.GetMeta("root", maxSize)
771+
772+
if cachedRootErr == nil {
773+
signedRoot, cachedRootErr = r.validateRoot(rootJSON)
745774
}
746775

747-
// if remote store couldn't be setup, or we failed to get a root from it
748-
// load the root from cache (offline operation)
749-
if err != nil {
750-
if err, ok := err.(store.ErrMetaNotFound); ok {
751-
// if the error was MetaNotFound then we successfully contacted
752-
// the store and it doesn't know about the repo.
753-
return nil, err
754-
}
755-
result, cacheErr := r.fileStore.GetMeta("root", maxSize)
756-
if cacheErr != nil {
757-
// if cache didn't return a root, we cannot proceed - just return
758-
// the original error.
776+
remote, remoteErr := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
777+
if remoteErr != nil {
778+
logrus.Error(remoteErr)
779+
} else if cachedRootErr != nil || checkInitialized {
780+
// remoteErr was nil and we had a cachedRootErr (or are specifically
781+
// checking for initialization of the repo).
782+
783+
// if remote store successfully set up, try and get root from remote
784+
tmpJSON, err := remote.GetMeta("root", maxSize)
785+
if err != nil {
786+
// we didn't have a root in cache and were unable to load one from
787+
// the server. Nothing we can do but error.
759788
return nil, err
760789
}
761-
rootJSON = result
762-
logrus.Debugf(
763-
"Using local cache instead of remote due to failure: %s", err.Error())
764-
}
765-
// can't just unmarshal into SignedRoot because validate root
766-
// needs the root.Signed field to still be []byte for signature
767-
// validation
768-
root := &data.Signed{}
769-
err = json.Unmarshal(rootJSON, root)
770-
if err != nil {
771-
return nil, err
772-
}
790+
if cachedRootErr != nil {
791+
// we always want to use the downloaded root if there was a cache
792+
// error.
793+
signedRoot, err = r.validateRoot(tmpJSON)
794+
if err != nil {
795+
return nil, err
796+
}
773797

774-
err = r.CertManager.ValidateRoot(root, r.gun)
775-
if err != nil {
776-
return nil, err
798+
err = r.fileStore.SetMeta("root", tmpJSON)
799+
if err != nil {
800+
// if we can't write cache we should still continue, just log error
801+
logrus.Errorf("could not save root to cache: %s", err.Error())
802+
}
803+
}
777804
}
778805

779806
kdb := keys.NewDB()
780807
r.tufRepo = tuf.NewRepo(kdb, r.CryptoService)
781808

782-
signedRoot, err := data.RootFromSigned(root)
783-
if err != nil {
784-
return nil, err
809+
if signedRoot == nil {
810+
return nil, ErrRepoNotInitialized{}
785811
}
812+
786813
err = r.tufRepo.SetRoot(signedRoot)
787814
if err != nil {
788815
return nil, err
@@ -796,6 +823,28 @@ func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
796823
), nil
797824
}
798825

826+
// validateRoot MUST only be used during bootstrapping. It will only validate
827+
// signatures of the root based on known keys, not expiry or other metadata.
828+
// This is so that an out of date root can be loaded to be used in a rotation
829+
// should the TUF update process detect a problem.
830+
func (r *NotaryRepository) validateRoot(rootJSON []byte) (*data.SignedRoot, error) {
831+
// can't just unmarshal into SignedRoot because validate root
832+
// needs the root.Signed field to still be []byte for signature
833+
// validation
834+
root := &data.Signed{}
835+
err := json.Unmarshal(rootJSON, root)
836+
if err != nil {
837+
return nil, err
838+
}
839+
840+
err = r.CertManager.ValidateRoot(root, r.gun)
841+
if err != nil {
842+
return nil, err
843+
}
844+
845+
return data.RootFromSigned(root)
846+
}
847+
799848
// RotateKey removes all existing keys associated with the role, and either
800849
// creates and adds one new key or delegates managing the key to the server.
801850
// These changes are staged in a changelist until publish is called.

0 commit comments

Comments
 (0)