diff --git a/go.mod b/go.mod index 86e456c7cbc..342c30fc821 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/spf13/viper v1.10.1 github.com/spiffe/go-spiffe/v2 v2.0.0-beta.11 github.com/stretchr/testify v1.7.0 - github.com/theupdateframework/go-tuf v0.0.0-20220127213825-87caa18db2a6 + github.com/theupdateframework/go-tuf v0.0.0-20220211205608-f0c3294f63b9 github.com/xanzy/go-gitlab v0.55.1 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 @@ -57,6 +57,7 @@ require ( github.com/withfig/autocomplete-tools/packages/cobra v0.0.0-20220122124547-31d3821a6898 go.opentelemetry.io/contrib v1.3.0 // indirect go.opentelemetry.io/proto/otlp v0.12.0 // indirect + golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506 // indirect k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect ) diff --git a/go.sum b/go.sum index 4be2e7bdf9a..7fd81060563 100644 --- a/go.sum +++ b/go.sum @@ -1147,6 +1147,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -1658,6 +1659,7 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1667,8 +1669,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= -github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-policy-agent/opa v0.35.0 h1:wsXkq/3JJucRUN4h46pn9Zv6cC6fnHWrVxjgoykxM7o= github.com/open-policy-agent/opa v0.35.0/go.mod h1:xEmekKlk6/c+so5HF9wtPnGPXDfBuBsrMGhSHOHEF+U= @@ -1997,8 +2001,9 @@ github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= @@ -2009,8 +2014,9 @@ github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaE github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/theupdateframework/go-tuf v0.0.0-20211203210025-7ded50136bf9/go.mod h1:n2n6wwC9BEnYS/C/APAtNln0eM5zYAYOkOTx6VEG/mA= -github.com/theupdateframework/go-tuf v0.0.0-20220127213825-87caa18db2a6 h1:DQcx01pdKnazMbVc0aE3u4V9hZ3HLONkOQAZJozdPYk= github.com/theupdateframework/go-tuf v0.0.0-20220127213825-87caa18db2a6/go.mod h1:I0Gs4Tev4hYQ5wiNqN8VJ7qS0gw7KOZNQuckC624RmE= +github.com/theupdateframework/go-tuf v0.0.0-20220211205608-f0c3294f63b9 h1:U8bHY5mmNuZHc3+e7l2/LAmfTk7oiMiB3Qn8fsp4z5g= +github.com/theupdateframework/go-tuf v0.0.0-20220211205608-f0c3294f63b9/go.mod h1:ENa0O55YQfI0U/nn4AAuqPydrbkqQCiq9GDw6YLCyXU= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -2294,8 +2300,9 @@ golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506 h1:EuGTJDfeg/PGZJp3gq1K+14eSLFTsrj1eg8KQuiUyKg= +golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -2390,6 +2397,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -2538,6 +2546,7 @@ golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/pkg/cosign/tuf/client.go b/pkg/cosign/tuf/client.go index aca2afe00f3..73d8105d51a 100644 --- a/pkg/cosign/tuf/client.go +++ b/pkg/cosign/tuf/client.go @@ -31,7 +31,6 @@ import ( "time" "github.com/pkg/errors" - gtuf "github.com/theupdateframework/go-tuf" "github.com/theupdateframework/go-tuf/client" tuf_leveldbstore "github.com/theupdateframework/go-tuf/client/leveldbstore" "github.com/theupdateframework/go-tuf/data" @@ -133,40 +132,71 @@ func (t *TUF) Close() error { return t.local.Close() } -func NewFromEnv(ctx context.Context) (*TUF, error) { - // Initializes a new TUF object from the local cache or defaults. - t, err := newTuf(ctx) +// initializeTUF creates a TUF client using the following params: +// * embed: indicates using the embedded metadata and in-memory file updates. +// When this is false, this uses a filesystem cache. +// * mirror: provides a reference to a remote GCS or HTTP mirror. +// * root: provides an external initial root.json. When this is not provided, this +// defaults to the embedded root.json. +// * forceUpdate: indicates checking the remote for an update, even when the local +// timestamp.json is up to date. +func initializeTUF(ctx context.Context, embed bool, mirror string, root []byte, forceUpdate bool) (*TUF, error) { + t := &TUF{ + mirror: mirror, + embedded: embed, + } + + var err error + if t.embedded { + t.local, err = embeddedLocalStore() + if err != nil { + return nil, err + } + t.targets = newEmbeddedImpl() + } else { + tufDB := filepath.Join(rootCacheDir(), "tuf.db") + t.local, err = localStore(tufDB) + if err != nil { + return nil, err + } + t.targets = newFileImpl() + } + + t.remote, err = remoteFromMirror(ctx, t.mirror) if err != nil { + t.Close() return nil, err } + t.client = client.NewClient(t.local, t.remote) + trustedMeta, err := t.local.GetMeta() if err != nil { + t.Close() return nil, errors.Wrap(err, "getting trusted meta") } + if root == nil { + root, err = getRoot(trustedMeta) + if err != nil { + t.Close() + return nil, errors.Wrap(err, "getting trusted root") + } + } + + if err := t.client.InitLocal(root); err != nil { + t.Close() + return nil, errors.Wrap(err, "unable to initialize client, local cache may be corrupt") + } + // We have our local store, whether it was embedded or not! // Now check to see if it needs to be updated. trustedTimestamp, ok := trustedMeta["timestamp.json"] - if ok && !isExpiredTimestamp(trustedTimestamp) { + if ok && !isExpiredTimestamp(trustedTimestamp) && !forceUpdate { return t, nil } - // We need to update our tufdb. - trustedRoot, err := getRoot(trustedMeta) - if err != nil { - t.Close() - return nil, errors.Wrap(err, "getting trusted root") - } - rootKeys, rootThreshold, err := getRootKeys(trustedRoot) - if err != nil { - t.Close() - return nil, errors.Wrap(err, "bad trusted root") - } - if err := t.client.Init(rootKeys, rootThreshold); err != nil { - t.Close() - return nil, errors.Wrap(err, "unable to initialize client, local cache may be corrupt") - } + // Update when timestamp is out of date. if err := t.updateMetadataAndDownloadTargets(); err != nil { t.Close() return nil, errors.Wrap(err, "updating local metadata and targets") @@ -175,6 +205,39 @@ func NewFromEnv(ctx context.Context) (*TUF, error) { return t, err } +func NewFromEnv(ctx context.Context) (*TUF, error) { + // Get local and mirror from env + tufDB := filepath.Join(rootCacheDir(), "tuf.db") + var embed bool + + // Check for the current local. + _, statErr := os.Stat(tufDB) + switch { + case os.IsNotExist(statErr): + // There is no root at the location, use embedded. + embed = true + case statErr != nil: + // Some other error, bail + return nil, statErr + default: + // There is a root! Happy path. + embed = false + } + + // Check for the current remote mirror. + mirror := DefaultRemoteRoot + b, err := os.ReadFile(cachedRemote(rootCacheDir())) + if err == nil { + remoteInfo := remoteCache{} + if err := json.Unmarshal(b, &remoteInfo); err == nil { + mirror = remoteInfo.Mirror + } + } + + // Initializes a new TUF object from the local cache or defaults. + return initializeTUF(ctx, embed, mirror, nil, false) +} + func getRoot(meta map[string]json.RawMessage) (json.RawMessage, error) { trustedRoot, ok := meta["root.json"] if ok { @@ -189,43 +252,13 @@ func getRoot(meta map[string]json.RawMessage) (json.RawMessage, error) { } func Initialize(ctx context.Context, mirror string, root []byte) error { - // Initialize the remote repository. - remote, err := remoteFromMirror(ctx, mirror) - if err != nil { - return err - } - - // Initialize the local. - tufDB := filepath.Join(rootCacheDir(), "tuf.db") - local, err := localStore(tufDB) + // Initialize the client. Force an update. + t, err := initializeTUF(ctx, false, mirror, root, true) if err != nil { return err } - defer local.Close() + t.Close() - // Initialize the client. - if root == nil { - trustedMeta, err := local.GetMeta() - if err != nil { - return errors.Wrap(err, "getting trusted meta") - } - root, err = getRoot(trustedMeta) - if err != nil { - return errors.Wrap(err, "getting trusted root") - } - } - rootKeys, rootThreshold, err := getRootKeys(root) - if err != nil { - return errors.Wrap(err, "bad trusted root") - } - c := client.NewClient(local, remote) - if err := c.Init(rootKeys, rootThreshold); err != nil { - return errors.Wrap(err, "initializing root") - } - // Timestamp does not need to be saved in memory on Initialize - if err := updateMetadataAndDownloadTargets(c, newFileImpl()); err != nil { - return errors.Wrap(err, "updating local metadata and targets") - } // Store the remote for later. remoteInfo := &remoteCache{Mirror: mirror} b, err := json.Marshal(remoteInfo) @@ -345,27 +378,9 @@ var isExpiredTimestamp = func(metadata []byte) bool { return time.Until(*expiration) <= 0 } -func getRootKeys(rootFileBytes []byte) ([]*data.PublicKey, int, error) { - store := gtuf.MemoryStore(map[string]json.RawMessage{"root.json": rootFileBytes}, nil) - repo, err := gtuf.NewRepo(store) - if err != nil { - return nil, 0, err - } - rootKeys, err := repo.RootKeys() - if err != nil { - return nil, 0, err - } - rootThreshold, err := repo.GetThreshold("root") - return rootKeys, rootThreshold, err -} - func (t *TUF) updateMetadataAndDownloadTargets() error { - return updateMetadataAndDownloadTargets(t.client, t.targets) -} - -func updateMetadataAndDownloadTargets(c *client.Client, t targetImpl) error { // Download updated targets and cache new metadata and targets in ${TUF_ROOT}. - targetFiles, err := c.Update() + targetFiles, err := t.client.Update() if err != nil && !client.IsLatestSnapshot(err) { return errors.Wrap(err, "updating tuf metadata") } @@ -374,10 +389,10 @@ func updateMetadataAndDownloadTargets(c *client.Client, t targetImpl) error { // If the cache directory is enabled, update that too. for name := range targetFiles { buf := bytes.Buffer{} - if err := downloadRemoteTarget(name, c, &buf); err != nil { + if err := downloadRemoteTarget(name, t.client, &buf); err != nil { return err } - if err := t.Set(name, buf.Bytes()); err != nil { + if err := t.targets.Set(name, buf.Bytes()); err != nil { return err } } @@ -517,57 +532,6 @@ func newFileImpl() targetImpl { return f } -func newTuf(ctx context.Context) (*TUF, error) { - t := &TUF{} - tufDB := filepath.Join(rootCacheDir(), "tuf.db") - var local client.LocalStore - var err error - - _, statErr := os.Stat(tufDB) - switch { - case os.IsNotExist(statErr): - // There is no root at the location, try embedded - local, err = embeddedLocalStore() - if err != nil { - return nil, err - } - t.targets = newEmbeddedImpl() - t.embedded = true - case statErr != nil: - // Some other error, bail - return nil, statErr - default: - // There is a root! Happy path. - local, err = localStore(tufDB) - if err != nil { - 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. - t.mirror = DefaultRemoteRoot - b, err := os.ReadFile(cachedRemote(rootCacheDir())) - if err == nil { - remoteInfo := remoteCache{} - if err := json.Unmarshal(b, &remoteInfo); err == nil { - t.mirror = remoteInfo.Mirror - } - } - - remote, err := remoteFromMirror(ctx, t.mirror) - if err != nil { - return nil, err - } - t.remote = remote - - t.client = client.NewClient(local, t.remote) - return t, nil -} - func remoteFromMirror(ctx context.Context, mirror string) (client.RemoteStore, error) { if _, parseErr := url.ParseRequestURI(mirror); parseErr != nil { return GcsRemoteStore(ctx, mirror, nil, nil)