Skip to content
This repository was archived by the owner on Jul 19, 2024. It is now read-only.

Commit 25cfd18

Browse files
get version from attestation (#4)
1 parent 7c5c2ca commit 25cfd18

File tree

6 files changed

+198
-31
lines changed

6 files changed

+198
-31
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ require (
1818
)
1919

2020
require (
21+
github.com/sigstore/cosign v1.13.1
2122
github.com/sigstore/cosign/v2 v2.0.3-0.20230619102641-b0072d56686b
2223
gorm.io/driver/sqlite v1.5.2
2324
)
@@ -130,7 +131,6 @@ require (
130131
github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf // indirect
131132
github.com/magiconair/properties v1.8.7 // indirect
132133
github.com/mailru/easyjson v0.7.7 // indirect
133-
github.com/mattn/go-colorable v0.1.13 // indirect
134134
github.com/mattn/go-isatty v0.0.19 // indirect
135135
github.com/mattn/go-sqlite3 v1.14.17 // indirect
136136
github.com/miekg/pkcs11 v1.1.1 // indirect

go.sum

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
486486
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
487487
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
488488
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
489-
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
489+
github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo=
490490
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
491491
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
492492
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
@@ -589,9 +589,7 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsI
589589
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
590590
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
591591
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
592-
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
593592
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
594-
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
595593
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
596594
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
597595
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
@@ -699,6 +697,8 @@ github.com/secure-systems-lab/go-securesystemslib v0.6.0/go.mod h1:8Mtpo9JKks/qh
699697
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
700698
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
701699
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
700+
github.com/sigstore/cosign v1.13.1 h1:+5oF8jisEcDw2TuXxCADC1u5//HfdnJhGbpv9Isiwu4=
701+
github.com/sigstore/cosign v1.13.1/go.mod h1:PlfJODkovUOKsLrGI7Su57Ie/Eb/Ks7hRHw3tn5hQS4=
702702
github.com/sigstore/cosign/v2 v2.0.3-0.20230619102641-b0072d56686b h1:4dXzC6VFLCXH9r1CqgMSIcd6+P1FToHxzqHIh99MxLs=
703703
github.com/sigstore/cosign/v2 v2.0.3-0.20230619102641-b0072d56686b/go.mod h1:vITelUMv9WOJQs4XdHao7JCAx8q3QAkDRK7sDrWnN60=
704704
github.com/sigstore/rekor v1.2.2-0.20230530122220-67cc9e58bd23 h1:eZY7mQFcc0VvNr0fiAK3/n7kh73+T06KzBEIUYzFSDQ=
@@ -1037,7 +1037,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
10371037
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
10381038
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
10391039
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1040-
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
10411040
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
10421041
golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
10431042
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1275,7 +1274,7 @@ gorm.io/driver/sqlite v1.5.2/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed
12751274
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
12761275
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55 h1:sC1Xj4TYrLqg1n3AN10w871An7wJM0gzgcm8jkIkECQ=
12771276
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
1278-
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
1277+
gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk=
12791278
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
12801279
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
12811280
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

services/container-registry/registry.go

Lines changed: 188 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package containerregistry
22

33
import (
4-
"bytes"
54
"context"
5+
"encoding/base64"
66
"encoding/json"
7+
"fmt"
78
"regexp"
9+
"strings"
810

911
"github.com/google/go-containerregistry/pkg/authn"
1012
"github.com/google/go-containerregistry/pkg/crane"
11-
cdl "github.com/sigstore/cosign/v2/cmd/cosign/cli/download"
13+
"github.com/google/go-containerregistry/pkg/name"
14+
v1 "github.com/google/go-containerregistry/pkg/v1"
1215
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
16+
"github.com/sigstore/cosign/v2/pkg/cosign"
17+
"github.com/sigstore/cosign/v2/pkg/oci"
18+
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
1319
)
1420

1521
type Interface interface {
@@ -54,41 +60,201 @@ func (c *Client) ListTagsWithConstraint(repoName string, constraint string) ([]s
5460
return result, nil
5561
}
5662

57-
func (c *Client) VersionFromSbom(mainPkg string, repo string) (string, error) {
58-
type Package struct {
59-
Name string `json:"name"`
60-
VersionInfo string `json:"versionInfo"`
61-
}
62-
type Packages struct {
63-
Packages []Package `json:"packages"`
64-
}
65-
buf := new(bytes.Buffer)
63+
func (c *Client) VersionFromSbom(mainPkg string, imageRef string) (string, error) {
64+
ctx := context.TODO()
65+
// type Package struct {
66+
// Name string `json:"name"`
67+
// VersionInfo string `json:"versionInfo"`
68+
// }
69+
// type Packages struct {
70+
// Packages []Package `json:"packages"`
71+
// }
72+
// buf := new(bytes.Buffer)
6673
kc := authn.NewMultiKeychain(
6774
authn.DefaultKeychain,
6875
)
6976

70-
o := &options.RegistryOptions{Keychain: kc}
71-
do := &options.SBOMDownloadOptions{Platform: "linux/amd64"}
72-
sboms, err := cdl.SBOMCmd(context.TODO(), *o, *do, repo, buf)
77+
regOpts := options.RegistryOptions{Keychain: kc}
78+
// do := &options.SBOMDownloadOptions{Platform: "linux/amd64"}
79+
// sboms, err := cdl.SBOMCmd(context.TODO(), *o, *do, repo, buf)
80+
// if err != nil {
81+
// return "", err
82+
// }
83+
// var ps Packages
84+
85+
// for _, s := range sboms {
86+
// json.Unmarshal([]byte(s), &ps)
87+
// for _, v := range ps.Packages {
88+
// if v.Name == mainPkg {
89+
// return v.VersionInfo, nil
90+
// }
91+
// }
92+
// }
93+
attOptions := &options.AttestationDownloadOptions{
94+
PredicateType: "https://slsa.dev/provenance/v1",
95+
Platform: "linux/amd64",
96+
}
97+
ref, err := name.ParseReference(imageRef, regOpts.NameOptions()...)
98+
if err != nil {
99+
return "", err
100+
}
101+
ociremoteOpts, err := regOpts.ClientOpts(ctx)
73102
if err != nil {
74103
return "", err
75104
}
76-
var ps Packages
77105

78-
for _, s := range sboms {
79-
json.Unmarshal([]byte(s), &ps)
80-
for _, v := range ps.Packages {
81-
if v.Name == mainPkg {
82-
return v.VersionInfo, nil
83-
}
106+
var predicateType string
107+
predicateType, err = options.ParsePredicateType(attOptions.PredicateType)
108+
if err != nil {
109+
return "", err
110+
}
111+
112+
se, err := ociremote.SignedEntity(ref, ociremoteOpts...)
113+
if err != nil {
114+
return "", err
115+
}
116+
117+
idx, isIndex := se.(oci.SignedImageIndex)
118+
119+
// We only allow --platform on multiarch indexes
120+
if attOptions.Platform != "" && !isIndex {
121+
return "", fmt.Errorf("specified reference is not a multiarch image")
122+
}
123+
124+
if attOptions.Platform != "" && isIndex {
125+
targetPlatform, err := v1.ParsePlatform(attOptions.Platform)
126+
if err != nil {
127+
return "", fmt.Errorf("parsing platform: %w", err)
128+
}
129+
platforms, err := getIndexPlatforms(idx)
130+
if err != nil {
131+
return "", fmt.Errorf("getting available platforms: %w", err)
132+
}
133+
134+
platforms = matchPlatform(targetPlatform, platforms)
135+
if len(platforms) == 0 {
136+
return "", fmt.Errorf("unable to find an attestation for %s", targetPlatform.String())
137+
}
138+
if len(platforms) > 1 {
139+
return "nil", fmt.Errorf(
140+
"platform spec matches more than one image architecture: %s",
141+
platforms.String(),
142+
)
143+
}
144+
145+
nse, err := idx.SignedImage(platforms[0].hash)
146+
if err != nil {
147+
return "", fmt.Errorf("searching for %s image: %w", platforms[0].hash.String(), err)
84148
}
149+
if nse == nil {
150+
return "", fmt.Errorf("unable to find image %s", platforms[0].hash.String())
151+
}
152+
se = nse
153+
}
154+
155+
attestations, err := cosign.FetchAttestations(se, predicateType)
156+
if err != nil {
157+
return "", err
158+
}
159+
if len(attestations) > 1 {
160+
return "", fmt.Errorf("filtered attestation list is more than one")
85161
}
86-
return "", nil
162+
var a attestationPayload
163+
att := attestations[0]
164+
pB, err := base64.StdEncoding.DecodeString(att.PayLoad)
165+
if err != nil {
166+
return "", err
167+
}
168+
if err := json.Unmarshal(pB, &a); err != nil {
169+
return "", err
170+
}
171+
return a.Predicate.BuildDefinition.InternalParameters[mainPkg], nil
172+
}
173+
174+
type attestationPayload struct {
175+
Predicate predicate `json:"predicate"`
176+
}
177+
178+
type predicate struct {
179+
BuildDefinition buildDefinition `json:"buildDefinition"`
180+
}
181+
182+
type buildDefinition struct {
183+
InternalParameters map[string]string `json:"internalParameters"`
87184
}
88185

186+
// cosign download attestation cgr.dev/chainguard/redis --predicate-type https://slsa.dev/provenance/v1 | jq '.payload | @base64d | fromjson | .predicate.buildDefinition.internalParameters.redis'
187+
89188
func (c *Client) getAuthOpt() crane.Option {
90189
kc := authn.NewMultiKeychain(
91190
authn.DefaultKeychain,
92191
)
93192
return crane.WithAuthFromKeychain(kc)
94193
}
194+
195+
func getIndexPlatforms(idx oci.SignedImageIndex) (platformList, error) {
196+
im, err := idx.IndexManifest()
197+
if err != nil {
198+
return nil, fmt.Errorf("fetching index manifest: %w", err)
199+
}
200+
201+
platforms := platformList{}
202+
for _, m := range im.Manifests {
203+
if m.Platform == nil {
204+
continue
205+
}
206+
platforms = append(platforms, struct {
207+
hash v1.Hash
208+
platform *v1.Platform
209+
}{m.Digest, m.Platform})
210+
}
211+
return platforms, nil
212+
}
213+
214+
// matchPlatform filters a list of platforms returning only those matching
215+
// a base. "Based" on ko's internal equivalent while it moves to GGCR.
216+
// https://github.com/google/ko/blob/e6a7a37e26d82a8b2bb6df991c5a6cf6b2728794/pkg/build/gobuild.go#L1020
217+
func matchPlatform(base *v1.Platform, list platformList) platformList {
218+
ret := platformList{}
219+
for _, p := range list {
220+
if base.OS != "" && base.OS != p.platform.OS {
221+
continue
222+
}
223+
if base.Architecture != "" && base.Architecture != p.platform.Architecture {
224+
continue
225+
}
226+
if base.Variant != "" && base.Variant != p.platform.Variant {
227+
continue
228+
}
229+
230+
if base.OSVersion != "" && p.platform.OSVersion != base.OSVersion {
231+
if base.OS != "windows" {
232+
continue
233+
} else { //nolint: revive
234+
if pcount, bcount := strings.Count(base.OSVersion, "."), strings.Count(p.platform.OSVersion, "."); pcount == 2 && bcount == 3 {
235+
if base.OSVersion != p.platform.OSVersion[:strings.LastIndex(p.platform.OSVersion, ".")] {
236+
continue
237+
}
238+
} else {
239+
continue
240+
}
241+
}
242+
}
243+
ret = append(ret, p)
244+
}
245+
246+
return ret
247+
}
248+
249+
type platformList []struct {
250+
hash v1.Hash
251+
platform *v1.Platform
252+
}
253+
254+
func (pl *platformList) String() string {
255+
r := []string{}
256+
for _, p := range *pl {
257+
r = append(r, p.platform.String())
258+
}
259+
return strings.Join(r, ", ")
260+
}

services/container-registry/registry_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func TestListTag(t *testing.T) {
3636

3737
func TestVersionFromSbom(t *testing.T) {
3838
c := New()
39-
tag, err := c.VersionFromSbom("nginx", "cgr.dev/chainguard/nginx:1.25.1-r0")
39+
v, err := c.VersionFromSbom("nginx", "cgr.dev/chainguard/nginx:1.25.1-r0")
4040
assert.NoError(t, err)
41-
fmt.Printf("sboms: %v\n", tag)
41+
fmt.Printf("version: %v\n", v)
4242
}

services/digest-fetcher/digest_fetcher.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func (c *client) Fetch(images []config.Image) error {
9595
c.log.Errorf("save digest to db %v", err)
9696
break
9797
}
98-
c.log.Info("saved digest to db")
98+
c.log.Infof("saved to db %s %s", v.Name, tag)
9999
}
100100
}
101101
}()

services/digest-fetcher/digetst_fetcher_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ func TestFetch(t *testing.T) {
2828
},
2929
Images: images,
3030
WorkerFetchInterval: "10s",
31+
DB: "mysql",
3132
}
3233
d, err := time.ParseDuration(conf.WorkerFetchInterval)
34+
assert.NoError(t, err)
3335
storage, err := inject.GetStorage(conf)
3436
assert.NoError(t, err)
3537
registryClient, err := inject.GetContainerRegistryClient()

0 commit comments

Comments
 (0)