From eab616a9a2b59e91f619bcd159fd4207aa585afd Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Fri, 15 Apr 2022 11:32:49 -0500 Subject: [PATCH 1/2] add debug info for tuf update fail Signed-off-by: Asra Ali --- pkg/cosign/tuf/client.go | 97 ++++++++++++++++++++++++++++++++++------ 1 file changed, 84 insertions(+), 13 deletions(-) diff --git a/pkg/cosign/tuf/client.go b/pkg/cosign/tuf/client.go index ab632fac130..aea7de0eb68 100644 --- a/pkg/cosign/tuf/client.go +++ b/pkg/cosign/tuf/client.go @@ -22,6 +22,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "net/url" "os" "path" @@ -55,10 +56,16 @@ type TUF struct { // JSON output representing the configured root status type RootStatus struct { - Local string `json:"local"` - Remote string `json:"remote"` - Expiration map[string]string `json:"expiration"` - Targets []string `json:"targets"` + Local string `json:"local"` + Remote string `json:"remote"` + Metadata map[string]MetadataStatus `json:"metadata"` + Targets []string `json:"targets"` +} + +type MetadataStatus struct { + Version int `json:"version"` + Size int `json:"len"` + Expiration string `json:"expiration"` } type TargetFile struct { @@ -91,16 +98,32 @@ func GetRootStatus(ctx context.Context) (*RootStatus, error) { return t.getRootStatus() } +func getMetadataStatus(b []byte) (*MetadataStatus, error) { + expires, err := getExpiration(b) + if err != nil { + return nil, err + } + version, err := getVersion(b) + if err != nil { + return nil, err + } + return &MetadataStatus{ + Size: len(b), + Expiration: expires.Format(time.RFC822), + Version: int(version), + }, nil +} + func (t *TUF) getRootStatus() (*RootStatus, error) { local := "embedded" if !t.embedded { local = rootCacheDir() } status := &RootStatus{ - Local: local, - Remote: t.mirror, - Expiration: map[string]string{}, - Targets: []string{}, + Local: local, + Remote: t.mirror, + Metadata: make(map[string]MetadataStatus), + Targets: []string{}, } // Get targets @@ -118,11 +141,11 @@ func (t *TUF) getRootStatus() (*RootStatus, error) { return nil, errors.Wrap(err, "getting trusted meta") } for role, md := range trustedMeta { - expires, err := getExpiration(md) + mdStatus, err := getMetadataStatus(md) if err != nil { - return nil, errors.Wrap(err, fmt.Sprintf("getting expiration for %s", role)) + continue } - status.Expiration[role] = expires.Format(time.RFC822) + status.Metadata[role] = *mdStatus } return status, nil @@ -360,6 +383,12 @@ func embeddedLocalStore() (client.LocalStore, error) { return local, nil } +type signedMeta struct { + Type string `json:"_type"` + Expires time.Time `json:"expires"` + Version int64 `json:"version"` +} + //go:embed repository var embeddedRootRepo embed.FS @@ -368,13 +397,25 @@ func getExpiration(metadata []byte) (*time.Time, error) { if err := json.Unmarshal(metadata, s); err != nil { return nil, err } - sm := &data.Timestamp{} + sm := &signedMeta{} if err := json.Unmarshal(s.Signed, sm); err != nil { return nil, err } return &sm.Expires, nil } +func getVersion(metadata []byte) (int64, error) { + s := &data.Signed{} + if err := json.Unmarshal(metadata, s); err != nil { + return 0, err + } + sm := &signedMeta{} + if err := json.Unmarshal(s.Signed, sm); err != nil { + return 0, err + } + return sm.Version, nil +} + var isExpiredTimestamp = func(metadata []byte) bool { expiration, err := getExpiration(metadata) if err != nil { @@ -387,7 +428,37 @@ func (t *TUF) updateMetadataAndDownloadTargets() error { // Download updated targets and cache new metadata and targets in ${TUF_ROOT}. targetFiles, err := t.client.Update() if err != nil && !client.IsLatestSnapshot(err) { - return errors.Wrap(err, "updating tuf metadata") + // Get some extra information for debugging. What was the state of the metadata + // on the remote? + status := struct { + Mirror string `json:"mirror"` + Metadata map[string]MetadataStatus `json:"metadata"` + }{ + Mirror: t.mirror, + Metadata: make(map[string]MetadataStatus), + } + for _, md := range []string{"root.json", "targets.json", "snapshot.json", "timestamp.json"} { + r, _, err := t.remote.GetMeta(md) + if err != nil { + // May be missing, or failed download. + continue + } + defer r.Close() + b, err := ioutil.ReadAll(r) + if err != nil { + continue + } + mdStatus, err := getMetadataStatus(b) + if err != nil { + continue + } + status.Metadata[md] = *mdStatus + } + b, innerErr := json.MarshalIndent(status, "", "\t") + if innerErr != nil { + return innerErr + } + return fmt.Errorf("error updating to TUF remote mirror: %w\nremote status:%s", err, string(b)) } // Update the in-memory targets. From e777d62eac508d38fce8e91fcbb55a60fc63451e Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Fri, 15 Apr 2022 12:33:39 -0500 Subject: [PATCH 2/2] move debugging funcs to top Signed-off-by: Asra Ali --- pkg/cosign/tuf/client.go | 140 ++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/pkg/cosign/tuf/client.go b/pkg/cosign/tuf/client.go index aea7de0eb68..b37902bc3bc 100644 --- a/pkg/cosign/tuf/client.go +++ b/pkg/cosign/tuf/client.go @@ -66,6 +66,7 @@ type MetadataStatus struct { Version int `json:"version"` Size int `json:"len"` Expiration string `json:"expiration"` + Error string `json:"error"` } type TargetFile struct { @@ -82,20 +83,48 @@ type sigstoreCustomMetadata struct { Sigstore customMetadata `json:"sigstore"` } +type signedMeta struct { + Type string `json:"_type"` + Expires time.Time `json:"expires"` + Version int64 `json:"version"` +} + // RemoteCache contains information to cache on the location of the remote // repository. type remoteCache struct { Mirror string `json:"mirror"` } -// GetRootStatus gets the current root status for info logging -func GetRootStatus(ctx context.Context) (*RootStatus, error) { - t, err := NewFromEnv(ctx) - if err != nil { +func getExpiration(metadata []byte) (*time.Time, error) { + s := &data.Signed{} + if err := json.Unmarshal(metadata, s); err != nil { return nil, err } - defer t.Close() - return t.getRootStatus() + sm := &signedMeta{} + if err := json.Unmarshal(s.Signed, sm); err != nil { + return nil, err + } + return &sm.Expires, nil +} + +func getVersion(metadata []byte) (int64, error) { + s := &data.Signed{} + if err := json.Unmarshal(metadata, s); err != nil { + return 0, err + } + sm := &signedMeta{} + if err := json.Unmarshal(s.Signed, sm); err != nil { + return 0, err + } + return sm.Version, nil +} + +var isExpiredTimestamp = func(metadata []byte) bool { + expiration, err := getExpiration(metadata) + if err != nil { + return true + } + return time.Until(*expiration) <= 0 } func getMetadataStatus(b []byte) (*MetadataStatus, error) { @@ -143,6 +172,7 @@ func (t *TUF) getRootStatus() (*RootStatus, error) { for role, md := range trustedMeta { mdStatus, err := getMetadataStatus(md) if err != nil { + status.Metadata[role] = MetadataStatus{Error: err.Error()} continue } status.Metadata[role] = *mdStatus @@ -151,6 +181,29 @@ func (t *TUF) getRootStatus() (*RootStatus, error) { return status, nil } +func getRoot(meta map[string]json.RawMessage) (json.RawMessage, error) { + trustedRoot, ok := meta["root.json"] + if ok { + return trustedRoot, nil + } + // On first initialize, there will be no root in the TUF DB, so read from embedded. + trustedRoot, err := embeddedRootRepo.ReadFile(path.Join("repository", "root.json")) + if err != nil { + return nil, err + } + return trustedRoot, nil +} + +// GetRootStatus gets the current root status for info logging +func GetRootStatus(ctx context.Context) (*RootStatus, error) { + t, err := NewFromEnv(ctx) + if err != nil { + return nil, err + } + defer t.Close() + return t.getRootStatus() +} + // Close closes the local TUF store. Should only be called once per client. func (t *TUF) Close() error { return t.local.Close() @@ -262,19 +315,6 @@ func NewFromEnv(ctx context.Context) (*TUF, error) { return initializeTUF(ctx, embed, mirror, nil, false) } -func getRoot(meta map[string]json.RawMessage) (json.RawMessage, error) { - trustedRoot, ok := meta["root.json"] - if ok { - return trustedRoot, nil - } - // On first initialize, there will be no root in the TUF DB, so read from embedded. - trustedRoot, err := embeddedRootRepo.ReadFile(path.Join("repository", "root.json")) - if err != nil { - return nil, err - } - return trustedRoot, nil -} - func Initialize(ctx context.Context, mirror string, root []byte) error { // Initialize the client. Force an update. t, err := initializeTUF(ctx, false, mirror, root, true) @@ -383,47 +423,6 @@ func embeddedLocalStore() (client.LocalStore, error) { return local, nil } -type signedMeta struct { - Type string `json:"_type"` - Expires time.Time `json:"expires"` - Version int64 `json:"version"` -} - -//go:embed repository -var embeddedRootRepo embed.FS - -func getExpiration(metadata []byte) (*time.Time, error) { - s := &data.Signed{} - if err := json.Unmarshal(metadata, s); err != nil { - return nil, err - } - sm := &signedMeta{} - if err := json.Unmarshal(s.Signed, sm); err != nil { - return nil, err - } - return &sm.Expires, nil -} - -func getVersion(metadata []byte) (int64, error) { - s := &data.Signed{} - if err := json.Unmarshal(metadata, s); err != nil { - return 0, err - } - sm := &signedMeta{} - if err := json.Unmarshal(s.Signed, sm); err != nil { - return 0, err - } - return sm.Version, nil -} - -var isExpiredTimestamp = func(metadata []byte) bool { - expiration, err := getExpiration(metadata) - if err != nil { - return true - } - return time.Until(*expiration) <= 0 -} - func (t *TUF) updateMetadataAndDownloadTargets() error { // Download updated targets and cache new metadata and targets in ${TUF_ROOT}. targetFiles, err := t.client.Update() @@ -476,15 +475,6 @@ func (t *TUF) updateMetadataAndDownloadTargets() error { return nil } -func downloadRemoteTarget(name string, c *client.Client, w io.Writer) error { - dest := targetDestination{} - if err := c.Download(name, &dest); err != nil { - return errors.Wrap(err, "downloading target") - } - _, err := io.Copy(w, &dest.buf) - return err -} - type targetDestination struct { buf bytes.Buffer } @@ -498,6 +488,15 @@ func (t *targetDestination) Delete() error { return nil } +func downloadRemoteTarget(name string, c *client.Client, w io.Writer) error { + dest := targetDestination{} + if err := c.Download(name, &dest); err != nil { + return errors.Wrap(err, "downloading target") + } + _, err := io.Copy(w, &dest.buf) + return err +} + func rootCacheDir() string { rootDir := os.Getenv(TufRootEnv) if rootDir == "" { @@ -539,6 +538,9 @@ func (m *memoryCache) Set(p string, b []byte) error { return nil } +//go:embed repository +var embeddedRootRepo embed.FS + type embedded struct { setImpl }