Skip to content

Commit

Permalink
Merge pull request #2 from rancherlabs/mvp
Browse files Browse the repository at this point in the history
Add subcommand verify
  • Loading branch information
pjbgf authored Nov 7, 2024
2 parents 963b929 + 30a76c3 commit b5a85fa
Show file tree
Hide file tree
Showing 7 changed files with 1,167 additions and 339 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ An example being:
slsactl download sbom rancher/rancher:v2.8.1
```

### Verify
The cosign verification of Rancher Prime images can be done with:

```bash
slsactl verify <prime_image>:<tag>
```

## License
Copyright (c) 2014-2024 [Rancher Labs, Inc.](http://rancher.com)

Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var (
cmds = map[string]command{
"download": downloadCmd,
"version": versionCmd,
"verify": verifyCmd,
}

usagef = `usage: %[1]s <command>
Expand Down
36 changes: 36 additions & 0 deletions cmd/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cmd

import (
"flag"
"fmt"
"os"

"github.com/rancherlabs/slsactl/internal/verify"
)

const verifyf = `usage:
%[1]s verify <IMAGE>
`

func verifyCmd(args []string) error {
f := flag.NewFlagSet("", flag.ContinueOnError)
err := f.Parse(args)
if err != nil {
return err
}

if len(f.Args()) != 1 {
showVerifyUsage()
}

err = verify.Verify(f.Arg(0))
if err != nil {
fmt.Printf("cannot validate image %s: ensure you are using an image from the Prime registry\n", f.Arg(0))
}
return err
}

func showVerifyUsage() {
fmt.Printf(verifyf, exeName())
os.Exit(1)
}
348 changes: 242 additions & 106 deletions go.mod

Large diffs are not rendered by default.

956 changes: 723 additions & 233 deletions go.sum

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions internal/verify/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package verify

import (
"context"
"crypto"
"fmt"
"os"
"strings"
"time"

"github.com/google/go-containerregistry/pkg/logs"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/verify"
)

const (
timeout = 45 * time.Second
)

func Verify(imageName string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

certIdentity, err := certIdentity(imageName)
if err != nil {
return err
}

fmt.Println("identity:", certIdentity)

v := &verify.VerifyCommand{
CertVerifyOptions: options.CertVerifyOptions{
CertIdentity: certIdentity,
CertOidcIssuer: "https://token.actions.githubusercontent.com",
},
CheckClaims: true,
HashAlgorithm: crypto.SHA256,
MaxWorkers: 5,
}

if strings.EqualFold(os.Getenv("DEBUG"), "true") {
logs.Debug.SetOutput(os.Stderr)
}

return v.Exec(ctx, []string{imageName})
}

func certIdentity(imageName string) (string, error) {
if len(imageName) < 5 {
return "", fmt.Errorf("invalid image name: %q", imageName)
}

if strings.Contains(imageName, "@") {
fmt.Println("warn: image name with digest is not supported, use tags only.")
imageName = strings.Split(imageName, "@")[0]
}

d := strings.Split(imageName, ":")
if len(d) < 2 || len(d[1]) == 0 {
return "", fmt.Errorf("missing image tag: %q", imageName)
}

names := strings.Split(d[0], "/")
if len(names) < 2 {
return "", fmt.Errorf("unsupported image name: %q", imageName)
}

repo := strings.Join(names[len(names)-2:], "/")
ref := strings.Replace(d[1], "-", "&#43;", 1)
repo = overrideRepo(repo)

indentity := fmt.Sprintf(
"https://github.com/%s/.github/workflows/release.yml@refs/tags/%s", repo, ref)

return indentity, nil
}

func overrideRepo(repo string) string {
if strings.HasPrefix(repo, "rancher/rke2-") {
return "rancher/rke2"
}
return repo
}
75 changes: 75 additions & 0 deletions internal/verify/verify_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package verify

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestCertificateIdentity(t *testing.T) {
tests := []struct {
image string
want string
wantErr string
}{
{
image: "foo/bar:v0.0.7",
want: "https://github.com/foo/bar/.github/workflows/release.yml@refs/tags/v0.0.7",
},
{
image: "foo/bar:v0.0.7-barfoo2",
want: "https://github.com/foo/bar/.github/workflows/release.yml@refs/tags/v0.0.7&#43;barfoo2",
},
{
image: "",
wantErr: "invalid image name",
},
{
image: "foo/bar",
wantErr: "missing image tag",
},
{
image: "foo/bar:",
wantErr: "missing image tag",
},
{
image: "foo/bar@sha256:a32d91ba265e6fcb1963c28bb688d0b799a1966f30f6ea17d8eca1d436bbc267",
wantErr: "missing image tag",
},
{
image: "bar/foo:v3.14@sha256:a32d91ba265e6fcb1963c28bb688d0b799a1966f30f6ea17d8eca1d436bbc267",
want: "https://github.com/bar/foo/.github/workflows/release.yml@refs/tags/v3.14",
},
{
image: "foo:bar",
wantErr: "unsupported image name",
},
{
image: "tocker.local/foo/bar:v0.0.7",
want: "https://github.com/foo/bar/.github/workflows/release.yml@refs/tags/v0.0.7",
},
{
image: "tocker.local/bar/foo/bar:v3.14",
want: "https://github.com/foo/bar/.github/workflows/release.yml@refs/tags/v3.14",
},
{
image: "rancher/rke2-bar:v0.0.7",
want: "https://github.com/rancher/rke2/.github/workflows/release.yml@refs/tags/v0.0.7",
},
}

for _, tc := range tests {
tc := tc
t.Run(tc.image, func(t *testing.T) {
got, err := certIdentity(tc.image)

assert.Equal(t, tc.want, got)

if tc.wantErr == "" {
assert.NoError(t, err)
} else {
assert.ErrorContains(t, err, tc.wantErr)
}
})
}
}

0 comments on commit b5a85fa

Please sign in to comment.