mirror of
https://github.com/moby/moby.git
synced 2025-08-01 05:47:11 +03:00
revendor notary and wrap friendlier error messages
Signed-off-by: Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
This commit is contained in:
@ -165,7 +165,7 @@ RUN set -x \
|
|||||||
&& rm -rf "$GOPATH"
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
# Install notary server
|
# Install notary server
|
||||||
ENV NOTARY_VERSION docker-v1.10-2
|
ENV NOTARY_VERSION docker-v1.10-3
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& export GOPATH="$(mktemp -d)" \
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||||
|
@ -284,13 +284,15 @@ func notaryError(repoName string, err error) error {
|
|||||||
case signed.ErrInvalidKeyType:
|
case signed.ErrInvalidKeyType:
|
||||||
return fmt.Errorf("Warning: potential malicious behavior - trust data mismatch for remote repository %s: %v", repoName, err)
|
return fmt.Errorf("Warning: potential malicious behavior - trust data mismatch for remote repository %s: %v", repoName, err)
|
||||||
case signed.ErrNoKeys:
|
case signed.ErrNoKeys:
|
||||||
return fmt.Errorf("Error: could not find signing keys for remote repository %s: %v", repoName, err)
|
return fmt.Errorf("Error: could not find signing keys for remote repository %s, or could not decrypt signing key: %v", repoName, err)
|
||||||
case signed.ErrLowVersion:
|
case signed.ErrLowVersion:
|
||||||
return fmt.Errorf("Warning: potential malicious behavior - trust data version is lower than expected for remote repository %s: %v", repoName, err)
|
return fmt.Errorf("Warning: potential malicious behavior - trust data version is lower than expected for remote repository %s: %v", repoName, err)
|
||||||
case signed.ErrInsufficientSignatures:
|
case signed.ErrRoleThreshold:
|
||||||
return fmt.Errorf("Warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v", repoName, err)
|
return fmt.Errorf("Warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v", repoName, err)
|
||||||
case client.ErrRepositoryNotExist:
|
case client.ErrRepositoryNotExist:
|
||||||
return fmt.Errorf("Error: remote trust data repository not initialized for %s: %v", repoName, err)
|
return fmt.Errorf("Error: remote trust data does not exist for %s: %v", repoName, err)
|
||||||
|
case signed.ErrInsufficientSignatures:
|
||||||
|
return fmt.Errorf("Error: could not produce valid signature for %s. If Yubikey was used, was touch input provided?: %v", repoName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -49,7 +49,7 @@ clone git github.com/docker/distribution a7ae88da459b98b481a245e5b1750134724ac67
|
|||||||
clone git github.com/vbatts/tar-split v0.9.11
|
clone git github.com/vbatts/tar-split v0.9.11
|
||||||
|
|
||||||
# get desired notary commit, might also need to be updated in Dockerfile
|
# get desired notary commit, might also need to be updated in Dockerfile
|
||||||
clone git github.com/docker/notary docker-v1.10-2
|
clone git github.com/docker/notary docker-v1.10-3
|
||||||
|
|
||||||
clone git google.golang.org/grpc 174192fc93efcb188fc8f46ca447f0da606b6885 https://github.com/grpc/grpc-go.git
|
clone git google.golang.org/grpc 174192fc93efcb188fc8f46ca447f0da606b6885 https://github.com/grpc/grpc-go.git
|
||||||
clone git github.com/miekg/pkcs11 80f102b5cac759de406949c47f0928b99bd64cdf
|
clone git github.com/miekg/pkcs11 80f102b5cac759de406949c47f0928b99bd64cdf
|
||||||
|
@ -312,7 +312,7 @@ func (s *DockerTrustSuite) TestUntrustedCreate(c *check.C) {
|
|||||||
s.trustedCmd(createCmd)
|
s.trustedCmd(createCmd)
|
||||||
out, _, err := runCommandWithOutput(createCmd)
|
out, _, err := runCommandWithOutput(createCmd)
|
||||||
c.Assert(err, check.Not(check.IsNil))
|
c.Assert(err, check.Not(check.IsNil))
|
||||||
c.Assert(string(out), checker.Contains, "does not have trust data for", check.Commentf("Missing expected output on trusted create:\n%s", out))
|
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))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,7 +402,7 @@ func (s *DockerTrustSuite) TestTrustedCreateFromBadTrustServer(c *check.C) {
|
|||||||
s.trustedCmd(createCmd)
|
s.trustedCmd(createCmd)
|
||||||
out, _, err = runCommandWithOutput(createCmd)
|
out, _, err = runCommandWithOutput(createCmd)
|
||||||
c.Assert(err, check.Not(check.IsNil))
|
c.Assert(err, check.Not(check.IsNil))
|
||||||
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))
|
c.Assert(string(out), checker.Contains, "valid signatures did not meet threshold", check.Commentf("Missing expected output on trusted push:\n%s", out))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ func (s *DockerTrustSuite) TestUntrustedPull(c *check.C) {
|
|||||||
out, _, err := runCommandWithOutput(pullCmd)
|
out, _, err := runCommandWithOutput(pullCmd)
|
||||||
|
|
||||||
c.Assert(err, check.NotNil, check.Commentf(out))
|
c.Assert(err, check.NotNil, check.Commentf(out))
|
||||||
c.Assert(string(out), checker.Contains, "Error: remote trust data repository not initialized", check.Commentf(out))
|
c.Assert(string(out), checker.Contains, "Error: remote trust data does not exist", check.Commentf(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerTrustSuite) TestPullWhenCertExpired(c *check.C) {
|
func (s *DockerTrustSuite) TestPullWhenCertExpired(c *check.C) {
|
||||||
@ -141,7 +141,7 @@ func (s *DockerTrustSuite) TestTrustedPullFromBadTrustServer(c *check.C) {
|
|||||||
out, _, err = runCommandWithOutput(pullCmd)
|
out, _, err = runCommandWithOutput(pullCmd)
|
||||||
|
|
||||||
c.Assert(err, check.NotNil, check.Commentf(out))
|
c.Assert(err, check.NotNil, check.Commentf(out))
|
||||||
c.Assert(string(out), checker.Contains, "failed to validate data with current trusted certificates", check.Commentf(out))
|
c.Assert(string(out), checker.Contains, "valid signatures did not meet threshold", check.Commentf(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerTrustSuite) TestTrustedPullWithExpiredSnapshot(c *check.C) {
|
func (s *DockerTrustSuite) TestTrustedPullWithExpiredSnapshot(c *check.C) {
|
||||||
|
@ -3303,7 +3303,7 @@ func (s *DockerTrustSuite) TestTrustedRunFromBadTrustServer(c *check.C) {
|
|||||||
c.Fatalf("Expected to fail on this run due to different remote data: %s\n%s", err, out)
|
c.Fatalf("Expected to fail on this run due to different remote data: %s\n%s", err, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.Contains(string(out), "failed to validate data with current trusted certificates") {
|
if !strings.Contains(string(out), "valid signatures did not meet threshold") {
|
||||||
c.Fatalf("Missing expected output on trusted push:\n%s", out)
|
c.Fatalf("Missing expected output on trusted push:\n%s", out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ Then please do not report your issue here - you should instead report it to [htt
|
|||||||
Then please do not open an issue here yet - you should first try one of the following support forums:
|
Then please do not open an issue here yet - you should first try one of the following support forums:
|
||||||
|
|
||||||
- irc: #docker-trust on freenode
|
- irc: #docker-trust on freenode
|
||||||
- mailing-list: <trust@dockerproject.org> or https://groups.google.com/a/dockerproject.org/forum/#!forum/trust
|
|
||||||
|
|
||||||
## Reporting an issue properly
|
## Reporting an issue properly
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"dmcgowan",
|
"dmcgowan",
|
||||||
"endophage",
|
"endophage",
|
||||||
"nathanmccauley",
|
"nathanmccauley",
|
||||||
|
"riyazdf",
|
||||||
]
|
]
|
||||||
|
|
||||||
[people]
|
[people]
|
||||||
@ -50,3 +51,8 @@
|
|||||||
Name = "Nathan McCauley"
|
Name = "Nathan McCauley"
|
||||||
Email = "nathan.mccauley@docker.com"
|
Email = "nathan.mccauley@docker.com"
|
||||||
GitHub = "nathanmccauley"
|
GitHub = "nathanmccauley"
|
||||||
|
|
||||||
|
[people.riyazdf]
|
||||||
|
Name = "Riyaz Faizullabhoy"
|
||||||
|
Email = "riyaz@docker.com"
|
||||||
|
GitHub = "riyazdf"
|
||||||
|
19
vendor/src/github.com/docker/notary/Makefile
vendored
19
vendor/src/github.com/docker/notary/Makefile
vendored
@ -92,14 +92,21 @@ build: go_version
|
|||||||
@echo "+ $@"
|
@echo "+ $@"
|
||||||
@go build -tags "${NOTARY_BUILDTAGS}" -v ${GO_LDFLAGS} ./...
|
@go build -tags "${NOTARY_BUILDTAGS}" -v ${GO_LDFLAGS} ./...
|
||||||
|
|
||||||
|
# When running `go test ./...`, it runs all the suites in parallel, which causes
|
||||||
|
# problems when running with a yubikey
|
||||||
test: TESTOPTS =
|
test: TESTOPTS =
|
||||||
test: go_version
|
test: go_version
|
||||||
|
@echo Note: when testing with a yubikey plugged in, make sure to include 'TESTOPTS="-p 1"'
|
||||||
@echo "+ $@ $(TESTOPTS)"
|
@echo "+ $@ $(TESTOPTS)"
|
||||||
|
@echo
|
||||||
go test -tags "${NOTARY_BUILDTAGS}" $(TESTOPTS) ./...
|
go test -tags "${NOTARY_BUILDTAGS}" $(TESTOPTS) ./...
|
||||||
|
|
||||||
|
test-full: TESTOPTS =
|
||||||
test-full: vet lint
|
test-full: vet lint
|
||||||
|
@echo Note: when testing with a yubikey plugged in, make sure to include 'TESTOPTS="-p 1"'
|
||||||
@echo "+ $@"
|
@echo "+ $@"
|
||||||
go test -tags "${NOTARY_BUILDTAGS}" -v ./...
|
@echo
|
||||||
|
go test -tags "${NOTARY_BUILDTAGS}" $(TESTOPTS) -v ./...
|
||||||
|
|
||||||
protos:
|
protos:
|
||||||
@protoc --go_out=plugins=grpc:. proto/*.proto
|
@protoc --go_out=plugins=grpc:. proto/*.proto
|
||||||
@ -118,14 +125,18 @@ gen-cover: go_version
|
|||||||
@mkdir -p "$(COVERDIR)"
|
@mkdir -p "$(COVERDIR)"
|
||||||
$(foreach PKG,$(PKGS),$(call gocover,$(PKG)))
|
$(foreach PKG,$(PKGS),$(call gocover,$(PKG)))
|
||||||
|
|
||||||
|
# Generates the cover binaries and runs them all in serial, so this can be used
|
||||||
|
# run all tests with a yubikey without any problems
|
||||||
cover: GO_EXC := go
|
cover: GO_EXC := go
|
||||||
OPTS = -tags "${NOTARY_BUILDTAGS}" -coverpkg "$(shell ./coverpkg.sh $(1) $(NOTARY_PKG))"
|
OPTS = -tags "${NOTARY_BUILDTAGS}" -coverpkg "$(shell ./coverpkg.sh $(1) $(NOTARY_PKG))"
|
||||||
cover: gen-cover covmerge
|
cover: gen-cover covmerge
|
||||||
@go tool cover -html="$(COVERPROFILE)"
|
@go tool cover -html="$(COVERPROFILE)"
|
||||||
|
|
||||||
# Codecov knows how to merge multiple coverage files
|
# Generates the cover binaries and runs them all in serial, so this can be used
|
||||||
|
# run all tests with a yubikey without any problems
|
||||||
ci: OPTS = -tags "${NOTARY_BUILDTAGS}" -race -coverpkg "$(shell ./coverpkg.sh $(1) $(NOTARY_PKG))"
|
ci: OPTS = -tags "${NOTARY_BUILDTAGS}" -race -coverpkg "$(shell ./coverpkg.sh $(1) $(NOTARY_PKG))"
|
||||||
GO_EXC := godep go
|
GO_EXC := godep go
|
||||||
|
# Codecov knows how to merge multiple coverage files, so covmerge is not needed
|
||||||
ci: gen-cover
|
ci: gen-cover
|
||||||
|
|
||||||
covmerge:
|
covmerge:
|
||||||
@ -151,10 +162,10 @@ notary-dockerfile:
|
|||||||
@docker build --rm --force-rm -t notary .
|
@docker build --rm --force-rm -t notary .
|
||||||
|
|
||||||
server-dockerfile:
|
server-dockerfile:
|
||||||
@docker build --rm --force-rm -f Dockerfile.server -t notary-server .
|
@docker build --rm --force-rm -f server.Dockerfile -t notary-server .
|
||||||
|
|
||||||
signer-dockerfile:
|
signer-dockerfile:
|
||||||
@docker build --rm --force-rm -f Dockerfile.signer -t notary-signer .
|
@docker build --rm --force-rm -f signer.Dockerfile -t notary-signer .
|
||||||
|
|
||||||
docker-images: notary-dockerfile server-dockerfile signer-dockerfile
|
docker-images: notary-dockerfile server-dockerfile signer-dockerfile
|
||||||
|
|
||||||
|
149
vendor/src/github.com/docker/notary/client/client.go
vendored
149
vendor/src/github.com/docker/notary/client/client.go
vendored
@ -419,7 +419,7 @@ func (r *NotaryRepository) RemoveTarget(targetName string, roles ...string) erro
|
|||||||
// subtree and also the "targets/x" subtree, as we will defer parsing it until
|
// 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.
|
// we explicitly reach it in our iteration of the provided list of roles.
|
||||||
func (r *NotaryRepository) ListTargets(roles ...string) ([]*TargetWithRole, error) {
|
func (r *NotaryRepository) ListTargets(roles ...string) ([]*TargetWithRole, error) {
|
||||||
_, err := r.Update()
|
_, err := r.Update(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -479,7 +479,7 @@ func (r *NotaryRepository) listSubtree(targets map[string]*TargetWithRole, role
|
|||||||
// will be returned
|
// will be returned
|
||||||
// See the IMPORTANT section on ListTargets above. Those roles also apply here.
|
// See the IMPORTANT section on ListTargets above. Those roles also apply here.
|
||||||
func (r *NotaryRepository) GetTargetByName(name string, roles ...string) (*TargetWithRole, error) {
|
func (r *NotaryRepository) GetTargetByName(name string, roles ...string) (*TargetWithRole, error) {
|
||||||
c, err := r.Update()
|
c, err := r.Update(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -514,7 +514,7 @@ func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) {
|
|||||||
func (r *NotaryRepository) Publish() error {
|
func (r *NotaryRepository) Publish() error {
|
||||||
var initialPublish bool
|
var initialPublish bool
|
||||||
// update first before publishing
|
// update first before publishing
|
||||||
_, err := r.Update()
|
_, err := r.Update(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the remote is not aware of the repo, then this is being published
|
// 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.
|
// for the first time. Try to load from disk instead for publishing.
|
||||||
@ -555,13 +555,21 @@ func (r *NotaryRepository) Publish() error {
|
|||||||
// we send anything to remote
|
// we send anything to remote
|
||||||
updatedFiles := make(map[string][]byte)
|
updatedFiles := make(map[string][]byte)
|
||||||
|
|
||||||
// check if our root file is nearing expiry. Resign if it is.
|
// check if our root file is nearing expiry or dirty. Resign if it is. If
|
||||||
if nearExpiry(r.tufRepo.Root) || r.tufRepo.Root.Dirty || initialPublish {
|
// root is not dirty but we are publishing for the first time, then just
|
||||||
|
// publish the existing root we have.
|
||||||
|
if nearExpiry(r.tufRepo.Root) || r.tufRepo.Root.Dirty {
|
||||||
rootJSON, err := serializeCanonicalRole(r.tufRepo, data.CanonicalRootRole)
|
rootJSON, err := serializeCanonicalRole(r.tufRepo, data.CanonicalRootRole)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
updatedFiles[data.CanonicalRootRole] = rootJSON
|
updatedFiles[data.CanonicalRootRole] = rootJSON
|
||||||
|
} else if initialPublish {
|
||||||
|
rootJSON, err := r.tufRepo.Root.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
updatedFiles[data.CanonicalRootRole] = rootJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate through all the targets files - if they are dirty, sign and update
|
// iterate through all the targets files - if they are dirty, sign and update
|
||||||
@ -714,75 +722,94 @@ func (r *NotaryRepository) saveMetadata(ignoreSnapshot bool) error {
|
|||||||
return r.fileStore.SetMeta(data.CanonicalSnapshotRole, snapshotJSON)
|
return r.fileStore.SetMeta(data.CanonicalSnapshotRole, snapshotJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns a properly constructed ErrRepositoryNotExist error based on this
|
||||||
|
// repo's information
|
||||||
|
func (r *NotaryRepository) errRepositoryNotExist() error {
|
||||||
|
host := r.baseURL
|
||||||
|
parsed, err := url.Parse(r.baseURL)
|
||||||
|
if err == nil {
|
||||||
|
host = parsed.Host // try to exclude the scheme and any paths
|
||||||
|
}
|
||||||
|
return ErrRepositoryNotExist{remote: host, gun: r.gun}
|
||||||
|
}
|
||||||
|
|
||||||
// Update bootstraps a trust anchor (root.json) before updating all the
|
// Update bootstraps a trust anchor (root.json) before updating all the
|
||||||
// metadata from the repo.
|
// metadata from the repo.
|
||||||
func (r *NotaryRepository) Update() (*tufclient.Client, error) {
|
func (r *NotaryRepository) Update(forWrite bool) (*tufclient.Client, error) {
|
||||||
c, err := r.bootstrapClient()
|
c, err := r.bootstrapClient(forWrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(store.ErrMetaNotFound); ok {
|
if _, ok := err.(store.ErrMetaNotFound); ok {
|
||||||
host := r.baseURL
|
return nil, r.errRepositoryNotExist()
|
||||||
parsed, err := url.Parse(r.baseURL)
|
|
||||||
if err == nil {
|
|
||||||
host = parsed.Host // try to exclude the scheme and any paths
|
|
||||||
}
|
|
||||||
return nil, ErrRepositoryNotExist{remote: host, gun: r.gun}
|
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = c.Update()
|
err = c.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if notFound, ok := err.(store.ErrMetaNotFound); ok && notFound.Resource == data.CanonicalRootRole {
|
||||||
|
return nil, r.errRepositoryNotExist()
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
|
// bootstrapClient attempts to bootstrap a root.json to be used as the trust
|
||||||
var rootJSON []byte
|
// anchor for a repository. The checkInitialized argument indicates whether
|
||||||
remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
|
// we should always attempt to contact the server to determine if the repository
|
||||||
if err == nil {
|
// is initialized or not. If set to true, we will always attempt to download
|
||||||
|
// and return an error if the remote repository errors.
|
||||||
|
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
|
// if remote store successfully set up, try and get root from remote
|
||||||
rootJSON, err = remote.GetMeta("root", maxSize)
|
tmpJSON, err := remote.GetMeta("root", maxSize)
|
||||||
}
|
if err != nil {
|
||||||
|
// we didn't have a root in cache and were unable to load one from
|
||||||
// if remote store couldn't be setup, or we failed to get a root from it
|
// the server. Nothing we can do but error.
|
||||||
// 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.
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
result, cacheErr := r.fileStore.GetMeta("root", maxSize)
|
if cachedRootErr != nil {
|
||||||
if cacheErr != nil {
|
// we always want to use the downloaded root if there was a cache
|
||||||
// if cache didn't return a root, we cannot proceed - just return
|
// error.
|
||||||
// the original error.
|
signedRoot, err = r.validateRoot(tmpJSON)
|
||||||
return nil, err
|
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)
|
err = r.fileStore.SetMeta("root", tmpJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
// if we can't write cache we should still continue, just log error
|
||||||
|
logrus.Errorf("could not save root to cache: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kdb := keys.NewDB()
|
kdb := keys.NewDB()
|
||||||
r.tufRepo = tuf.NewRepo(kdb, r.CryptoService)
|
r.tufRepo = tuf.NewRepo(kdb, r.CryptoService)
|
||||||
|
|
||||||
signedRoot, err := data.RootFromSigned(root)
|
if signedRoot == nil {
|
||||||
if err != nil {
|
return nil, ErrRepoNotInitialized{}
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.tufRepo.SetRoot(signedRoot)
|
err = r.tufRepo.SetRoot(signedRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -796,6 +823,28 @@ func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
|
|||||||
), nil
|
), 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
|
// 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.
|
// 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.
|
// These changes are staged in a changelist until publish is called.
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
// Use this to initialize remote HTTPStores from the config settings
|
// Use this to initialize remote HTTPStores from the config settings
|
||||||
func getRemoteStore(baseURL, gun string, rt http.RoundTripper) (store.RemoteStore, error) {
|
func getRemoteStore(baseURL, gun string, rt http.RoundTripper) (store.RemoteStore, error) {
|
||||||
return store.NewHTTPStore(
|
s, err := store.NewHTTPStore(
|
||||||
baseURL+"/v2/"+gun+"/_trust/tuf/",
|
baseURL+"/v2/"+gun+"/_trust/tuf/",
|
||||||
"",
|
"",
|
||||||
"json",
|
"json",
|
||||||
@ -25,6 +25,10 @@ func getRemoteStore(baseURL, gun string, rt http.RoundTripper) (store.RemoteStor
|
|||||||
"key",
|
"key",
|
||||||
rt,
|
rt,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return store.OfflineStore{}, err
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyChangelist(repo *tuf.Repo, cl changelist.Changelist) error {
|
func applyChangelist(repo *tuf.Repo, cl changelist.Changelist) error {
|
||||||
|
@ -26,7 +26,7 @@ func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper,
|
|||||||
keyStores := []trustmanager.KeyStore{fileKeyStore}
|
keyStores := []trustmanager.KeyStore{fileKeyStore}
|
||||||
yubiKeyStore, _ := yubikey.NewYubiKeyStore(fileKeyStore, retriever)
|
yubiKeyStore, _ := yubikey.NewYubiKeyStore(fileKeyStore, retriever)
|
||||||
if yubiKeyStore != nil {
|
if yubiKeyStore != nil {
|
||||||
keyStores = append(keyStores, yubiKeyStore)
|
keyStores = []trustmanager.KeyStore{yubiKeyStore, fileKeyStore}
|
||||||
}
|
}
|
||||||
|
|
||||||
return repositoryFromKeystores(baseDir, gun, baseURL, rt, keyStores)
|
return repositoryFromKeystores(baseDir, gun, baseURL, rt, keyStores)
|
||||||
|
@ -73,9 +73,9 @@ func (cs *CryptoService) Create(role, algorithm string) (data.PublicKey, error)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPrivateKey returns a private key by ID. It tries to get the key first
|
// GetPrivateKey returns a private key and role if present by ID.
|
||||||
// without a GUN (in which case it's a root key). If that fails, try to get
|
// It tries to get the key first without a GUN (in which case it's a root key).
|
||||||
// the key with the GUN (non-root key).
|
// If that fails, try to get the key with the GUN (non-root key).
|
||||||
// If that fails, then we don't have the key.
|
// If that fails, then we don't have the key.
|
||||||
func (cs *CryptoService) GetPrivateKey(keyID string) (k data.PrivateKey, role string, err error) {
|
func (cs *CryptoService) GetPrivateKey(keyID string) (k data.PrivateKey, role string, err error) {
|
||||||
keyPaths := []string{keyID, filepath.Join(cs.gun, keyID)}
|
keyPaths := []string{keyID, filepath.Join(cs.gun, keyID)}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
notaryserver:
|
notaryserver:
|
||||||
build: .
|
build: .
|
||||||
dockerfile: Dockerfile.server
|
dockerfile: server.Dockerfile
|
||||||
links:
|
links:
|
||||||
- notarymysql
|
- notarymysql
|
||||||
- notarysigner
|
- notarysigner
|
||||||
@ -15,7 +15,7 @@ notarysigner:
|
|||||||
- /dev/bus/usb/003/010:/dev/bus/usb/002/010
|
- /dev/bus/usb/003/010:/dev/bus/usb/002/010
|
||||||
- /var/run/pcscd/pcscd.comm:/var/run/pcscd/pcscd.comm
|
- /var/run/pcscd/pcscd.comm:/var/run/pcscd/pcscd.comm
|
||||||
build: .
|
build: .
|
||||||
dockerfile: Dockerfile.signer
|
dockerfile: signer.Dockerfile
|
||||||
links:
|
links:
|
||||||
- notarymysql
|
- notarymysql
|
||||||
command: -config=fixtures/signer-config.json
|
command: -config=fixtures/signer-config.json
|
||||||
|
@ -129,6 +129,7 @@ func (c Client) checkRoot() error {
|
|||||||
|
|
||||||
// downloadRoot is responsible for downloading the root.json
|
// downloadRoot is responsible for downloading the root.json
|
||||||
func (c *Client) downloadRoot() error {
|
func (c *Client) downloadRoot() error {
|
||||||
|
logrus.Debug("Downloading Root...")
|
||||||
role := data.CanonicalRootRole
|
role := data.CanonicalRootRole
|
||||||
size := maxSize
|
size := maxSize
|
||||||
var expectedSha256 []byte
|
var expectedSha256 []byte
|
||||||
@ -240,7 +241,7 @@ func (c Client) verifyRoot(role string, s *data.Signed, minVersion int) error {
|
|||||||
// Timestamps are special in that we ALWAYS attempt to download and only
|
// Timestamps are special in that we ALWAYS attempt to download and only
|
||||||
// use cache if the download fails (and the cache is still valid).
|
// use cache if the download fails (and the cache is still valid).
|
||||||
func (c *Client) downloadTimestamp() error {
|
func (c *Client) downloadTimestamp() error {
|
||||||
logrus.Debug("downloadTimestamp")
|
logrus.Debug("Downloading Timestamp...")
|
||||||
role := data.CanonicalTimestampRole
|
role := data.CanonicalTimestampRole
|
||||||
|
|
||||||
// We may not have a cached timestamp if this is the first time
|
// We may not have a cached timestamp if this is the first time
|
||||||
@ -299,7 +300,7 @@ func (c *Client) downloadTimestamp() error {
|
|||||||
|
|
||||||
// downloadSnapshot is responsible for downloading the snapshot.json
|
// downloadSnapshot is responsible for downloading the snapshot.json
|
||||||
func (c *Client) downloadSnapshot() error {
|
func (c *Client) downloadSnapshot() error {
|
||||||
logrus.Debug("downloadSnapshot")
|
logrus.Debug("Downloading Snapshot...")
|
||||||
role := data.CanonicalSnapshotRole
|
role := data.CanonicalSnapshotRole
|
||||||
if c.local.Timestamp == nil {
|
if c.local.Timestamp == nil {
|
||||||
return ErrMissingMeta{role: "snapshot"}
|
return ErrMissingMeta{role: "snapshot"}
|
||||||
@ -372,6 +373,7 @@ func (c *Client) downloadSnapshot() error {
|
|||||||
// It uses a pre-order tree traversal as it's necessary to download parents first
|
// It uses a pre-order tree traversal as it's necessary to download parents first
|
||||||
// to obtain the keys to validate children.
|
// to obtain the keys to validate children.
|
||||||
func (c *Client) downloadTargets(role string) error {
|
func (c *Client) downloadTargets(role string) error {
|
||||||
|
logrus.Debug("Downloading Targets...")
|
||||||
stack := utils.NewStack()
|
stack := utils.NewStack()
|
||||||
stack.Push(role)
|
stack.Push(role)
|
||||||
for !stack.Empty() {
|
for !stack.Empty() {
|
||||||
|
@ -43,10 +43,11 @@ func NewRoot(keys map[string]PublicKey, roles map[string]*RootRole, consistent b
|
|||||||
|
|
||||||
// ToSigned partially serializes a SignedRoot for further signing
|
// ToSigned partially serializes a SignedRoot for further signing
|
||||||
func (r SignedRoot) ToSigned() (*Signed, error) {
|
func (r SignedRoot) ToSigned() (*Signed, error) {
|
||||||
s, err := json.MarshalCanonical(r.Signed)
|
s, err := defaultSerializer.MarshalCanonical(r.Signed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// cast into a json.RawMessage
|
||||||
signed := json.RawMessage{}
|
signed := json.RawMessage{}
|
||||||
err = signed.UnmarshalJSON(s)
|
err = signed.UnmarshalJSON(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -60,6 +61,15 @@ func (r SignedRoot) ToSigned() (*Signed, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns the serialized form of SignedRoot as bytes
|
||||||
|
func (r SignedRoot) MarshalJSON() ([]byte, error) {
|
||||||
|
signed, err := r.ToSigned()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return defaultSerializer.Marshal(signed)
|
||||||
|
}
|
||||||
|
|
||||||
// RootFromSigned fully unpacks a Signed object into a SignedRoot
|
// RootFromSigned fully unpacks a Signed object into a SignedRoot
|
||||||
func RootFromSigned(s *Signed) (*SignedRoot, error) {
|
func RootFromSigned(s *Signed) (*SignedRoot, error) {
|
||||||
r := Root{}
|
r := Root{}
|
||||||
|
36
vendor/src/github.com/docker/notary/tuf/data/serializer.go
vendored
Normal file
36
vendor/src/github.com/docker/notary/tuf/data/serializer.go
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import "github.com/jfrazelle/go/canonical/json"
|
||||||
|
|
||||||
|
// Serializer is an interface that can marshal and unmarshal TUF data. This
|
||||||
|
// is expected to be a canonical JSON marshaller
|
||||||
|
type serializer interface {
|
||||||
|
MarshalCanonical(from interface{}) ([]byte, error)
|
||||||
|
Marshal(from interface{}) ([]byte, error)
|
||||||
|
Unmarshal(from []byte, to interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanonicalJSON marshals to and from canonical JSON
|
||||||
|
type canonicalJSON struct{}
|
||||||
|
|
||||||
|
// MarshalCanonical returns the canonical JSON form of a thing
|
||||||
|
func (c canonicalJSON) MarshalCanonical(from interface{}) ([]byte, error) {
|
||||||
|
return json.MarshalCanonical(from)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal returns the regular non-canonical JSON form of a thing
|
||||||
|
func (c canonicalJSON) Marshal(from interface{}) ([]byte, error) {
|
||||||
|
return json.Marshal(from)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals some JSON bytes
|
||||||
|
func (c canonicalJSON) Unmarshal(from []byte, to interface{}) error {
|
||||||
|
return json.Unmarshal(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultSerializer is a canonical JSON serializer
|
||||||
|
var defaultSerializer serializer = canonicalJSON{}
|
||||||
|
|
||||||
|
func setDefaultSerializer(s serializer) {
|
||||||
|
defaultSerializer = s
|
||||||
|
}
|
@ -95,7 +95,7 @@ func (e *Ed25519) GetKey(keyID string) data.PublicKey {
|
|||||||
return data.PublicKeyFromPrivate(e.keys[keyID].privKey)
|
return data.PublicKeyFromPrivate(e.keys[keyID].privKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPrivateKey returns a single private key based on the ID
|
// GetPrivateKey returns a single private key and role if present, based on the ID
|
||||||
func (e *Ed25519) GetPrivateKey(keyID string) (data.PrivateKey, string, error) {
|
func (e *Ed25519) GetPrivateKey(keyID string) (data.PrivateKey, string, error) {
|
||||||
if k, ok := e.keys[keyID]; ok {
|
if k, ok := e.keys[keyID]; ok {
|
||||||
return k.privKey, k.role, nil
|
return k.privKey, k.role, nil
|
||||||
|
@ -118,12 +118,12 @@ func tryUnmarshalError(resp *http.Response, defaultError error) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func translateStatusToError(resp *http.Response) error {
|
func translateStatusToError(resp *http.Response, resource string) error {
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
case http.StatusOK:
|
case http.StatusOK:
|
||||||
return nil
|
return nil
|
||||||
case http.StatusNotFound:
|
case http.StatusNotFound:
|
||||||
return ErrMetaNotFound{}
|
return ErrMetaNotFound{Resource: resource}
|
||||||
case http.StatusBadRequest:
|
case http.StatusBadRequest:
|
||||||
return tryUnmarshalError(resp, ErrInvalidOperation{})
|
return tryUnmarshalError(resp, ErrInvalidOperation{})
|
||||||
default:
|
default:
|
||||||
@ -148,7 +148,7 @@ func (s HTTPStore) GetMeta(name string, size int64) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if err := translateStatusToError(resp); err != nil {
|
if err := translateStatusToError(resp, name); err != nil {
|
||||||
logrus.Debugf("received HTTP status %d when requesting %s.", resp.StatusCode, name)
|
logrus.Debugf("received HTTP status %d when requesting %s.", resp.StatusCode, name)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -179,7 +179,7 @@ func (s HTTPStore) SetMeta(name string, blob []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
return translateStatusToError(resp)
|
return translateStatusToError(resp, "POST "+name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMultiPartMetaRequest builds a request with the provided metadata updates
|
// NewMultiPartMetaRequest builds a request with the provided metadata updates
|
||||||
@ -223,7 +223,8 @@ func (s HTTPStore) SetMultiMeta(metas map[string][]byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
return translateStatusToError(resp)
|
// if this 404's something is pretty wrong
|
||||||
|
return translateStatusToError(resp, "POST metadata endpoint")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s HTTPStore) buildMetaURL(name string) (*url.URL, error) {
|
func (s HTTPStore) buildMetaURL(name string) (*url.URL, error) {
|
||||||
@ -271,7 +272,7 @@ func (s HTTPStore) GetTarget(path string) (io.ReadCloser, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if err := translateStatusToError(resp); err != nil {
|
if err := translateStatusToError(resp, path); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return resp.Body, nil
|
return resp.Body, nil
|
||||||
@ -292,7 +293,7 @@ func (s HTTPStore) GetKey(role string) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if err := translateStatusToError(resp); err != nil {
|
if err := translateStatusToError(resp, role+" key"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
43
vendor/src/github.com/docker/notary/tuf/store/offlinestore.go
vendored
Normal file
43
vendor/src/github.com/docker/notary/tuf/store/offlinestore.go
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrOffline is used to indicate we are operating offline
|
||||||
|
type ErrOffline struct{}
|
||||||
|
|
||||||
|
func (e ErrOffline) Error() string {
|
||||||
|
return "client is offline"
|
||||||
|
}
|
||||||
|
|
||||||
|
var err = ErrOffline{}
|
||||||
|
|
||||||
|
// OfflineStore is to be used as a placeholder for a nil store. It simply
|
||||||
|
// return ErrOffline for every operation
|
||||||
|
type OfflineStore struct{}
|
||||||
|
|
||||||
|
// GetMeta return ErrOffline
|
||||||
|
func (es OfflineStore) GetMeta(name string, size int64) ([]byte, error) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMeta return ErrOffline
|
||||||
|
func (es OfflineStore) SetMeta(name string, blob []byte) error {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMultiMeta return ErrOffline
|
||||||
|
func (es OfflineStore) SetMultiMeta(map[string][]byte) error {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKey return ErrOffline
|
||||||
|
func (es OfflineStore) GetKey(role string) ([]byte, error) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTarget return ErrOffline
|
||||||
|
func (es OfflineStore) GetTarget(path string) (io.ReadCloser, error) {
|
||||||
|
return nil, err
|
||||||
|
}
|
Reference in New Issue
Block a user