Skip to content

Commit

Permalink
add root status output (sigstore#1404)
Browse files Browse the repository at this point in the history
Signed-off-by: Asra Ali <[email protected]>
  • Loading branch information
asraa authored and mlieberman85 committed May 6, 2022
1 parent ae8a91e commit 48f42c3
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 11 deletions.
18 changes: 17 additions & 1 deletion cmd/cosign/cli/initialize/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package initialize
import (
"context"
_ "embed" // To enable the `go:embed` directive.
"encoding/json"
"fmt"

"github.com/sigstore/cosign/pkg/blob"
"github.com/sigstore/cosign/pkg/cosign/tuf"
Expand All @@ -34,5 +36,19 @@ func DoInitialize(ctx context.Context, root, mirror string) error {
}
}

return tuf.Initialize(ctx, mirror, rootFileBytes)
if err := tuf.Initialize(ctx, mirror, rootFileBytes); err != nil {
return err
}

status, err := tuf.GetRootStatus(ctx)
if err != nil {
return err
}
b, err := json.MarshalIndent(status, "", "\t")
if err != nil {
return err
}

fmt.Println("Root status: \n", string(b))
return nil
}
88 changes: 78 additions & 10 deletions pkg/cosign/tuf/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"embed"
"encoding/json"
"fmt"
"io"
"net/url"
"os"
Expand All @@ -44,10 +45,20 @@ const (
)

type TUF struct {
client *client.Client
targets targetImpl
local client.LocalStore
remote client.RemoteStore
client *client.Client
targets targetImpl
local client.LocalStore
remote client.RemoteStore
embedded bool // local embedded or cache
mirror string // location of mirror
}

// 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"`
}

// RemoteCache contains information to cache on the location of the remote
Expand All @@ -56,6 +67,53 @@ 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 {
return nil, err
}
defer t.Close()
return t.getRootStatus()
}

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{},
}

// Get targets
targets, err := t.client.Targets()
if err != nil {
return nil, err
}
for t := range targets {
status.Targets = append(status.Targets, t)
}

// Get metadata expiration
trustedMeta, err := t.local.GetMeta()
if err != nil {
return nil, errors.Wrap(err, "getting trusted meta")
}
for role, md := range trustedMeta {
expires, err := getExpiration(md)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("getting expiration for %s", role))
}
status.Expiration[role] = expires.Format(time.RFC822)
}

return status, nil
}

// Close closes the local TUF store. Should only be called once per client.
func (t *TUF) Close() error {
return t.local.Close()
Expand Down Expand Up @@ -226,16 +284,24 @@ func embeddedLocalStore() (client.LocalStore, error) {
//go:embed repository
var embeddedRootRepo embed.FS

var isExpiredTimestamp = func(metadata []byte) bool {
func getExpiration(metadata []byte) (*time.Time, error) {
s := &data.Signed{}
if err := json.Unmarshal(metadata, s); err != nil {
return true
return nil, err
}
sm := &data.Timestamp{}
if err := json.Unmarshal(s.Signed, sm); err != nil {
return nil, err
}
return &sm.Expires, nil
}

var isExpiredTimestamp = func(metadata []byte) bool {
expiration, err := getExpiration(metadata)
if err != nil {
return true
}
return time.Until(sm.Expires) <= 0
return time.Until(*expiration) <= 0
}

func getRootKeys(rootFileBytes []byte) ([]*data.PublicKey, int, error) {
Expand Down Expand Up @@ -425,6 +491,7 @@ func newTuf(ctx context.Context) (*TUF, error) {
return nil, err
}
t.targets = newEmbeddedImpl()
t.embedded = true
case statErr != nil:
// Some other error, bail
return nil, statErr
Expand All @@ -435,21 +502,22 @@ func newTuf(ctx context.Context) (*TUF, error) {
return nil, err
}
t.targets = newFileImpl()
t.embedded = false
}
t.local = local

// If there's a remote defined in the cache, use it. Otherwise, use the
// default remote root.
mirror := DefaultRemoteRoot
t.mirror = DefaultRemoteRoot
b, err := os.ReadFile(cachedRemote(rootCacheDir()))
if err == nil {
remoteInfo := remoteCache{}
if err := json.Unmarshal(b, &remoteInfo); err == nil {
mirror = remoteInfo.Mirror
t.mirror = remoteInfo.Mirror
}
}

remote, err := remoteFromMirror(ctx, mirror)
remote, err := remoteFromMirror(ctx, t.mirror)
if err != nil {
return nil, err
}
Expand Down
13 changes: 13 additions & 0 deletions pkg/cosign/tuf/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ import (
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/theupdateframework/go-tuf"
"github.com/theupdateframework/go-tuf/data"
"github.com/theupdateframework/go-tuf/verify"
)

var targets = []string{
"artifact.pub",
"fulcio.crt.pem",
"fulcio_v1.crt.pem",
"ctfe.pub",
Expand Down Expand Up @@ -223,6 +226,16 @@ func checkTargetsAndMeta(t *testing.T, tuf *TUF) {
} else if len(ts) == 0 {
t.Errorf("expected timestamp length of %d, got 0", len(ts))
}

// Check root status matches
status, err := tuf.getRootStatus()
if err != nil {
t.Fatal(err)
}
if !cmp.Equal(targets, status.Targets,
cmpopts.SortSlices(func(a, b string) bool { return a < b })) {
t.Errorf("mismatched targets, expected %s, got %s", targets, status.Targets)
}
}

func dirLen(t *testing.T, td string) int {
Expand Down

0 comments on commit 48f42c3

Please sign in to comment.