Skip to content

Commit 4c76ff3

Browse files
authored
'cosign init' minor enhancements (file or URL root, write to $HOME/.sigstore) (#530)
* make minor changes to cosign init Signed-off-by: Asra Ali <[email protected]> * use gcs root Signed-off-by: Asra Ali <[email protected]> * also pin sha Signed-off-by: Asra Ali <[email protected]> * embed initial root Signed-off-by: Asra Ali <[email protected]> * remove sha because of embedded root Signed-off-by: Asra Ali <[email protected]>
1 parent a7aff49 commit 4c76ff3

File tree

6 files changed

+202
-105
lines changed

6 files changed

+202
-105
lines changed

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,5 @@
2424
*.libfuzzer
2525
*fuzz.a
2626

27-
# Root metadata
28-
*.sigstore/root/
29-
3027
bin*
3128
dist/

.sigstore/keys.json

Lines changed: 0 additions & 57 deletions
This file was deleted.

cmd/cosign/cli/1.root.json

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
5+
"sig": "30450221008a35d51da0f845301a5eac98ad0df00a934f59b709c1eaf81c86be734d9356f80220742942325599749800f52675f6efe124345980a2a636c0dc76f9caf9fc3123b0"
6+
},
7+
{
8+
"keyid": "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
9+
"sig": "3045022100ef9157ece2a09baec1eab80adfc00b04da20b1f9a0d1b47c5dabc4506719ef2c022074f72acd57398e4ddc8c2a5040df902961e9615dca48f3fbe38cbb506e500066"
10+
},
11+
{
12+
"keyid": "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
13+
"sig": "30450220420fdc9a09cd069b8b15fd8db9cedf7d0dee75871bd1cfee77c926d4120a770002210097553b5ad0d6b4a13902ed37509638bb63a9009f78230cd56c802909ffbfead7"
14+
},
15+
{
16+
"keyid": "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
17+
"sig": "304502202aaf32e66f90752f658672b085ecfe45cc1ad31ee6cf5c9ad05f3267685f8d88022100b5df02acdaa371123db9d7a42219553fe079b230b168833e951be7ee56ded347"
18+
},
19+
{
20+
"keyid": "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209",
21+
"sig": "304402205d420c7d05c58980c1c9f7d221f53b5334aae27a447d2a91c2ceddd685269749022039ec83e51f8e1779d7f0142dfa4a5bbecfe327fc0b91b7416090fea2416fd53a"
22+
}
23+
],
24+
"signed": {
25+
"_type": "root",
26+
"consistent_snapshot": false,
27+
"expires": "2021-12-18T13:28:12.99008-06:00",
28+
"keys": {
29+
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97": {
30+
"keyid_hash_algorithms": [
31+
"sha256",
32+
"sha512"
33+
],
34+
"keytype": "ecdsa-sha2-nistp256",
35+
"keyval": {
36+
"public": "04cbc5cab2684160323c25cd06c3307178a6b1d1c9b949328453ae473c5ba7527e35b13f298b41633382241f3fd8526c262d43b45adee5c618fa0642c82b8a9803"
37+
},
38+
"scheme": "ecdsa-sha2-nistp256"
39+
},
40+
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62": {
41+
"keyid_hash_algorithms": [
42+
"sha256",
43+
"sha512"
44+
],
45+
"keytype": "ecdsa-sha2-nistp256",
46+
"keyval": {
47+
"public": "04a71aacd835dc170ba6db3fa33a1a33dee751d4f8b0217b805b9bd3242921ee93672fdcfd840576c5bb0dc0ed815edf394c1ee48c2b5e02485e59bfc512f3adc7"
48+
},
49+
"scheme": "ecdsa-sha2-nistp256"
50+
},
51+
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b": {
52+
"keyid_hash_algorithms": [
53+
"sha256",
54+
"sha512"
55+
],
56+
"keytype": "ecdsa-sha2-nistp256",
57+
"keyval": {
58+
"public": "04117b33dd265715bf23315e368faa499728db8d1f0a377070a1c7b1aba2cc21be6ab1628e42f2cdd7a35479f2dce07b303a8ba646c55569a8d2a504ba7e86e447"
59+
},
60+
"scheme": "ecdsa-sha2-nistp256"
61+
},
62+
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb": {
63+
"keyid_hash_algorithms": [
64+
"sha256",
65+
"sha512"
66+
],
67+
"keytype": "ecdsa-sha2-nistp256",
68+
"keyval": {
69+
"public": "04cc1cd53a61c23e88cc54b488dfae168a257c34fac3e88811c55962b24cffbfecb724447999c54670e365883716302e49da57c79a33cd3e16f81fbc66f0bcdf48"
70+
},
71+
"scheme": "ecdsa-sha2-nistp256"
72+
},
73+
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209": {
74+
"keyid_hash_algorithms": [
75+
"sha256",
76+
"sha512"
77+
],
78+
"keytype": "ecdsa-sha2-nistp256",
79+
"keyval": {
80+
"public": "048a78a44ac01099890d787e5e62afc29c8ccb69a70ec6549a6b04033b0a8acbfb42ab1ab9c713d225cdb52b858886cf46c8e90a7f3b9e6371882f370c259e1c5b"
81+
},
82+
"scheme": "ecdsa-sha2-nistp256"
83+
}
84+
},
85+
"roles": {
86+
"root": {
87+
"keyids": [
88+
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
89+
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
90+
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
91+
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
92+
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209"
93+
],
94+
"threshold": 3
95+
},
96+
"snapshot": {
97+
"keyids": [
98+
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
99+
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
100+
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
101+
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
102+
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209"
103+
],
104+
"threshold": 3
105+
},
106+
"targets": {
107+
"keyids": [
108+
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
109+
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
110+
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
111+
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
112+
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209"
113+
],
114+
"threshold": 3
115+
},
116+
"timestamp": {
117+
"keyids": [
118+
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
119+
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
120+
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
121+
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
122+
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209"
123+
],
124+
"threshold": 3
125+
}
126+
},
127+
"spec_version": "1.0",
128+
"version": 1
129+
}
130+
}

cmd/cosign/cli/init.go

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,49 @@ package cli
1717

1818
import (
1919
"context"
20+
_ "embed" // To enable the `go:embed` directive.
2021
"flag"
22+
"io/ioutil"
23+
"net/http"
24+
"path/filepath"
25+
"strings"
2126

2227
"github.com/peterbourgon/ff/v3/ffcli"
2328
ctuf "github.com/sigstore/cosign/pkg/cosign/tuf"
2429
)
2530

31+
//go:embed 1.root.json
32+
var initialRoot string
33+
34+
func loadFileOrURL(fileRef string) ([]byte, error) {
35+
var raw []byte
36+
var err error
37+
if strings.HasPrefix(fileRef, "http://") || strings.HasPrefix(fileRef, "https://") {
38+
// #nosec G107
39+
resp, err := http.Get(fileRef)
40+
if err != nil {
41+
return nil, err
42+
}
43+
defer resp.Body.Close()
44+
raw, err = ioutil.ReadAll(resp.Body)
45+
if err != nil {
46+
return nil, err
47+
}
48+
} else {
49+
raw, err = ioutil.ReadFile(filepath.Clean(fileRef))
50+
if err != nil {
51+
return nil, err
52+
}
53+
}
54+
return raw, nil
55+
}
56+
2657
func Init() *ffcli.Command {
2758
var (
2859
flagset = flag.NewFlagSet("cosign init", flag.ExitOnError)
2960
// TODO: Support HTTP mirrors as well
3061
mirror = flagset.String("mirror", "sigstore-tuf-root", "GCS bucket to a SigStore TUF repository.")
31-
root = flagset.String("root", ".sigstore/keys.json", "path to trusted initial root.")
62+
root = flagset.String("root", "", "path to trusted initial root. defaults to embedded root.")
3263
threshold = flagset.Int("threshold", 3, "threshold of root key signers")
3364
)
3465
return &ffcli.Command{
@@ -38,13 +69,13 @@ func Init() *ffcli.Command {
3869
LongHelp: `Initializes SigStore root to retrieve trusted certificate and key targets for verification.
3970
4071
The following options are used by default:
41-
- Initial root keys are pulled from .sigstore/keys. If it does not exist, uses root keys provided in the release.
42-
- SigStore current TUF repository is pulled from the GCS mirror at .
43-
- Resulting trusted metadata is written to .sigstore/root.
72+
- The initial 1.root.json is embedded inside cosign.
73+
- SigStore current TUF repository is pulled from the GCS mirror at sigstore-tuf-root.
74+
- A default threshold of 3 root signatures is used.
4475
45-
To provide an out-of-band trusted root.json, copy the file into a directory named .sigstore/root/.
76+
To provide an out-of-band trusted initial root.json, use the -root flag with a file or URL reference.
4677
47-
The resulting updated TUF repository will be written to .sigstore/root/.
78+
The resulting updated TUF repository will be written to $HOME/.sigstore/root/.
4879
4980
Trusted keys and certificate used in cosign verification (e.g. verifying Fulcio issued certificates
5081
with Fulcio root CA) are pulled form the trusted metadata.
@@ -57,18 +88,30 @@ EXAMPLES
5788
cosign init
5889
5990
# initialize with an out-of-band root key file and custom repository mirror.
60-
cosign init-mirror <>
91+
cosign init -mirror <url> -root <url>
6192
`,
6293
FlagSet: flagset,
6394
Exec: func(ctx context.Context, args []string) error {
95+
// Get the initial trusted root contents.
96+
var rootFileBytes []byte
97+
if *root == "" {
98+
rootFileBytes = []byte(initialRoot)
99+
} else {
100+
var err error
101+
rootFileBytes, err = loadFileOrURL(*root)
102+
if err != nil {
103+
return err
104+
}
105+
}
106+
64107
// Initialize the remote repository.
65108
remote, err := ctuf.GcsRemoteStore(ctx, *mirror, nil, nil)
66109
if err != nil {
67110
return err
68111
}
69112

70113
// Initialize and update the local SigStore root.
71-
return ctuf.Init(context.Background(), *root, remote, *threshold)
114+
return ctuf.Init(context.Background(), rootFileBytes, remote, *threshold)
72115
},
73116
}
74117
}

pkg/cosign/tuf/client.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"github.com/pkg/errors"
2828

29+
"github.com/theupdateframework/go-tuf"
2930
"github.com/theupdateframework/go-tuf/client"
3031
tuf_leveldbstore "github.com/theupdateframework/go-tuf/client/leveldbstore"
3132
"github.com/theupdateframework/go-tuf/data"
@@ -37,15 +38,19 @@ const (
3738
defaultLocalStore = ".sigstore/root/"
3839
)
3940

40-
// Global TUF client. Stores local targets in .sigstore/root.
41+
// Global TUF client. Stores local targets in $HOME/.sigstore/root.
4142
// Could be in memory local store, but that would mean re-download each time cosign is run.
4243
var rootClient *client.Client
4344
var rootClientMu = &sync.Mutex{}
4445

4546
func CosignRoot() string {
4647
rootDir := os.Getenv(TufRootEnv)
4748
if rootDir == "" {
48-
return defaultLocalStore
49+
home, err := os.UserHomeDir()
50+
if err != nil {
51+
home = ""
52+
}
53+
return path.Join(home, defaultLocalStore)
4954
}
5055
return rootDir
5156
}
@@ -73,16 +78,13 @@ func (b *ByteDestination) Delete() error {
7378
return nil
7479
}
7580

76-
func getRootKeys(rootPath string) ([]*data.Key, error) {
77-
rootFile, err := os.Open(rootPath)
81+
func getRootKeys(rootFileBytes []byte) ([]*data.Key, error) {
82+
store := tuf.MemoryStore(map[string]json.RawMessage{"root.json": rootFileBytes}, nil)
83+
repo, err := tuf.NewRepo(store)
7884
if err != nil {
7985
return nil, err
8086
}
81-
var rootKeys []*data.Key
82-
if err := json.NewDecoder(rootFile).Decode(&rootKeys); err != nil {
83-
return nil, err
84-
}
85-
return rootKeys, nil
87+
return repo.RootKeys()
8688
}
8789

8890
// Gets the global TUF client if the directory exists.
@@ -102,7 +104,7 @@ func RootClient(ctx context.Context, local string, remote client.RemoteStore) (*
102104
}
103105

104106
func updateMetadataAndDownloadTargets(c *client.Client) error {
105-
// Download initial targets and store in .sigstore/root/targets/.
107+
// Download initial targets and store in $HOME/.sigstore/root/targets/.
106108
targetFiles, err := c.Update()
107109
if err != nil && !client.IsLatestSnapshot(err) {
108110
return errors.Wrap(err, "updating tuf metadata")
@@ -137,20 +139,20 @@ func downloadTarget(name string, c *client.Client, out client.Destination) error
137139
return err
138140
}
139141

140-
// Instantiates the global TUF client. Downloads all initial targets and stores in .sigstore/root/targets/.
141-
func Init(ctx context.Context, rootFile string, remote client.RemoteStore, threshold int) error {
142+
// Instantiates the global TUF client. Downloads all initial targets and stores in $HOME/.sigstore/root/targets/.
143+
func Init(ctx context.Context, rootBytes []byte, remote client.RemoteStore, threshold int) error {
142144
rootClient, err := RootClient(ctx, CosignRoot(), remote)
143145
if err != nil {
144146
return errors.Wrap(err, "initializing root client")
145147
}
146-
rootKeys, err := getRootKeys(rootFile)
148+
rootKeys, err := getRootKeys(rootBytes)
147149
if err != nil {
148150
return errors.Wrap(err, "retrieving root keys")
149151
}
150152
if err := rootClient.Init(rootKeys, threshold); err != nil {
151153
return errors.Wrap(err, "initializing tuf client")
152154
}
153-
// Download initial targets and store in .sigstore/root/targets/.
155+
// Download initial targets and store in $HOME/.sigstore/root/targets/.
154156
if err := os.MkdirAll(CosignRoot(), 0755); err != nil {
155157
return errors.Wrap(err, "creating targets dir")
156158
}

0 commit comments

Comments
 (0)