mirror of
https://github.com/hauler-dev/hauler.git
synced 2026-08-27 14:07:17 +00:00
add cosign drop-in funcs
Signed-off-by: Adam Martin <adam.martin@rancherfederal.com>
This commit is contained in:
+238
-7
@@ -2,14 +2,39 @@ package cosign
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"oras.land/oras-go/pkg/content"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"context"
|
||||
"strings"
|
||||
"encoding/json"
|
||||
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"oras.land/oras-go/pkg/content"
|
||||
"github.com/rancherfederal/hauler/pkg/store"
|
||||
"github.com/rancherfederal/hauler/pkg/log"
|
||||
"github.com/rancherfederal/hauler/internal/mapper"
|
||||
"github.com/rancherfederal/hauler/pkg/reference"
|
||||
"github.com/rancherfederal/hauler/pkg/artifacts/file"
|
||||
"github.com/rancherfederal/hauler/pkg/artifacts/file/getter"
|
||||
"github.com/rancherfederal/hauler/pkg/apis/hauler.cattle.io/v1alpha1"
|
||||
)
|
||||
|
||||
// VerifyFileSignature verifies the digital signature of a file using Sigstore/Cosign.
|
||||
func VerifySignature(filePath string, keyPath string) error {
|
||||
func VerifySignature(ctx context.Context, s *store.Layout, keyPath string) error {
|
||||
|
||||
// Ensure that the cosign binary is installed or download it if needed
|
||||
cosignBinaryPath, err := ensureCosignBinary(ctx, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Command to verify the signature using Cosign.
|
||||
cmd := exec.Command("cosign", "verify", "--insecure-ignore-tlog", "--key", keyPath, filePath)
|
||||
cmd := exec.Command(cosignBinaryPath, "verify", "--insecure-ignore-tlog", "--key", keyPath, s.Root)
|
||||
|
||||
// Run the command and capture its output.
|
||||
output, err := cmd.CombinedOutput()
|
||||
@@ -21,9 +46,17 @@ func VerifySignature(filePath string, keyPath string) error {
|
||||
}
|
||||
|
||||
// SaveImage saves image and any signatures/attestations to the store.
|
||||
func SaveImage(storePath string, ref string) error {
|
||||
func SaveImage(ctx context.Context, s *store.Layout, ref string) error {
|
||||
|
||||
// Ensure that the cosign binary is installed or download it if needed
|
||||
cosignBinaryPath, err := ensureCosignBinary(ctx, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println(cosignBinaryPath)
|
||||
|
||||
// Command to verify the signature using Cosign.
|
||||
cmd := exec.Command("cosign", "save", ref, "--dir", storePath)
|
||||
cmd := exec.Command("/Users/amartin/go/bin/cosign", "save", ref, "--dir", s.Root)
|
||||
|
||||
// Run the command and capture its output.
|
||||
output, err := cmd.CombinedOutput()
|
||||
@@ -35,9 +68,17 @@ func SaveImage(storePath string, ref string) error {
|
||||
}
|
||||
|
||||
// LoadImage loads store to a remote registry.
|
||||
func LoadImage(storePath string, registry string, ropts content.RegistryOptions) error {
|
||||
func LoadImage(ctx context.Context, s *store.Layout, registry string, ropts content.RegistryOptions) error {
|
||||
|
||||
//Ensure that the cosign binary is installed or download it if needed
|
||||
cosignBinaryPath, err := ensureCosignBinary(ctx, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println(cosignBinaryPath)
|
||||
|
||||
// Command to verify the signature using Cosign.
|
||||
cmd := exec.Command("cosign", "load", "--registry", registry, "--dir", storePath)
|
||||
cmd := exec.Command("/Users/amartin/go/bin/cosign", "load", "--registry", registry, "--dir", s.Root)
|
||||
|
||||
// Conditionally add extra registry flags.
|
||||
if ropts.Insecure {
|
||||
@@ -55,3 +96,193 @@ func LoadImage(storePath string, registry string, ropts content.RegistryOptions)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureCosignBinary checks if the cosign binary exists in the specified directory and installs it if not.
|
||||
func ensureCosignBinary(ctx context.Context, s *store.Layout) (string, error) {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
// Get the current user's information
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error: %v\n", err)
|
||||
}
|
||||
|
||||
// Get the user's home directory
|
||||
homeDir := currentUser.HomeDir
|
||||
|
||||
// Construct the path to the .hauler directory
|
||||
haulerDir := filepath.Join(homeDir, ".hauler")
|
||||
|
||||
// Create the .hauler directory if it doesn't exist
|
||||
if _, err := os.Stat(haulerDir); os.IsNotExist(err) {
|
||||
// .hauler directory does not exist, create it
|
||||
if err := os.MkdirAll(haulerDir, 0755); err != nil {
|
||||
return "", fmt.Errorf("Error creating .hauler directory: %v\n", err)
|
||||
}
|
||||
l.Infof("Created .hauler directory at: %s", haulerDir)
|
||||
}
|
||||
|
||||
// Check if the cosign binary exists in the specified directory.
|
||||
binaryPath := filepath.Join(haulerDir, "cosign")
|
||||
_, err = os.Stat(binaryPath)
|
||||
if err == nil {
|
||||
// Cosign binary is already installed in the specified directory.
|
||||
return binaryPath, nil
|
||||
}
|
||||
|
||||
// Cosign binary is not found.
|
||||
l.Infof("Cosign binary not found. Checking to see if it exists in the store...")
|
||||
|
||||
// grab binary from store if it exists, otherwise try to download it from GitHub.
|
||||
// if the binary has to be downloaded, then automatically add it to the store afterwards.
|
||||
err = copyCosignFromStore(ctx, s, haulerDir)
|
||||
if err != nil {
|
||||
l.Warnf("%s", err)
|
||||
err = downloadCosign(ctx, haulerDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = addCosignToStore(ctx, s, binaryPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return binaryPath, nil
|
||||
}
|
||||
|
||||
// used to check if the cosign binary is in the store and if so copy it to the .hauler directory
|
||||
func copyCosignFromStore(ctx context.Context, s *store.Layout, destDir string) error {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
ref := "hauler/cosign:latest"
|
||||
r, err := reference.Parse(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found := false
|
||||
if err := s.Walk(func(reference string, desc ocispec.Descriptor) error {
|
||||
|
||||
if !strings.Contains(reference, r.Name()) {
|
||||
return nil
|
||||
}
|
||||
found = true
|
||||
|
||||
rc, err := s.Fetch(ctx, desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
var m ocispec.Manifest
|
||||
if err := json.NewDecoder(rc).Decode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mapperStore, err := mapper.FromManifest(m, destDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pushedDesc, err := s.Copy(ctx, reference, mapperStore, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Infof("extracted [%s] from store with digest [%s]", ref, pushedDesc.Digest.String())
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !found {
|
||||
return fmt.Errorf("Reference [%s] not found in store. Hauler will attempt to download it from Github.", ref)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// adds the cosign binary to the store.
|
||||
// this is to help with airgapped situations where you cannot access the internet.
|
||||
func addCosignToStore(ctx context.Context, s *store.Layout, binaryPath string) error {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
fi := v1alpha1.File{
|
||||
Path: binaryPath,
|
||||
}
|
||||
|
||||
copts := getter.ClientOptions{
|
||||
NameOverride: fi.Name,
|
||||
}
|
||||
|
||||
f := file.NewFile(fi.Path, file.WithClient(getter.NewClient(copts)))
|
||||
ref, err := reference.NewTagged(f.Name(fi.Path), reference.DefaultTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
desc, err := s.AddOCI(ctx, f, ref.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Infof("added 'file' to store at [%s], with digest [%s]", ref.Name(), desc.Digest.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// used to check if the cosign binary is in the store and if so copy it to the .hauler directory
|
||||
func downloadCosign(ctx context.Context, haulerDir string) error {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
// Define the GitHub release URL and architecture-specific binary name.
|
||||
releaseURL := "https://github.com/rancher-government-solutions/cosign/releases/latest/download"
|
||||
|
||||
// Determine the architecture and add it to the binary name.
|
||||
arch := runtime.GOARCH
|
||||
rOS := runtime.GOOS
|
||||
binaryName := "cosign"
|
||||
if rOS == "windows" {
|
||||
binaryName = fmt.Sprintf("cosign-%s-%s.exe", rOS, arch)
|
||||
} else {
|
||||
binaryName = fmt.Sprintf("cosign-%s-%s", rOS, arch)
|
||||
}
|
||||
|
||||
// Download the binary.
|
||||
downloadURL := fmt.Sprintf("%s/%s", releaseURL, binaryName)
|
||||
resp, err := http.Get(downloadURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error downloading cosign binary: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Create the cosign binary file in the specified directory.
|
||||
binaryFile, err := os.Create(filepath.Join(haulerDir, binaryName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating cosign binary: %v", err)
|
||||
}
|
||||
defer binaryFile.Close()
|
||||
|
||||
// Copy the downloaded binary to the file.
|
||||
_, err = io.Copy(binaryFile, resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error saving cosign binary: %v", err)
|
||||
}
|
||||
|
||||
// Make the binary executable.
|
||||
if err := os.Chmod(binaryFile.Name(), 0755); err != nil {
|
||||
return fmt.Errorf("error setting executable permission: %v", err)
|
||||
}
|
||||
|
||||
// Rename the binary to "cosign"
|
||||
oldBinaryPath := filepath.Join(haulerDir, binaryName)
|
||||
newBinaryPath := filepath.Join(haulerDir, "cosign")
|
||||
if err := os.Rename(oldBinaryPath, newBinaryPath); err != nil {
|
||||
return fmt.Errorf("error renaming cosign binary: %v", err)
|
||||
}
|
||||
|
||||
l.Infof("Cosign binary downloaded and installed to %s", haulerDir)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user