Skip to content

Commit

Permalink
feat: verify checksum files using minisign (#3103)
Browse files Browse the repository at this point in the history
* feat: verify checksum files using minisign

* refactor: split functions

* docs: update JSONSchema

* fix: check if minisign supports the current environment

* fix: fix a compile error

* fix: output a warning when minisign fails

* fix: stop locking

* fix: check if minisign supports the environment

* refactor: fix a lint error

* refactor: split function

* fix: fix a lint error

* fix: fix a bug that checksum is missing

* fix: fix a lint error
  • Loading branch information
suzuki-shunsuke authored Sep 22, 2024
1 parent b08f3dc commit 0c0cba1
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 57 deletions.
3 changes: 3 additions & 0 deletions json-schema/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@
},
"cosign": {
"$ref": "#/$defs/Cosign"
},
"minisign": {
"$ref": "#/$defs/Minisign"
}
},
"additionalProperties": false,
Expand Down
8 changes: 8 additions & 0 deletions pkg/config/registry/checksum.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Checksum struct {
Enabled *bool `json:"enabled,omitempty"`
Replacements Replacements `json:"replacements,omitempty"`
Cosign *Cosign `json:"cosign,omitempty"`
Minisign *Minisign `json:"minisign,omitempty"`
}

type ChecksumPattern struct {
Expand Down Expand Up @@ -47,3 +48,10 @@ func (c *Checksum) GetCosign() *Cosign {
}
return c.Cosign
}

func (c *Checksum) GetMinisign() *Minisign {
if c == nil {
return nil
}
return c.Minisign
}
59 changes: 57 additions & 2 deletions pkg/installpackage/checksum.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/aquaproj/aqua/v2/pkg/checksum"
"github.com/aquaproj/aqua/v2/pkg/config"
"github.com/aquaproj/aqua/v2/pkg/download"
"github.com/aquaproj/aqua/v2/pkg/minisign"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
"github.com/suzuki-shunsuke/logrus-error/logerr"
Expand All @@ -27,13 +28,16 @@ func (is *Installer) dlAndExtractChecksum(ctx context.Context, logE *logrus.Entr
return "", fmt.Errorf("read a checksum file: %w", err)
}

var tempFilePath string

if cos := pkg.PackageInfo.Checksum.GetCosign(); cos.GetEnabled() && !is.cosignDisabled {
f, err := afero.TempFile(is.fs, "", "")
if err != nil {
return "", fmt.Errorf("create a temporary file: %w", err)
}
tempFilePath = f.Name()
defer f.Close()
defer is.fs.Remove(f.Name()) //nolint:errcheck
defer is.fs.Remove(tempFilePath) //nolint:errcheck
if _, err := f.Write(b); err != nil {
return "", fmt.Errorf("write a checksum to a temporary file: %w", err)
}
Expand All @@ -46,14 +50,65 @@ func (is *Installer) dlAndExtractChecksum(ctx context.Context, logE *logrus.Entr
RepoOwner: pkg.PackageInfo.RepoOwner,
RepoName: pkg.PackageInfo.RepoName,
Version: pkg.Package.Version,
}, cos, art, f.Name()); err != nil {
}, cos, art, tempFilePath); err != nil {
return "", fmt.Errorf("verify a checksum file with Cosign: %w", err)
}
}

if err := is.verifyChecksumWithMinisign(ctx, logE, pkg, tempFilePath, b); err != nil {
return "", err
}

return checksum.GetChecksum(logE, assetName, string(b), pkg.PackageInfo.Checksum) //nolint:wrapcheck
}

// b is a content of the checksum file.
// tempFilePath is a path of the checksum file.
func (is *Installer) verifyChecksumWithMinisign(ctx context.Context, logE *logrus.Entry, pkg *config.Package, tempFilePath string, b []byte) error {
ms := pkg.PackageInfo.Checksum.GetMinisign()
if !ms.GetEnabled() {
return nil
}

mPkg := minisign.Package()
if f, err := mPkg.PackageInfo.CheckSupported(is.realRuntime, is.realRuntime.Env()); err != nil {
return fmt.Errorf("check if minisign supports this environment: %w", err)
} else if !f {
logE.Warn("minisign doesn't support this environment")
return nil
}

if tempFilePath == "" {
f, err := afero.TempFile(is.fs, "", "")
if err != nil {
return fmt.Errorf("create a temporary file: %w", err)
}
tempFilePath = f.Name()
defer f.Close()
defer is.fs.Remove(tempFilePath) //nolint:errcheck
if _, err := f.Write(b); err != nil {
return fmt.Errorf("write a checksum to a temporary file: %w", err)
}
}

art := pkg.TemplateArtifact(is.runtime, "")
logE.Info("verifing a checksum file with Minisign")
if err := is.minisignInstaller.install(ctx, logE); err != nil {
return err
}
if err := is.minisignVerifier.Verify(ctx, logE, is.runtime, ms, art, &download.File{
RepoOwner: pkg.PackageInfo.RepoOwner,
RepoName: pkg.PackageInfo.RepoName,
Version: pkg.Package.Version,
}, &minisign.ParamVerify{
ArtifactPath: tempFilePath,
PublicKey: ms.PublicKey,
}); err != nil {
return fmt.Errorf("verify a checksum file with Minisign: %w", err)
}
return nil
}

type ParamVerifyChecksum struct {
ChecksumID string
Checksum *checksum.Checksum
Expand Down
6 changes: 5 additions & 1 deletion pkg/installpackage/dedicated_installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ func newDedicatedInstaller(installer *Installer, pkg func() *config.Package, che
func (di *DedicatedInstaller) install(ctx context.Context, logE *logrus.Entry) error {
di.mutex.Lock()
defer di.mutex.Unlock()
chksum := di.checksums[di.installer.runtime.Env()]

pkg := di.pkg()
logE = logE.WithFields(logrus.Fields{
"package_name": pkg.Package.Name,
"package_version": pkg.Package.Version,
})

pkgInfo, err := pkg.PackageInfo.Override(logE, pkg.Package.Version, di.installer.runtime)
if err != nil {
Expand All @@ -47,6 +50,7 @@ func (di *DedicatedInstaller) install(ctx context.Context, logE *logrus.Entry) e
}

pkg.PackageInfo = pkgInfo
chksum := di.checksums[fmt.Sprintf("%s/%s", di.installer.runtime.GOOS, di.installer.runtime.Arch(pkg.PackageInfo.Rosetta2, pkg.PackageInfo.WindowsARMEmulation))]

if err := di.installer.InstallPackage(ctx, logE, &ParamInstallPackage{
Checksums: checksum.New(), // Check checksum but not update aqua-checksums.json
Expand Down
7 changes: 7 additions & 0 deletions pkg/installpackage/verify_minisign.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ func (is *Installer) verifyWithMinisign(ctx context.Context, logE *logrus.Entry,
if !m.GetEnabled() {
return nil
}
mPkg := minisign.Package()
if f, err := mPkg.PackageInfo.CheckSupported(is.realRuntime, is.realRuntime.Env()); err != nil {
return fmt.Errorf("check if minisign supports this environment: %w", err)
} else if !f {
logE.Warn("minisign doesn't support this environment")
return nil
}
art := ppkg.TemplateArtifact(is.runtime, param.Asset)
logE.Info("verify a package with minisign")
if err := is.minisignInstaller.install(ctx, logE); err != nil {
Expand Down
13 changes: 8 additions & 5 deletions pkg/minisign/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import (
"errors"
"fmt"
"math/rand"
"strings"
"time"

"github.com/aquaproj/aqua/v2/pkg/config"
"github.com/aquaproj/aqua/v2/pkg/cosign"
"github.com/aquaproj/aqua/v2/pkg/runtime"
"github.com/aquaproj/aqua/v2/pkg/timer"
"github.com/sirupsen/logrus"
"github.com/suzuki-shunsuke/logrus-error/logerr"
)

type CommandExecutor interface {
Expand Down Expand Up @@ -55,9 +56,6 @@ func wait(ctx context.Context, logE *logrus.Entry, retryCount int) error {
}

func (e *ExecutorImpl) exec(ctx context.Context, args []string) error {
mutex := cosign.GetMutex()
mutex.Lock()
defer mutex.Unlock()
_, err := e.executor.Exec(ctx, e.minisignExePath, args...)
return err //nolint:wrapcheck
}
Expand All @@ -75,9 +73,14 @@ func (e *ExecutorImpl) Verify(ctx context.Context, logE *logrus.Entry, param *Pa
signature,
}
for i := range 5 {
if err := e.exec(ctx, args); err == nil {
err := e.exec(ctx, args)
if err == nil {
return nil
}
logerr.WithError(logE, err).WithFields(logrus.Fields{
"exe": e.minisignExePath,
"args": strings.Join(args, " "),
}).Warn("execute minisign")
if i == 4 { //nolint:mnd
break
}
Expand Down
22 changes: 15 additions & 7 deletions pkg/minisign/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,33 @@ type ParamVerify struct {
}

func (v *Verifier) Verify(ctx context.Context, logE *logrus.Entry, rt *runtime.Runtime, m *registry.Minisign, art *template.Artifact, file *download.File, param *ParamVerify) error {
sigFile, err := v.downloadSignature(ctx, logE, rt, m, art, file)
if err != nil {
return err
}
defer v.fs.Remove(sigFile) //nolint:errcheck
return v.exe.Verify(ctx, logE, param, sigFile) //nolint:wrapcheck
}

func (v *Verifier) downloadSignature(ctx context.Context, logE *logrus.Entry, rt *runtime.Runtime, m *registry.Minisign, art *template.Artifact, file *download.File) (string, error) {
f, err := download.ConvertDownloadedFileToFile(m.ToDownloadedFile(), file, rt, art)
if err != nil {
return err //nolint:wrapcheck
return "", err //nolint:wrapcheck
}

rc, _, err := v.downloader.ReadCloser(ctx, logE, f)
if err != nil {
return fmt.Errorf("download a Minisign signature: %w", err)
return "", fmt.Errorf("download a Minisign signature: %w", err)
}
defer rc.Close()

signatureFile, err := afero.TempFile(v.fs, "", "")
if err != nil {
return fmt.Errorf("create a temporary file: %w", err)
return "", fmt.Errorf("create a temporary file: %w", err)
}
defer signatureFile.Close()
defer v.fs.Remove(signatureFile.Name()) //nolint:errcheck
if _, err := io.Copy(signatureFile, rc); err != nil {
return fmt.Errorf("copy a signature to a temporary file: %w", err)
return signatureFile.Name(), fmt.Errorf("copy a signature to a temporary file: %w", err)
}

return v.exe.Verify(ctx, logE, param, signatureFile.Name()) //nolint:wrapcheck
return signatureFile.Name(), nil
}
38 changes: 19 additions & 19 deletions pkg/runtime/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,28 @@ import (
func allRuntimes() []*Runtime {
return []*Runtime{
{
GOOS: "darwin",
GOARCH: "amd64",
GOOS: darwin,
GOARCH: amd64,
},
{
GOOS: "darwin",
GOARCH: "arm64",
GOOS: darwin,
GOARCH: arm64,
},
{
GOOS: "linux",
GOARCH: "amd64",
GOOS: linux,
GOARCH: amd64,
},
{
GOOS: "linux",
GOARCH: "arm64",
GOOS: linux,
GOARCH: arm64,
},
{
GOOS: "windows",
GOARCH: "amd64",
GOOS: windows,
GOARCH: amd64,
},
{
GOOS: "windows",
GOARCH: "arm64",
GOOS: windows,
GOARCH: arm64,
},
}
}
Expand All @@ -48,29 +48,29 @@ func GetRuntimes(env string) ([]*Runtime, error) {
}, nil
}
switch o {
case "darwin", "linux", "windows":
case darwin, linux, windows:
return []*Runtime{
{
GOOS: o,
GOARCH: "amd64",
GOARCH: amd64,
},
{
GOOS: o,
GOARCH: "arm64",
GOARCH: arm64,
},
}, nil
case "amd64", "arm64":
case amd64, arm64:
return []*Runtime{
{
GOOS: "darwin",
GOOS: darwin,
GOARCH: o,
},
{
GOOS: "windows",
GOOS: windows,
GOARCH: o,
},
{
GOOS: "linux",
GOOS: linux,
GOARCH: o,
},
}, nil
Expand Down
34 changes: 17 additions & 17 deletions pkg/runtime/parse_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,46 +20,46 @@ func TestGetRuntimesFromEnvs(t *testing.T) { //nolint:funlen
},
{
name: "all",
envs: []string{"darwin", "all"},
envs: []string{darwin, "all"},
rts: allRuntimes(),
},
{
name: "darwin amd64",
envs: []string{"darwin", "amd64"},
envs: []string{darwin, amd64},
rts: []*Runtime{
{
GOOS: "darwin",
GOARCH: "amd64",
GOOS: darwin,
GOARCH: amd64,
},
{
GOOS: "darwin",
GOARCH: "arm64",
GOOS: darwin,
GOARCH: arm64,
},
{
GOOS: "windows",
GOARCH: "amd64",
GOOS: windows,
GOARCH: amd64,
},
{
GOOS: "linux",
GOARCH: "amd64",
GOOS: linux,
GOARCH: amd64,
},
},
},
{
name: "darwin linux/amd64",
envs: []string{"darwin", "linux/amd64"},
envs: []string{darwin, "linux/amd64"},
rts: []*Runtime{
{
GOOS: "darwin",
GOARCH: "amd64",
GOOS: darwin,
GOARCH: amd64,
},
{
GOOS: "darwin",
GOARCH: "arm64",
GOOS: darwin,
GOARCH: arm64,
},
{
GOOS: "linux",
GOARCH: "amd64",
GOOS: linux,
GOARCH: amd64,
},
},
},
Expand Down
Loading

0 comments on commit 0c0cba1

Please sign in to comment.