Skip to content

Commit

Permalink
refactor: file verifier (#3125)
Browse files Browse the repository at this point in the history
* refactor: file verifier

* fix: fix lint errors

* fix: fix minisign enabled

* fix: fix error messages

* refactor: fix a lint error
  • Loading branch information
suzuki-shunsuke committed Sep 24, 2024
1 parent 6e7b350 commit 991e38d
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 200 deletions.
157 changes: 46 additions & 111 deletions pkg/installpackage/checksum.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,13 @@ import (

"github.com/aquaproj/aqua/v2/pkg/checksum"
"github.com/aquaproj/aqua/v2/pkg/config"
"github.com/aquaproj/aqua/v2/pkg/config/registry"
"github.com/aquaproj/aqua/v2/pkg/download"
"github.com/aquaproj/aqua/v2/pkg/ghattestation"
"github.com/aquaproj/aqua/v2/pkg/minisign"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
"github.com/suzuki-shunsuke/logrus-error/logerr"
)

func (is *Installer) dlAndExtractChecksum(ctx context.Context, logE *logrus.Entry, pkg *config.Package, assetName string) (string, error) { //nolint:cyclop
func (is *Installer) dlAndExtractChecksum(ctx context.Context, logE *logrus.Entry, pkg *config.Package, assetName string) (string, error) { //nolint:funlen
file, _, err := is.checksumDownloader.DownloadChecksum(ctx, logE, is.runtime, pkg)
if err != nil {
return "", fmt.Errorf("download a checksum file: %w", err)
Expand All @@ -31,124 +28,62 @@ func (is *Installer) dlAndExtractChecksum(ctx context.Context, logE *logrus.Entr
}

var tempFilePath string
pkgInfo := pkg.PackageInfo

if cos := pkg.PackageInfo.Checksum.GetCosign(); cos.GetEnabled() && !is.cosignDisabled {
f, err := afero.TempFile(is.fs, "", "")
verifiers := []FileVerifier{
&gitHubArtifactAttestationsVerifier{
gaa: pkgInfo.Checksum.GetGitHubArtifactAttestations(),
pkg: pkg,
ghInstaller: is.ghInstaller,
ghVerifier: is.ghVerifier,
},
&cosignVerifier{
cosignDisabled: is.cosignDisabled,
cosign: pkgInfo.Checksum.GetCosign(),
pkg: pkg,
installer: is.cosignInstaller,
verifier: is.cosign,
runtime: is.runtime,
asset: assetName,
},
&minisignVerifier{
pkg: pkg,
minisign: pkgInfo.Checksum.GetMinisign(),
installer: is.minisignInstaller,
verifier: is.minisignVerifier,
runtime: is.runtime,
asset: assetName,
},
}

for _, verifier := range verifiers {
a, err := verifier.Enabled(logE)
if err != nil {
return "", fmt.Errorf("create a temporary file: %w", err)
return "", fmt.Errorf("check if the verifier is enabled: %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)
if !a {
continue
}
art := pkg.TemplateArtifact(is.runtime, assetName)
logE.Info("verify a checksum file with Cosign")
if err := is.cosignInstaller.install(ctx, logE); err != nil {
return "", err
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)
}
}
if err := is.cosign.Verify(ctx, logE, is.runtime, &download.File{
RepoOwner: pkg.PackageInfo.RepoOwner,
RepoName: pkg.PackageInfo.RepoName,
Version: pkg.Package.Version,
}, cos, art, tempFilePath); err != nil {
return "", fmt.Errorf("verify a checksum file with Cosign: %w", err)
if err := verifier.Verify(ctx, logE, tempFilePath); err != nil {
return "", fmt.Errorf("verifiy the checksum file: %w", err)
}
}

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

if err := is.verifyChecksumWithGitHubArtifactAttestation(ctx, logE, pkg, pkg.PackageInfo.Checksum.GetGitHubArtifactAttestations(), 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
}

func (is *Installer) verifyChecksumWithGitHubArtifactAttestation(ctx context.Context, logE *logrus.Entry, pkg *config.Package, gaa *registry.GitHubArtifactAttestations, tempFilePath string, b []byte) error {
if !gaa.GetEnabled() {
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)
}
}

logE.Info("verify GitHub Artifact Attestations")
if err := is.ghInstaller.install(ctx, logE); err != nil {
return fmt.Errorf("install GitHub CLI: %w", err)
}

if err := is.ghVerifier.Verify(ctx, logE, &ghattestation.ParamVerify{
Repository: pkg.PackageInfo.RepoOwner + "/" + pkg.PackageInfo.RepoName,
ArtifactPath: tempFilePath,
SignerWorkflow: gaa.SignerWorkflow,
}); err != nil {
return fmt.Errorf("verify a package with minisign: %w", err)
}

return nil
}

type ParamVerifyChecksum struct {
ChecksumID string
Checksum *checksum.Checksum
Expand Down
64 changes: 52 additions & 12 deletions pkg/installpackage/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,60 @@ func (is *Installer) download(ctx context.Context, logE *logrus.Entry, param *Do
}
}()

if err := is.verifyWithCosign(ctx, logE, bodyFile, param); err != nil {
return err
}

if err := is.verifyWithSLSA(ctx, logE, bodyFile, param); err != nil {
return err
}

if err := is.verifyWithMinisign(ctx, logE, bodyFile, param); err != nil {
return err
verifiers := []FileVerifier{
&gitHubArtifactAttestationsVerifier{
gaa: pkgInfo.GitHubArtifactAttestations,
pkg: ppkg,
ghInstaller: is.ghInstaller,
ghVerifier: is.ghVerifier,
},
&cosignVerifier{
cosignDisabled: is.cosignDisabled,
pkg: ppkg,
cosign: pkgInfo.Cosign,
installer: is.cosignInstaller,
verifier: is.cosign,
runtime: is.runtime,
asset: param.Asset,
},
&slsaVerifier{
slsaDisabled: is.slsaDisabled,
pkg: ppkg,
provenance: pkgInfo.SLSAProvenance,
installer: is.slsaVerifierInstaller,
verifier: is.slsaVerifier,
runtime: is.runtime,
asset: param.Asset,
},
&minisignVerifier{
pkg: ppkg,
installer: is.minisignInstaller,
verifier: is.minisignVerifier,
runtime: is.runtime,
asset: param.Asset,
minisign: pkgInfo.Minisign,
},
}

if err := is.verifyWithGitHubArtifactAttestation(ctx, logE, ppkg, pkgInfo.GitHubArtifactAttestations, bodyFile); err != nil {
return err
var tempFilePath string
for _, verifier := range verifiers {
a, err := verifier.Enabled(logE)
if err != nil {
return fmt.Errorf("check if the verifier is enabled: %w", err)
}
if !a {
continue
}
if tempFilePath == "" {
a, err := bodyFile.Path()
if err != nil {
return fmt.Errorf("get a temporary file path: %w", err)
}
tempFilePath = a
}
if err := verifier.Verify(ctx, logE, tempFilePath); err != nil {
return fmt.Errorf("verify the asset: %w", err)
}
}

if err := is.verifyChecksumWrap(ctx, logE, param, bodyFile); err != nil {
Expand Down
55 changes: 33 additions & 22 deletions pkg/installpackage/verify_cosign.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,49 @@ import (
"context"
"fmt"

"github.com/aquaproj/aqua/v2/pkg/config"
"github.com/aquaproj/aqua/v2/pkg/config/registry"
"github.com/aquaproj/aqua/v2/pkg/download"
"github.com/aquaproj/aqua/v2/pkg/runtime"
"github.com/sirupsen/logrus"
)

func (is *Installer) verifyWithCosign(ctx context.Context, logE *logrus.Entry, bodyFile *download.DownloadedFile, param *DownloadParam) error {
if is.cosignDisabled {
type cosignVerifier struct {
cosignDisabled bool
pkg *config.Package
cosign *registry.Cosign
installer *DedicatedInstaller
verifier CosignVerifier
runtime *runtime.Runtime
asset string
}

func (c *cosignVerifier) Enabled(logE *logrus.Entry) (bool, error) {
if c.cosignDisabled {
logE.Debug("cosign is disabled")
return nil
return false, nil
}

ppkg := param.Package

cos := ppkg.PackageInfo.Cosign
if !cos.GetEnabled() {
return nil
}
return c.cosign.GetEnabled(), nil
}

art := ppkg.TemplateArtifact(is.runtime, param.Asset)
logE.Info("verify a package with Cosign")
if err := is.cosignInstaller.install(ctx, logE); err != nil {
func (c *cosignVerifier) Verify(ctx context.Context, logE *logrus.Entry, file string) error {
logE.Info("verifying a file with Cosign")
if err := c.installer.install(ctx, logE); err != nil {
return fmt.Errorf("install sigstore/cosign: %w", err)
}
tempFilePath, err := bodyFile.Path()
if err != nil {
return fmt.Errorf("get a temporary file path: %w", err)
}
if err := is.cosign.Verify(ctx, logE, is.runtime, &download.File{
RepoOwner: ppkg.PackageInfo.RepoOwner,
RepoName: ppkg.PackageInfo.RepoName,
Version: ppkg.Package.Version,
}, cos, art, tempFilePath); err != nil {
return fmt.Errorf("verify a package with Cosign: %w", err)

pkg := c.pkg
cos := c.cosign

art := pkg.TemplateArtifact(c.runtime, c.asset)

if err := c.verifier.Verify(ctx, logE, c.runtime, &download.File{
RepoOwner: pkg.PackageInfo.RepoOwner,
RepoName: pkg.PackageInfo.RepoName,
Version: pkg.Package.Version,
}, cos, art, file); err != nil {
return fmt.Errorf("verify a file with Cosign: %w", err)
}
return nil
}
35 changes: 20 additions & 15 deletions pkg/installpackage/verify_github_artifact_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,38 @@ import (

"github.com/aquaproj/aqua/v2/pkg/config"
"github.com/aquaproj/aqua/v2/pkg/config/registry"
"github.com/aquaproj/aqua/v2/pkg/download"
"github.com/aquaproj/aqua/v2/pkg/ghattestation"
"github.com/sirupsen/logrus"
)

func (is *Installer) verifyWithGitHubArtifactAttestation(ctx context.Context, logE *logrus.Entry, pkg *config.Package, gaa *registry.GitHubArtifactAttestations, bodyFile *download.DownloadedFile) error {
if !gaa.GetEnabled() {
return nil
}
type FileVerifier interface {
Enabled(logE *logrus.Entry) (bool, error)
Verify(ctx context.Context, logE *logrus.Entry, file string) error
}

tempFilePath, err := bodyFile.Path()
if err != nil {
return fmt.Errorf("get a temporary file path: %w", err)
}
type gitHubArtifactAttestationsVerifier struct {
gaa *registry.GitHubArtifactAttestations
pkg *config.Package
ghInstaller *DedicatedInstaller
ghVerifier GitHubArtifactAttestationsVerifier
}

func (g *gitHubArtifactAttestationsVerifier) Enabled(_ *logrus.Entry) (bool, error) {
return g.gaa.GetEnabled(), nil
}

func (g *gitHubArtifactAttestationsVerifier) Verify(ctx context.Context, logE *logrus.Entry, file string) error {
logE.Info("verify GitHub Artifact Attestations")
if err := is.ghInstaller.install(ctx, logE); err != nil {
if err := g.ghInstaller.install(ctx, logE); err != nil {
return fmt.Errorf("install GitHub CLI: %w", err)
}

if err := is.ghVerifier.Verify(ctx, logE, &ghattestation.ParamVerify{
Repository: pkg.PackageInfo.RepoOwner + "/" + pkg.PackageInfo.RepoName,
ArtifactPath: tempFilePath,
SignerWorkflow: gaa.SignerWorkflow,
if err := g.ghVerifier.Verify(ctx, logE, &ghattestation.ParamVerify{
Repository: g.pkg.PackageInfo.RepoOwner + "/" + g.pkg.PackageInfo.RepoName,
ArtifactPath: file,
SignerWorkflow: g.gaa.SignerWorkflow,
}); err != nil {
return fmt.Errorf("verify a package with minisign: %w", err)
}

return nil
}
Loading

0 comments on commit 991e38d

Please sign in to comment.