Signed-off-by: Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
| ... | ... |
@@ -153,11 +153,11 @@ RUN set -x \ |
| 153 | 153 |
&& rm -rf "$GOPATH" |
| 154 | 154 |
|
| 155 | 155 |
# Install notary server |
| 156 |
-ENV NOTARY_COMMIT 30c488b3b4c62fdbc2c1eae7cf3b62ca73f95fad |
|
| 156 |
+ENV NOTARY_VERSION docker-v1.10-1 |
|
| 157 | 157 |
RUN set -x \ |
| 158 | 158 |
&& export GOPATH="$(mktemp -d)" \ |
| 159 | 159 |
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \ |
| 160 |
- && (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_COMMIT") \ |
|
| 160 |
+ && (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \ |
|
| 161 | 161 |
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \ |
| 162 | 162 |
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \ |
| 163 | 163 |
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \ |
| ... | ... |
@@ -278,22 +278,24 @@ func notaryError(repoName string, err error) error {
|
| 278 | 278 |
case *json.SyntaxError: |
| 279 | 279 |
logrus.Debugf("Notary syntax error: %s", err)
|
| 280 | 280 |
return fmt.Errorf("Error: no trust data available for remote repository %s. Try running notary server and setting DOCKER_CONTENT_TRUST_SERVER to its HTTPS address?", repoName)
|
| 281 |
- case client.ErrExpired: |
|
| 281 |
+ case signed.ErrExpired: |
|
| 282 | 282 |
return fmt.Errorf("Error: remote repository %s out-of-date: %v", repoName, err)
|
| 283 | 283 |
case trustmanager.ErrKeyNotFound: |
| 284 | 284 |
return fmt.Errorf("Error: signing keys for remote repository %s not found: %v", repoName, err)
|
| 285 | 285 |
case *net.OpError: |
| 286 | 286 |
return fmt.Errorf("Error: error contacting notary server: %v", err)
|
| 287 | 287 |
case store.ErrMetaNotFound: |
| 288 |
- return fmt.Errorf("Error: trust data missing for remote repository %s: %v", repoName, err)
|
|
| 288 |
+ return fmt.Errorf("Error: trust data missing for remote repository %s or remote repository not found: %v", repoName, err)
|
|
| 289 | 289 |
case signed.ErrInvalidKeyType: |
| 290 |
- return fmt.Errorf("Error: trust data mismatch for remote repository %s, could be malicious behavior: %v", repoName, err)
|
|
| 290 |
+ return fmt.Errorf("Warning: potential malicious behavior - trust data mismatch for remote repository %s: %v", repoName, err)
|
|
| 291 | 291 |
case signed.ErrNoKeys: |
| 292 | 292 |
return fmt.Errorf("Error: could not find signing keys for remote repository %s: %v", repoName, err)
|
| 293 | 293 |
case signed.ErrLowVersion: |
| 294 |
- return fmt.Errorf("Error: trust data version is lower than expected for remote repository %s, could be malicious behavior: %v", repoName, err)
|
|
| 294 |
+ return fmt.Errorf("Warning: potential malicious behavior - trust data version is lower than expected for remote repository %s: %v", repoName, err)
|
|
| 295 | 295 |
case signed.ErrInsufficientSignatures: |
| 296 |
- return fmt.Errorf("Error: trust data has insufficient signatures for remote repository %s, could be malicious behavior: %v", repoName, err)
|
|
| 296 |
+ return fmt.Errorf("Warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v", repoName, err)
|
|
| 297 |
+ case client.ErrRepositoryNotExist: |
|
| 298 |
+ return fmt.Errorf("Error: remote trust data repository not initialized for %s: %v", repoName, err)
|
|
| 297 | 299 |
} |
| 298 | 300 |
|
| 299 | 301 |
return err |
| ... | ... |
@@ -432,7 +434,7 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, |
| 432 | 432 |
|
| 433 | 433 |
repo, err := cli.getNotaryRepository(repoInfo, authConfig) |
| 434 | 434 |
if err != nil {
|
| 435 |
- fmt.Fprintf(cli.out, "Error establishing connection to notary repository, has a notary server been setup and pointed to by the DOCKER_CONTENT_TRUST_SERVER environment variable?: %s\n", err) |
|
| 435 |
+ fmt.Fprintf(cli.out, "Error establishing connection to notary repository: %s\n", err) |
|
| 436 | 436 |
return err |
| 437 | 437 |
} |
| 438 | 438 |
|
| ... | ... |
@@ -454,7 +456,7 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, |
| 454 | 454 |
} |
| 455 | 455 |
|
| 456 | 456 |
err = repo.Publish() |
| 457 |
- if _, ok := err.(*client.ErrRepoNotInitialized); !ok {
|
|
| 457 |
+ if _, ok := err.(client.ErrRepoNotInitialized); !ok {
|
|
| 458 | 458 |
return notaryError(repoInfo.FullName(), err) |
| 459 | 459 |
} |
| 460 | 460 |
|
| ... | ... |
@@ -47,7 +47,7 @@ clone git github.com/docker/distribution 568bf038af6d65b376165d02886b1c7fcaef1f6 |
| 47 | 47 |
clone git github.com/vbatts/tar-split v0.9.11 |
| 48 | 48 |
|
| 49 | 49 |
# get desired notary commit, might also need to be updated in Dockerfile |
| 50 |
-clone git github.com/docker/notary 30c488b3b4c62fdbc2c1eae7cf3b62ca73f95fad |
|
| 50 |
+clone git github.com/docker/notary docker-v1.10-1 |
|
| 51 | 51 |
|
| 52 | 52 |
clone git google.golang.org/grpc 174192fc93efcb188fc8f46ca447f0da606b6885 https://github.com/grpc/grpc-go.git |
| 53 | 53 |
clone git github.com/miekg/pkcs11 80f102b5cac759de406949c47f0928b99bd64cdf |
| ... | ... |
@@ -5805,7 +5805,7 @@ func (s *DockerTrustSuite) TestTrustedBuildUntrustedTag(c *check.C) {
|
| 5805 | 5805 |
c.Fatalf("Expected error on trusted build with untrusted tag: %s\n%s", err, out)
|
| 5806 | 5806 |
} |
| 5807 | 5807 |
|
| 5808 |
- if !strings.Contains(out, fmt.Sprintf("trust data unavailable")) {
|
|
| 5808 |
+ if !strings.Contains(out, "does not have trust data for") {
|
|
| 5809 | 5809 |
c.Fatalf("Unexpected output on trusted build with untrusted tag:\n%s", out)
|
| 5810 | 5810 |
} |
| 5811 | 5811 |
} |
| ... | ... |
@@ -312,7 +312,7 @@ func (s *DockerTrustSuite) TestUntrustedCreate(c *check.C) {
|
| 312 | 312 |
s.trustedCmd(createCmd) |
| 313 | 313 |
out, _, err := runCommandWithOutput(createCmd) |
| 314 | 314 |
c.Assert(err, check.Not(check.IsNil)) |
| 315 |
- c.Assert(string(out), checker.Contains, "trust data unavailable", check.Commentf("Missing expected output on trusted create:\n%s", out))
|
|
| 315 |
+ c.Assert(string(out), checker.Contains, "does not have trust data for", check.Commentf("Missing expected output on trusted create:\n%s", out))
|
|
| 316 | 316 |
|
| 317 | 317 |
} |
| 318 | 318 |
|
| ... | ... |
@@ -58,7 +58,7 @@ func (s *DockerTrustSuite) TestUntrustedPull(c *check.C) {
|
| 58 | 58 |
out, _, err := runCommandWithOutput(pullCmd) |
| 59 | 59 |
|
| 60 | 60 |
c.Assert(err, check.NotNil, check.Commentf(out)) |
| 61 |
- c.Assert(string(out), checker.Contains, "trust data unavailable", check.Commentf(out)) |
|
| 61 |
+ c.Assert(string(out), checker.Contains, "Error: remote trust data repository not initialized", check.Commentf(out)) |
|
| 62 | 62 |
} |
| 63 | 63 |
|
| 64 | 64 |
func (s *DockerTrustSuite) TestPullWhenCertExpired(c *check.C) {
|
| ... | ... |
@@ -3087,7 +3087,7 @@ func (s *DockerTrustSuite) TestUntrustedRun(c *check.C) {
|
| 3087 | 3087 |
c.Fatalf("Error expected when running trusted run with:\n%s", out)
|
| 3088 | 3088 |
} |
| 3089 | 3089 |
|
| 3090 |
- if !strings.Contains(string(out), "trust data unavailable") {
|
|
| 3090 |
+ if !strings.Contains(string(out), "does not have trust data for") {
|
|
| 3091 | 3091 |
c.Fatalf("Missing expected output on trusted run:\n%s", out)
|
| 3092 | 3092 |
} |
| 3093 | 3093 |
} |
| ... | ... |
@@ -23,7 +23,7 @@ RUN softhsm2-util --init-token --slot 0 --label "test_token" --pin $NOTARY_SIGNE |
| 23 | 23 |
ENV NOTARYPKG github.com/docker/notary |
| 24 | 24 |
ENV GOPATH /go/src/${NOTARYPKG}/Godeps/_workspace:$GOPATH
|
| 25 | 25 |
|
| 26 |
-EXPOSE 4443 |
|
| 26 |
+EXPOSE 4444 |
|
| 27 | 27 |
|
| 28 | 28 |
# Copy the local repo to the expected go path |
| 29 | 29 |
COPY . /go/src/github.com/docker/notary |
| ... | ... |
@@ -2,17 +2,18 @@ package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"bytes" |
| 5 |
- "encoding/json" |
|
| 6 |
- "errors" |
|
| 7 | 5 |
"fmt" |
| 8 | 6 |
"io/ioutil" |
| 9 | 7 |
"net/http" |
| 8 |
+ "net/url" |
|
| 10 | 9 |
"os" |
| 11 | 10 |
"path/filepath" |
| 12 | 11 |
"strings" |
| 13 | 12 |
"time" |
| 14 | 13 |
|
| 15 | 14 |
"github.com/Sirupsen/logrus" |
| 15 |
+ "github.com/jfrazelle/go/canonical/json" |
|
| 16 |
+ |
|
| 16 | 17 |
"github.com/docker/notary/certs" |
| 17 | 18 |
"github.com/docker/notary/client/changelist" |
| 18 | 19 |
"github.com/docker/notary/cryptoservice" |
| ... | ... |
@@ -39,19 +40,12 @@ func init() {
|
| 39 | 39 |
) |
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 |
-// ErrRepoNotInitialized is returned when trying to can publish on an uninitialized |
|
| 42 |
+// ErrRepoNotInitialized is returned when trying to publish an uninitialized |
|
| 43 | 43 |
// notary repository |
| 44 | 44 |
type ErrRepoNotInitialized struct{}
|
| 45 | 45 |
|
| 46 |
-// ErrRepoNotInitialized is returned when trying to can publish on an uninitialized |
|
| 47 |
-// notary repository |
|
| 48 |
-func (err *ErrRepoNotInitialized) Error() string {
|
|
| 49 |
- return "Repository has not been initialized" |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-// ErrExpired is returned when the metadata for a role has expired |
|
| 53 |
-type ErrExpired struct {
|
|
| 54 |
- signed.ErrExpired |
|
| 46 |
+func (err ErrRepoNotInitialized) Error() string {
|
|
| 47 |
+ return "repository has not been initialized" |
|
| 55 | 48 |
} |
| 56 | 49 |
|
| 57 | 50 |
// ErrInvalidRemoteRole is returned when the server is requested to manage |
| ... | ... |
@@ -65,14 +59,21 @@ func (e ErrInvalidRemoteRole) Error() string {
|
| 65 | 65 |
"notary does not support the server managing the %s key", e.Role) |
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 |
+// ErrRepositoryNotExist is returned when an action is taken on a remote |
|
| 69 |
+// repository that doesn't exist |
|
| 70 |
+type ErrRepositoryNotExist struct {
|
|
| 71 |
+ remote string |
|
| 72 |
+ gun string |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+func (err ErrRepositoryNotExist) Error() string {
|
|
| 76 |
+ return fmt.Sprintf("%s does not have trust data for %s", err.remote, err.gun)
|
|
| 77 |
+} |
|
| 78 |
+ |
|
| 68 | 79 |
const ( |
| 69 | 80 |
tufDir = "tuf" |
| 70 | 81 |
) |
| 71 | 82 |
|
| 72 |
-// ErrRepositoryNotExist gets returned when trying to make an action over a repository |
|
| 73 |
-/// that doesn't exist. |
|
| 74 |
-var ErrRepositoryNotExist = errors.New("repository does not exist")
|
|
| 75 |
- |
|
| 76 | 83 |
// NotaryRepository stores all the information needed to operate on a notary |
| 77 | 84 |
// repository. |
| 78 | 85 |
type NotaryRepository struct {
|
| ... | ... |
@@ -323,7 +324,7 @@ func (r *NotaryRepository) AddDelegation(name string, threshold int, |
| 323 | 323 |
logrus.Debugf(`Adding delegation "%s" with threshold %d, and %d keys\n`, |
| 324 | 324 |
name, threshold, len(delegationKeys)) |
| 325 | 325 |
|
| 326 |
- tdJSON, err := json.Marshal(&changelist.TufDelegation{
|
|
| 326 |
+ tdJSON, err := json.MarshalCanonical(&changelist.TufDelegation{
|
|
| 327 | 327 |
NewThreshold: threshold, |
| 328 | 328 |
AddKeys: data.KeyList(delegationKeys), |
| 329 | 329 |
AddPaths: paths, |
| ... | ... |
@@ -385,7 +386,7 @@ func (r *NotaryRepository) AddTarget(target *Target, roles ...string) error {
|
| 385 | 385 |
logrus.Debugf("Adding target \"%s\" with sha256 \"%x\" and size %d bytes.\n", target.Name, target.Hashes["sha256"], target.Length)
|
| 386 | 386 |
|
| 387 | 387 |
meta := data.FileMeta{Length: target.Length, Hashes: target.Hashes}
|
| 388 |
- metaJSON, err := json.Marshal(meta) |
|
| 388 |
+ metaJSON, err := json.MarshalCanonical(meta) |
|
| 389 | 389 |
if err != nil {
|
| 390 | 390 |
return err |
| 391 | 391 |
} |
| ... | ... |
@@ -419,16 +420,8 @@ func (r *NotaryRepository) RemoveTarget(targetName string, roles ...string) erro |
| 419 | 419 |
// subtree and also the "targets/x" subtree, as we will defer parsing it until |
| 420 | 420 |
// we explicitly reach it in our iteration of the provided list of roles. |
| 421 | 421 |
func (r *NotaryRepository) ListTargets(roles ...string) ([]*TargetWithRole, error) {
|
| 422 |
- c, err := r.bootstrapClient() |
|
| 423 |
- if err != nil {
|
|
| 424 |
- return nil, err |
|
| 425 |
- } |
|
| 426 |
- |
|
| 427 |
- err = c.Update() |
|
| 422 |
+ _, err := r.Update() |
|
| 428 | 423 |
if err != nil {
|
| 429 |
- if err, ok := err.(signed.ErrExpired); ok {
|
|
| 430 |
- return nil, ErrExpired{err}
|
|
| 431 |
- } |
|
| 432 | 424 |
return nil, err |
| 433 | 425 |
} |
| 434 | 426 |
|
| ... | ... |
@@ -487,16 +480,8 @@ func (r *NotaryRepository) listSubtree(targets map[string]*TargetWithRole, role |
| 487 | 487 |
// will be returned |
| 488 | 488 |
// See the IMPORTANT section on ListTargets above. Those roles also apply here. |
| 489 | 489 |
func (r *NotaryRepository) GetTargetByName(name string, roles ...string) (*TargetWithRole, error) {
|
| 490 |
- c, err := r.bootstrapClient() |
|
| 491 |
- if err != nil {
|
|
| 492 |
- return nil, err |
|
| 493 |
- } |
|
| 494 |
- |
|
| 495 |
- err = c.Update() |
|
| 490 |
+ c, err := r.Update() |
|
| 496 | 491 |
if err != nil {
|
| 497 |
- if err, ok := err.(signed.ErrExpired); ok {
|
|
| 498 |
- return nil, ErrExpired{err}
|
|
| 499 |
- } |
|
| 500 | 492 |
return nil, err |
| 501 | 493 |
} |
| 502 | 494 |
|
| ... | ... |
@@ -529,47 +514,33 @@ func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) {
|
| 529 | 529 |
// Conceptually it performs an operation similar to a `git rebase` |
| 530 | 530 |
func (r *NotaryRepository) Publish() error {
|
| 531 | 531 |
var initialPublish bool |
| 532 |
- // attempt to initialize the repo from the remote store |
|
| 533 |
- c, err := r.bootstrapClient() |
|
| 532 |
+ // update first before publishing |
|
| 533 |
+ _, err := r.Update() |
|
| 534 | 534 |
if err != nil {
|
| 535 |
- if _, ok := err.(store.ErrMetaNotFound); ok {
|
|
| 536 |
- // if the remote store return a 404 (translated into ErrMetaNotFound), |
|
| 537 |
- // there is no trust data for yet. Attempt to load it from disk. |
|
| 535 |
+ // If the remote is not aware of the repo, then this is being published |
|
| 536 |
+ // for the first time. Try to load from disk instead for publishing. |
|
| 537 |
+ if _, ok := err.(ErrRepositoryNotExist); ok {
|
|
| 538 | 538 |
err := r.bootstrapRepo() |
| 539 | 539 |
if err != nil {
|
| 540 |
- // There are lots of reasons there might be an error, such as |
|
| 541 |
- // corrupt metadata. We need better errors from bootstrapRepo. |
|
| 542 | 540 |
logrus.Debugf("Unable to load repository from local files: %s",
|
| 543 | 541 |
err.Error()) |
| 544 | 542 |
if _, ok := err.(store.ErrMetaNotFound); ok {
|
| 545 |
- return &ErrRepoNotInitialized{}
|
|
| 543 |
+ return ErrRepoNotInitialized{}
|
|
| 546 | 544 |
} |
| 547 | 545 |
return err |
| 548 | 546 |
} |
| 549 |
- // We had local data but the server doesn't know about the repo yet, |
|
| 550 |
- // ensure we will push the initial root and targets file. Either or |
|
| 547 |
+ // Ensure we will push the initial root and targets file. Either or |
|
| 551 | 548 |
// both of the root and targets may not be marked as Dirty, since |
| 552 | 549 |
// there may not be any changes that update them, so use a |
| 553 | 550 |
// different boolean. |
| 554 | 551 |
initialPublish = true |
| 555 | 552 |
} else {
|
| 556 |
- // The remote store returned an error other than 404. We're |
|
| 557 |
- // unable to determine if the repo has been initialized or not. |
|
| 553 |
+ // We could not update, so we cannot publish. |
|
| 558 | 554 |
logrus.Error("Could not publish Repository: ", err.Error())
|
| 559 | 555 |
return err |
| 560 | 556 |
} |
| 561 |
- } else {
|
|
| 562 |
- // If we were successfully able to bootstrap the client (which only pulls |
|
| 563 |
- // root.json), update it with the rest of the tuf metadata in |
|
| 564 |
- // preparation for applying the changelist. |
|
| 565 |
- err = c.Update() |
|
| 566 |
- if err != nil {
|
|
| 567 |
- if err, ok := err.(signed.ErrExpired); ok {
|
|
| 568 |
- return ErrExpired{err}
|
|
| 569 |
- } |
|
| 570 |
- return err |
|
| 571 |
- } |
|
| 572 | 557 |
} |
| 558 |
+ |
|
| 573 | 559 |
cl, err := r.GetChangelist() |
| 574 | 560 |
if err != nil {
|
| 575 | 561 |
return err |
| ... | ... |
@@ -719,7 +690,7 @@ func (r *NotaryRepository) saveMetadata(ignoreSnapshot bool) error {
|
| 719 | 719 |
if err != nil {
|
| 720 | 720 |
return err |
| 721 | 721 |
} |
| 722 |
- targetsJSON, err := json.Marshal(signedTargets) |
|
| 722 |
+ targetsJSON, err := json.MarshalCanonical(signedTargets) |
|
| 723 | 723 |
if err != nil {
|
| 724 | 724 |
return err |
| 725 | 725 |
} |
| ... | ... |
@@ -744,6 +715,28 @@ func (r *NotaryRepository) saveMetadata(ignoreSnapshot bool) error {
|
| 744 | 744 |
return r.fileStore.SetMeta(data.CanonicalSnapshotRole, snapshotJSON) |
| 745 | 745 |
} |
| 746 | 746 |
|
| 747 |
+// Update bootstraps a trust anchor (root.json) before updating all the |
|
| 748 |
+// metadata from the repo. |
|
| 749 |
+func (r *NotaryRepository) Update() (*tufclient.Client, error) {
|
|
| 750 |
+ c, err := r.bootstrapClient() |
|
| 751 |
+ if err != nil {
|
|
| 752 |
+ if _, ok := err.(store.ErrMetaNotFound); ok {
|
|
| 753 |
+ host := r.baseURL |
|
| 754 |
+ parsed, err := url.Parse(r.baseURL) |
|
| 755 |
+ if err == nil {
|
|
| 756 |
+ host = parsed.Host // try to exclude the scheme and any paths |
|
| 757 |
+ } |
|
| 758 |
+ return nil, ErrRepositoryNotExist{remote: host, gun: r.gun}
|
|
| 759 |
+ } |
|
| 760 |
+ return nil, err |
|
| 761 |
+ } |
|
| 762 |
+ err = c.Update() |
|
| 763 |
+ if err != nil {
|
|
| 764 |
+ return nil, err |
|
| 765 |
+ } |
|
| 766 |
+ return c, nil |
|
| 767 |
+} |
|
| 768 |
+ |
|
| 747 | 769 |
func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
|
| 748 | 770 |
var rootJSON []byte |
| 749 | 771 |
remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip) |
| ... | ... |
@@ -845,7 +838,7 @@ func (r *NotaryRepository) rootFileKeyChange(role, action string, key data.Publi |
| 845 | 845 |
RoleName: role, |
| 846 | 846 |
Keys: kl, |
| 847 | 847 |
} |
| 848 |
- metaJSON, err := json.Marshal(meta) |
|
| 848 |
+ metaJSON, err := json.MarshalCanonical(meta) |
|
| 849 | 849 |
if err != nil {
|
| 850 | 850 |
return err |
| 851 | 851 |
} |
| ... | ... |
@@ -1,13 +1,14 @@ |
| 1 | 1 |
package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "encoding/json" |
|
| 5 | 4 |
"fmt" |
| 6 | 5 |
"net/http" |
| 7 | 6 |
"path" |
| 8 | 7 |
"time" |
| 9 | 8 |
|
| 10 | 9 |
"github.com/Sirupsen/logrus" |
| 10 |
+ "github.com/jfrazelle/go/canonical/json" |
|
| 11 |
+ |
|
| 11 | 12 |
"github.com/docker/notary/client/changelist" |
| 12 | 13 |
tuf "github.com/docker/notary/tuf" |
| 13 | 14 |
"github.com/docker/notary/tuf/data" |
| ... | ... |
@@ -261,5 +262,5 @@ func serializeCanonicalRole(tufRepo *tuf.Repo, role string) (out []byte, err err |
| 261 | 261 |
return |
| 262 | 262 |
} |
| 263 | 263 |
|
| 264 |
- return json.Marshal(s) |
|
| 264 |
+ return json.MarshalCanonical(s) |
|
| 265 | 265 |
} |
| ... | ... |
@@ -97,7 +97,7 @@ func (c *Client) update() error {
|
| 97 | 97 |
// hash and size in snapshot are unchanged but the root file has expired, |
| 98 | 98 |
// there is little expectation that the situation can be remedied. |
| 99 | 99 |
func (c Client) checkRoot() error {
|
| 100 |
- role := data.RoleName("root")
|
|
| 100 |
+ role := data.CanonicalRootRole |
|
| 101 | 101 |
size := c.local.Snapshot.Signed.Meta[role].Length |
| 102 | 102 |
hashSha256 := c.local.Snapshot.Signed.Meta[role].Hashes["sha256"] |
| 103 | 103 |
|
| ... | ... |
@@ -129,7 +129,7 @@ func (c Client) checkRoot() error {
|
| 129 | 129 |
|
| 130 | 130 |
// downloadRoot is responsible for downloading the root.json |
| 131 | 131 |
func (c *Client) downloadRoot() error {
|
| 132 |
- role := data.RoleName("root")
|
|
| 132 |
+ role := data.CanonicalRootRole |
|
| 133 | 133 |
size := maxSize |
| 134 | 134 |
var expectedSha256 []byte |
| 135 | 135 |
if c.local.Snapshot != nil {
|
| ... | ... |
@@ -241,7 +241,7 @@ func (c Client) verifyRoot(role string, s *data.Signed, minVersion int) error {
|
| 241 | 241 |
// use cache if the download fails (and the cache is still valid). |
| 242 | 242 |
func (c *Client) downloadTimestamp() error {
|
| 243 | 243 |
logrus.Debug("downloadTimestamp")
|
| 244 |
- role := data.RoleName("timestamp")
|
|
| 244 |
+ role := data.CanonicalTimestampRole |
|
| 245 | 245 |
|
| 246 | 246 |
// We may not have a cached timestamp if this is the first time |
| 247 | 247 |
// we're interacting with the repo. This will result in the |
| ... | ... |
@@ -272,7 +272,7 @@ func (c *Client) downloadTimestamp() error {
|
| 272 | 272 |
if err == nil {
|
| 273 | 273 |
// couldn't retrieve data from server and don't have valid |
| 274 | 274 |
// data in cache. |
| 275 |
- return store.ErrMetaNotFound{}
|
|
| 275 |
+ return store.ErrMetaNotFound{Resource: data.CanonicalTimestampRole}
|
|
| 276 | 276 |
} |
| 277 | 277 |
return err |
| 278 | 278 |
} |
| ... | ... |
@@ -300,7 +300,7 @@ func (c *Client) downloadTimestamp() error {
|
| 300 | 300 |
// downloadSnapshot is responsible for downloading the snapshot.json |
| 301 | 301 |
func (c *Client) downloadSnapshot() error {
|
| 302 | 302 |
logrus.Debug("downloadSnapshot")
|
| 303 |
- role := data.RoleName("snapshot")
|
|
| 303 |
+ role := data.CanonicalSnapshotRole |
|
| 304 | 304 |
if c.local.Timestamp == nil {
|
| 305 | 305 |
return ErrMissingMeta{role: "snapshot"}
|
| 306 | 306 |
} |
| ... | ... |
@@ -379,7 +379,6 @@ func (c *Client) downloadTargets(role string) error {
|
| 379 | 379 |
if err != nil {
|
| 380 | 380 |
return err |
| 381 | 381 |
} |
| 382 |
- role = data.RoleName(role) // this will really only do something for base targets role |
|
| 383 | 382 |
if c.local.Snapshot == nil {
|
| 384 | 383 |
return ErrMissingMeta{role: role}
|
| 385 | 384 |
} |
| ... | ... |
@@ -15,17 +15,18 @@ const ( |
| 15 | 15 |
CanonicalTimestampRole = "timestamp" |
| 16 | 16 |
) |
| 17 | 17 |
|
| 18 |
-// ValidRoles holds an overrideable mapping of canonical role names |
|
| 19 |
-// to any custom roles names a user wants to make use of. This allows |
|
| 20 |
-// us to be internally consistent while using different roles in the |
|
| 21 |
-// public TUF files. |
|
| 22 |
-var ValidRoles = map[string]string{
|
|
| 23 |
- CanonicalRootRole: CanonicalRootRole, |
|
| 24 |
- CanonicalTargetsRole: CanonicalTargetsRole, |
|
| 25 |
- CanonicalSnapshotRole: CanonicalSnapshotRole, |
|
| 26 |
- CanonicalTimestampRole: CanonicalTimestampRole, |
|
| 18 |
+// BaseRoles is an easy to iterate list of the top level |
|
| 19 |
+// roles. |
|
| 20 |
+var BaseRoles = []string{
|
|
| 21 |
+ CanonicalRootRole, |
|
| 22 |
+ CanonicalTargetsRole, |
|
| 23 |
+ CanonicalSnapshotRole, |
|
| 24 |
+ CanonicalTimestampRole, |
|
| 27 | 25 |
} |
| 28 | 26 |
|
| 27 |
+// Regex for validating delegation names |
|
| 28 |
+var delegationRegexp = regexp.MustCompile("^[-a-z0-9_/]+$")
|
|
| 29 |
+ |
|
| 29 | 30 |
// ErrNoSuchRole indicates the roles doesn't exist |
| 30 | 31 |
type ErrNoSuchRole struct {
|
| 31 | 32 |
Role string |
| ... | ... |
@@ -50,62 +51,15 @@ func (e ErrInvalidRole) Error() string {
|
| 50 | 50 |
return fmt.Sprintf("tuf: invalid role %s.", e.Role)
|
| 51 | 51 |
} |
| 52 | 52 |
|
| 53 |
-// SetValidRoles is a utility function to override some or all of the roles |
|
| 54 |
-func SetValidRoles(rs map[string]string) {
|
|
| 55 |
- // iterate ValidRoles |
|
| 56 |
- for k := range ValidRoles {
|
|
| 57 |
- if v, ok := rs[k]; ok {
|
|
| 58 |
- ValidRoles[k] = v |
|
| 59 |
- } |
|
| 60 |
- } |
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-// RoleName returns the (possibly overridden) role name for the provided |
|
| 64 |
-// canonical role name |
|
| 65 |
-func RoleName(canonicalRole string) string {
|
|
| 66 |
- if r, ok := ValidRoles[canonicalRole]; ok {
|
|
| 67 |
- return r |
|
| 68 |
- } |
|
| 69 |
- return canonicalRole |
|
| 70 |
-} |
|
| 71 |
- |
|
| 72 |
-// CanonicalRole does a reverse lookup to get the canonical role name |
|
| 73 |
-// from the (possibly overridden) role name |
|
| 74 |
-func CanonicalRole(role string) string {
|
|
| 75 |
- name := strings.ToLower(role) |
|
| 76 |
- if _, ok := ValidRoles[name]; ok {
|
|
| 77 |
- // The canonical version is always lower case |
|
| 78 |
- // se ensure we return name, not role |
|
| 79 |
- return name |
|
| 80 |
- } |
|
| 81 |
- targetsBase := fmt.Sprintf("%s/", ValidRoles[CanonicalTargetsRole])
|
|
| 82 |
- if strings.HasPrefix(name, targetsBase) {
|
|
| 83 |
- role = strings.TrimPrefix(role, targetsBase) |
|
| 84 |
- role = fmt.Sprintf("%s/%s", CanonicalTargetsRole, role)
|
|
| 85 |
- return role |
|
| 86 |
- } |
|
| 87 |
- for r, v := range ValidRoles {
|
|
| 88 |
- if role == v {
|
|
| 89 |
- return r |
|
| 90 |
- } |
|
| 91 |
- } |
|
| 92 |
- return "" |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 | 53 |
// ValidRole only determines the name is semantically |
| 96 | 54 |
// correct. For target delegated roles, it does NOT check |
| 97 | 55 |
// the the appropriate parent roles exist. |
| 98 | 56 |
func ValidRole(name string) bool {
|
| 99 |
- name = strings.ToLower(name) |
|
| 100 |
- if v, ok := ValidRoles[name]; ok {
|
|
| 101 |
- return name == v |
|
| 102 |
- } |
|
| 103 |
- |
|
| 104 | 57 |
if IsDelegation(name) {
|
| 105 | 58 |
return true |
| 106 | 59 |
} |
| 107 | 60 |
|
| 108 |
- for _, v := range ValidRoles {
|
|
| 61 |
+ for _, v := range BaseRoles {
|
|
| 109 | 62 |
if name == v {
|
| 110 | 63 |
return true |
| 111 | 64 |
} |
| ... | ... |
@@ -115,9 +69,8 @@ func ValidRole(name string) bool {
|
| 115 | 115 |
|
| 116 | 116 |
// IsDelegation checks if the role is a delegation or a root role |
| 117 | 117 |
func IsDelegation(role string) bool {
|
| 118 |
- targetsBase := ValidRoles[CanonicalTargetsRole] + "/" |
|
| 118 |
+ targetsBase := CanonicalTargetsRole + "/" |
|
| 119 | 119 |
|
| 120 |
- delegationRegexp := regexp.MustCompile("^[-a-z0-9_/]+$")
|
|
| 121 | 120 |
whitelistedChars := delegationRegexp.MatchString(role) |
| 122 | 121 |
|
| 123 | 122 |
// Limit size of full role string to 255 chars for db column size limit |
| ... | ... |
@@ -27,12 +27,12 @@ type Snapshot struct {
|
| 27 | 27 |
// and targets objects |
| 28 | 28 |
func NewSnapshot(root *Signed, targets *Signed) (*SignedSnapshot, error) {
|
| 29 | 29 |
logrus.Debug("generating new snapshot...")
|
| 30 |
- targetsJSON, err := json.Marshal(targets) |
|
| 30 |
+ targetsJSON, err := json.MarshalCanonical(targets) |
|
| 31 | 31 |
if err != nil {
|
| 32 | 32 |
logrus.Debug("Error Marshalling Targets")
|
| 33 | 33 |
return nil, err |
| 34 | 34 |
} |
| 35 |
- rootJSON, err := json.Marshal(root) |
|
| 35 |
+ rootJSON, err := json.MarshalCanonical(root) |
|
| 36 | 36 |
if err != nil {
|
| 37 | 37 |
logrus.Debug("Error Marshalling Root")
|
| 38 | 38 |
return nil, err |
| ... | ... |
@@ -52,8 +52,8 @@ func NewSnapshot(root *Signed, targets *Signed) (*SignedSnapshot, error) {
|
| 52 | 52 |
Version: 0, |
| 53 | 53 |
Expires: DefaultExpires("snapshot"),
|
| 54 | 54 |
Meta: Files{
|
| 55 |
- ValidRoles["root"]: rootMeta, |
|
| 56 |
- ValidRoles["targets"]: targetsMeta, |
|
| 55 |
+ CanonicalRootRole: rootMeta, |
|
| 56 |
+ CanonicalTargetsRole: targetsMeta, |
|
| 57 | 57 |
}, |
| 58 | 58 |
}, |
| 59 | 59 |
}, nil |
| ... | ... |
@@ -24,7 +24,7 @@ type Timestamp struct {
|
| 24 | 24 |
|
| 25 | 25 |
// NewTimestamp initializes a timestamp with an existing snapshot |
| 26 | 26 |
func NewTimestamp(snapshot *Signed) (*SignedTimestamp, error) {
|
| 27 |
- snapshotJSON, err := json.Marshal(snapshot) |
|
| 27 |
+ snapshotJSON, err := json.MarshalCanonical(snapshot) |
|
| 28 | 28 |
if err != nil {
|
| 29 | 29 |
return nil, err |
| 30 | 30 |
} |
| ... | ... |
@@ -39,7 +39,7 @@ func NewTimestamp(snapshot *Signed) (*SignedTimestamp, error) {
|
| 39 | 39 |
Version: 0, |
| 40 | 40 |
Expires: DefaultExpires("timestamp"),
|
| 41 | 41 |
Meta: Files{
|
| 42 |
- ValidRoles["snapshot"]: snapshotMeta, |
|
| 42 |
+ CanonicalSnapshotRole: snapshotMeta, |
|
| 43 | 43 |
}, |
| 44 | 44 |
}, |
| 45 | 45 |
}, nil |
| ... | ... |
@@ -5,9 +5,9 @@ import "fmt" |
| 5 | 5 |
// ErrMetaNotFound indicates we did not find a particular piece |
| 6 | 6 |
// of metadata in the store |
| 7 | 7 |
type ErrMetaNotFound struct {
|
| 8 |
- Role string |
|
| 8 |
+ Resource string |
|
| 9 | 9 |
} |
| 10 | 10 |
|
| 11 | 11 |
func (err ErrMetaNotFound) Error() string {
|
| 12 |
- return fmt.Sprintf("%s trust data unavailable", err.Role)
|
|
| 12 |
+ return fmt.Sprintf("%s trust data unavailable. Has a notary repository been initialized?", err.Resource)
|
|
| 13 | 13 |
} |
| ... | ... |
@@ -46,7 +46,7 @@ func (f *FilesystemStore) GetMeta(name string, size int64) ([]byte, error) {
|
| 46 | 46 |
meta, err := ioutil.ReadFile(path) |
| 47 | 47 |
if err != nil {
|
| 48 | 48 |
if os.IsNotExist(err) {
|
| 49 |
- err = ErrMetaNotFound{Role: name}
|
|
| 49 |
+ err = ErrMetaNotFound{Resource: name}
|
|
| 50 | 50 |
} |
| 51 | 51 |
return nil, err |
| 52 | 52 |
} |
| ... | ... |
@@ -39,7 +39,7 @@ func (m *memoryStore) GetMeta(name string, size int64) ([]byte, error) {
|
| 39 | 39 |
} |
| 40 | 40 |
return d[:size], nil |
| 41 | 41 |
} |
| 42 |
- return nil, ErrMetaNotFound{}
|
|
| 42 |
+ return nil, ErrMetaNotFound{Resource: name}
|
|
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 | 45 |
func (m *memoryStore) SetMeta(name string, meta []byte) error {
|
| ... | ... |
@@ -75,7 +75,7 @@ func (m *memoryStore) WalkStagedTargets(paths []string, targetsFn targetsWalkFun |
| 75 | 75 |
for _, path := range paths {
|
| 76 | 76 |
dat, ok := m.files[path] |
| 77 | 77 |
if !ok {
|
| 78 |
- return ErrMetaNotFound{}
|
|
| 78 |
+ return ErrMetaNotFound{Resource: path}
|
|
| 79 | 79 |
} |
| 80 | 80 |
meta, err := data.NewFileMeta(bytes.NewReader(dat), "sha256") |
| 81 | 81 |
if err != nil {
|
| ... | ... |
@@ -5,13 +5,14 @@ import ( |
| 5 | 5 |
"bytes" |
| 6 | 6 |
"crypto/sha256" |
| 7 | 7 |
"encoding/hex" |
| 8 |
- "encoding/json" |
|
| 9 | 8 |
"fmt" |
| 10 | 9 |
"path" |
| 11 | 10 |
"strings" |
| 12 | 11 |
"time" |
| 13 | 12 |
|
| 14 | 13 |
"github.com/Sirupsen/logrus" |
| 14 |
+ "github.com/jfrazelle/go/canonical/json" |
|
| 15 |
+ |
|
| 15 | 16 |
"github.com/docker/notary/tuf/data" |
| 16 | 17 |
"github.com/docker/notary/tuf/keys" |
| 17 | 18 |
"github.com/docker/notary/tuf/signed" |
| ... | ... |
@@ -306,7 +307,7 @@ func (tr *Repo) DeleteDelegation(role data.Role) error {
|
| 306 | 306 |
return nil |
| 307 | 307 |
} |
| 308 | 308 |
|
| 309 |
-// InitRepo creates the base files for a repo. It inspects data.ValidRoles and |
|
| 309 |
+// InitRepo creates the base files for a repo. It inspects data.BaseRoles and |
|
| 310 | 310 |
// data.ValidTypes to determine what the role names and filename should be. It |
| 311 | 311 |
// also relies on the keysDB having already been populated with the keys and |
| 312 | 312 |
// roles. |
| ... | ... |
@@ -328,7 +329,7 @@ func (tr *Repo) InitRepo(consistent bool) error {
|
| 328 | 328 |
func (tr *Repo) InitRoot(consistent bool) error {
|
| 329 | 329 |
rootRoles := make(map[string]*data.RootRole) |
| 330 | 330 |
rootKeys := make(map[string]data.PublicKey) |
| 331 |
- for _, r := range data.ValidRoles {
|
|
| 331 |
+ for _, r := range data.BaseRoles {
|
|
| 332 | 332 |
role := tr.keysDB.GetRole(r) |
| 333 | 333 |
if role == nil {
|
| 334 | 334 |
return data.ErrInvalidRole{Role: data.CanonicalRootRole, Reason: "root role not initialized in key database"}
|
| ... | ... |
@@ -352,14 +353,14 @@ func (tr *Repo) InitRoot(consistent bool) error {
|
| 352 | 352 |
// InitTargets initializes an empty targets, and returns the new empty target |
| 353 | 353 |
func (tr *Repo) InitTargets(role string) (*data.SignedTargets, error) {
|
| 354 | 354 |
r := data.Role{Name: role}
|
| 355 |
- if !r.IsDelegation() && data.CanonicalRole(role) != data.CanonicalTargetsRole {
|
|
| 355 |
+ if !r.IsDelegation() && role != data.CanonicalTargetsRole {
|
|
| 356 | 356 |
return nil, data.ErrInvalidRole{
|
| 357 | 357 |
Role: role, |
| 358 | 358 |
Reason: fmt.Sprintf("role is not a valid targets role name: %s", role),
|
| 359 | 359 |
} |
| 360 | 360 |
} |
| 361 | 361 |
targets := data.NewTargets() |
| 362 |
- tr.Targets[data.RoleName(role)] = targets |
|
| 362 |
+ tr.Targets[role] = targets |
|
| 363 | 363 |
return targets, nil |
| 364 | 364 |
} |
| 365 | 365 |
|
| ... | ... |
@@ -373,10 +374,10 @@ func (tr *Repo) InitSnapshot() error {
|
| 373 | 373 |
return err |
| 374 | 374 |
} |
| 375 | 375 |
|
| 376 |
- if _, ok := tr.Targets[data.RoleName(data.CanonicalTargetsRole)]; !ok {
|
|
| 376 |
+ if _, ok := tr.Targets[data.CanonicalTargetsRole]; !ok {
|
|
| 377 | 377 |
return ErrNotLoaded{role: "targets"}
|
| 378 | 378 |
} |
| 379 |
- targets, err := tr.Targets[data.RoleName(data.CanonicalTargetsRole)].ToSigned() |
|
| 379 |
+ targets, err := tr.Targets[data.CanonicalTargetsRole].ToSigned() |
|
| 380 | 380 |
if err != nil {
|
| 381 | 381 |
return err |
| 382 | 382 |
} |
| ... | ... |
@@ -573,7 +574,7 @@ func (tr *Repo) AddTargets(role string, targets data.Files) (data.Files, error) |
| 573 | 573 |
for path, target := range targets {
|
| 574 | 574 |
pathDigest := sha256.Sum256([]byte(path)) |
| 575 | 575 |
pathHex := hex.EncodeToString(pathDigest[:]) |
| 576 |
- if role == data.ValidRoles["targets"] || (r.CheckPaths(path) || r.CheckPrefixes(pathHex)) {
|
|
| 576 |
+ if role == data.CanonicalTargetsRole || (r.CheckPaths(path) || r.CheckPrefixes(pathHex)) {
|
|
| 577 | 577 |
t.Signed.Targets[path] = target |
| 578 | 578 |
} else {
|
| 579 | 579 |
invalid[path] = target |
| ... | ... |
@@ -606,7 +607,7 @@ func (tr *Repo) RemoveTargets(role string, targets ...string) error {
|
| 606 | 606 |
|
| 607 | 607 |
// UpdateSnapshot updates the FileMeta for the given role based on the Signed object |
| 608 | 608 |
func (tr *Repo) UpdateSnapshot(role string, s *data.Signed) error {
|
| 609 |
- jsonData, err := json.Marshal(s) |
|
| 609 |
+ jsonData, err := json.MarshalCanonical(s) |
|
| 610 | 610 |
if err != nil {
|
| 611 | 611 |
return err |
| 612 | 612 |
} |
| ... | ... |
@@ -621,7 +622,7 @@ func (tr *Repo) UpdateSnapshot(role string, s *data.Signed) error {
|
| 621 | 621 |
|
| 622 | 622 |
// UpdateTimestamp updates the snapshot meta in the timestamp based on the Signed object |
| 623 | 623 |
func (tr *Repo) UpdateTimestamp(s *data.Signed) error {
|
| 624 |
- jsonData, err := json.Marshal(s) |
|
| 624 |
+ jsonData, err := json.MarshalCanonical(s) |
|
| 625 | 625 |
if err != nil {
|
| 626 | 626 |
return err |
| 627 | 627 |
} |
| ... | ... |
@@ -639,7 +640,7 @@ func (tr *Repo) SignRoot(expires time.Time) (*data.Signed, error) {
|
| 639 | 639 |
logrus.Debug("signing root...")
|
| 640 | 640 |
tr.Root.Signed.Expires = expires |
| 641 | 641 |
tr.Root.Signed.Version++ |
| 642 |
- root := tr.keysDB.GetRole(data.ValidRoles["root"]) |
|
| 642 |
+ root := tr.keysDB.GetRole(data.CanonicalRootRole) |
|
| 643 | 643 |
signed, err := tr.Root.ToSigned() |
| 644 | 644 |
if err != nil {
|
| 645 | 645 |
return nil, err |
| ... | ... |
@@ -707,7 +708,7 @@ func (tr *Repo) SignSnapshot(expires time.Time) (*data.Signed, error) {
|
| 707 | 707 |
if err != nil {
|
| 708 | 708 |
return nil, err |
| 709 | 709 |
} |
| 710 |
- snapshot := tr.keysDB.GetRole(data.ValidRoles["snapshot"]) |
|
| 710 |
+ snapshot := tr.keysDB.GetRole(data.CanonicalSnapshotRole) |
|
| 711 | 711 |
signed, err = tr.sign(signed, *snapshot) |
| 712 | 712 |
if err != nil {
|
| 713 | 713 |
return nil, err |
| ... | ... |
@@ -733,7 +734,7 @@ func (tr *Repo) SignTimestamp(expires time.Time) (*data.Signed, error) {
|
| 733 | 733 |
if err != nil {
|
| 734 | 734 |
return nil, err |
| 735 | 735 |
} |
| 736 |
- timestamp := tr.keysDB.GetRole(data.ValidRoles["timestamp"]) |
|
| 736 |
+ timestamp := tr.keysDB.GetRole(data.CanonicalTimestampRole) |
|
| 737 | 737 |
signed, err = tr.sign(signed, *timestamp) |
| 738 | 738 |
if err != nil {
|
| 739 | 739 |
return nil, err |