mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-01-23 05:41:19 -05:00
image: add go code to upload image info and measurements
This commit is contained in:
parent
b8751f35f9
commit
217a744606
@ -9,6 +9,11 @@ go_library(
|
||||
"flags.go",
|
||||
"gcp.go",
|
||||
"image.go",
|
||||
"info.go",
|
||||
"measurements.go",
|
||||
"measurementsenvelope.go",
|
||||
"measurementsmerge.go",
|
||||
"measurementsupload.go",
|
||||
"must.go",
|
||||
"nop.go",
|
||||
"openstack.go",
|
||||
@ -19,6 +24,7 @@ go_library(
|
||||
importpath = "github.com/edgelesssys/constellation/v2/image/upload/internal/cmd",
|
||||
visibility = ["//image/upload:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/attestation/measurements",
|
||||
"//internal/cloud/cloudprovider",
|
||||
"//internal/logger",
|
||||
"//internal/osimage",
|
||||
@ -26,6 +32,8 @@ go_library(
|
||||
"//internal/osimage/aws",
|
||||
"//internal/osimage/azure",
|
||||
"//internal/osimage/gcp",
|
||||
"//internal/osimage/imageinfo",
|
||||
"//internal/osimage/measurementsuploader",
|
||||
"//internal/osimage/nop",
|
||||
"//internal/osimage/secureboot",
|
||||
"//internal/versionsapi",
|
||||
|
@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
@ -198,3 +199,143 @@ func parseGCPFlags(cmd *cobra.Command) (gcpFlags, error) {
|
||||
gcpBucket: gcpBucket,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type s3Flags struct {
|
||||
region string
|
||||
bucket string
|
||||
logLevel zapcore.Level
|
||||
}
|
||||
|
||||
func parseS3Flags(cmd *cobra.Command) (s3Flags, error) {
|
||||
region, err := cmd.Flags().GetString("region")
|
||||
if err != nil {
|
||||
return s3Flags{}, err
|
||||
}
|
||||
bucket, err := cmd.Flags().GetString("bucket")
|
||||
if err != nil {
|
||||
return s3Flags{}, err
|
||||
}
|
||||
verbose, err := cmd.Flags().GetBool("verbose")
|
||||
if err != nil {
|
||||
return s3Flags{}, err
|
||||
}
|
||||
logLevel := zapcore.InfoLevel
|
||||
if verbose {
|
||||
logLevel = zapcore.DebugLevel
|
||||
}
|
||||
|
||||
return s3Flags{
|
||||
region: region,
|
||||
bucket: bucket,
|
||||
logLevel: logLevel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type measurementsFlags struct {
|
||||
s3Flags
|
||||
measurementsPath string
|
||||
signaturePath string
|
||||
}
|
||||
|
||||
func parseUploadMeasurementsFlags(cmd *cobra.Command) (measurementsFlags, error) {
|
||||
s3, err := parseS3Flags(cmd)
|
||||
if err != nil {
|
||||
return measurementsFlags{}, err
|
||||
}
|
||||
|
||||
measurementsPath, err := cmd.Flags().GetString("measurements")
|
||||
if err != nil {
|
||||
return measurementsFlags{}, err
|
||||
}
|
||||
signaturePath, err := cmd.Flags().GetString("signature")
|
||||
if err != nil {
|
||||
return measurementsFlags{}, err
|
||||
}
|
||||
|
||||
return measurementsFlags{
|
||||
s3Flags: s3,
|
||||
measurementsPath: measurementsPath,
|
||||
signaturePath: signaturePath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type mergeMeasurementsFlags struct {
|
||||
out string
|
||||
logLevel zapcore.Level
|
||||
}
|
||||
|
||||
func parseMergeMeasurementsFlags(cmd *cobra.Command) (mergeMeasurementsFlags, error) {
|
||||
out, err := cmd.Flags().GetString("out")
|
||||
if err != nil {
|
||||
return mergeMeasurementsFlags{}, err
|
||||
}
|
||||
verbose, err := cmd.Flags().GetBool("verbose")
|
||||
if err != nil {
|
||||
return mergeMeasurementsFlags{}, err
|
||||
}
|
||||
logLevel := zapcore.InfoLevel
|
||||
if verbose {
|
||||
logLevel = zapcore.DebugLevel
|
||||
}
|
||||
|
||||
return mergeMeasurementsFlags{
|
||||
out: out,
|
||||
logLevel: logLevel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type envelopeMeasurementsFlags struct {
|
||||
version versionsapi.Version
|
||||
csp cloudprovider.Provider
|
||||
attestationVariant string
|
||||
in, out string
|
||||
logLevel zapcore.Level
|
||||
}
|
||||
|
||||
func parseEnvelopeMeasurementsFlags(cmd *cobra.Command) (envelopeMeasurementsFlags, error) {
|
||||
version, err := cmd.Flags().GetString("version")
|
||||
if err != nil {
|
||||
return envelopeMeasurementsFlags{}, err
|
||||
}
|
||||
ver, err := versionsapi.NewVersionFromShortPath(version, versionsapi.VersionKindImage)
|
||||
if err != nil {
|
||||
return envelopeMeasurementsFlags{}, err
|
||||
}
|
||||
csp, err := cmd.Flags().GetString("csp")
|
||||
if err != nil {
|
||||
return envelopeMeasurementsFlags{}, err
|
||||
}
|
||||
provider := cloudprovider.FromString(csp)
|
||||
attestationVariant, err := cmd.Flags().GetString("attestation-variant")
|
||||
if err != nil {
|
||||
return envelopeMeasurementsFlags{}, err
|
||||
}
|
||||
if provider == cloudprovider.Unknown {
|
||||
return envelopeMeasurementsFlags{}, errors.New("unknown cloud provider")
|
||||
}
|
||||
in, err := cmd.Flags().GetString("in")
|
||||
if err != nil {
|
||||
return envelopeMeasurementsFlags{}, err
|
||||
}
|
||||
out, err := cmd.Flags().GetString("out")
|
||||
if err != nil {
|
||||
return envelopeMeasurementsFlags{}, err
|
||||
}
|
||||
verbose, err := cmd.Flags().GetBool("verbose")
|
||||
if err != nil {
|
||||
return envelopeMeasurementsFlags{}, err
|
||||
}
|
||||
logLevel := zapcore.InfoLevel
|
||||
if verbose {
|
||||
logLevel = zapcore.DebugLevel
|
||||
}
|
||||
|
||||
return envelopeMeasurementsFlags{
|
||||
version: ver,
|
||||
csp: provider,
|
||||
attestationVariant: attestationVariant,
|
||||
in: in,
|
||||
out: out,
|
||||
logLevel: logLevel,
|
||||
}, nil
|
||||
}
|
||||
|
83
image/upload/internal/cmd/info.go
Normal file
83
image/upload/internal/cmd/info.go
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
infoupload "github.com/edgelesssys/constellation/v2/internal/osimage/imageinfo"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewInfoCmd creates a new info parent command.
|
||||
func NewInfoCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "info [flags] <image-info.json>...",
|
||||
Short: "Uploads OS image info to S3",
|
||||
Long: "Uploads OS image info to S3.",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: runInfo,
|
||||
}
|
||||
|
||||
cmd.SetOut(os.Stdout)
|
||||
|
||||
cmd.Flags().String("region", "eu-central-1", "AWS region of the archive S3 bucket")
|
||||
cmd.Flags().String("bucket", "cdn-constellation-backend", "S3 bucket name of the archive")
|
||||
cmd.Flags().Bool("verbose", false, "Enable verbose output")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInfo(cmd *cobra.Command, args []string) error {
|
||||
workdir := os.Getenv("BUILD_WORKING_DIRECTORY")
|
||||
if len(workdir) > 0 {
|
||||
must(os.Chdir(workdir))
|
||||
}
|
||||
|
||||
flags, err := parseS3Flags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log := logger.New(logger.PlainLog, flags.logLevel)
|
||||
log.Debugf("Parsed flags: %+v", flags)
|
||||
info, err := readInfoArgs(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uploadC, err := infoupload.New(cmd.Context(), flags.region, flags.bucket, log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image info: %w", err)
|
||||
}
|
||||
|
||||
url, err := uploadC.Upload(cmd.Context(), info)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image info: %w", err)
|
||||
}
|
||||
log.Infof("Uploaded image info to %s", url)
|
||||
return nil
|
||||
}
|
||||
|
||||
func readInfoArgs(paths []string) (versionsapi.ImageInfo, error) {
|
||||
infos := make([]versionsapi.ImageInfo, len(paths))
|
||||
for i, path := range paths {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return versionsapi.ImageInfo{}, err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := json.NewDecoder(f).Decode(&infos[i]); err != nil {
|
||||
return versionsapi.ImageInfo{}, err
|
||||
}
|
||||
}
|
||||
return versionsapi.MergeImageInfos(infos...)
|
||||
}
|
32
image/upload/internal/cmd/measurements.go
Normal file
32
image/upload/internal/cmd/measurements.go
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewMeasurementsCmd creates a new measurements command. Measurements needs another
|
||||
// verb, and does nothing on its own.
|
||||
func NewMeasurementsCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "measurements",
|
||||
Short: "Handle OS image measurements",
|
||||
Long: "Handle OS image measurements.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
}
|
||||
|
||||
cmd.SetOut(os.Stdout)
|
||||
|
||||
cmd.AddCommand(newMeasurementsUploadCmd())
|
||||
cmd.AddCommand(newMeasurementsMergeCmd())
|
||||
cmd.AddCommand(newMeasurementsEnvelopeCmd())
|
||||
|
||||
return cmd
|
||||
}
|
101
image/upload/internal/cmd/measurementsenvelope.go
Normal file
101
image/upload/internal/cmd/measurementsenvelope.go
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// newMeasurementsEnvelopeCmd creates a new envelope command.
|
||||
func newMeasurementsEnvelopeCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "envelope",
|
||||
Short: "Envelope OS image measurements",
|
||||
Long: "Envelope OS image measurements for one variant to follow the measurements v2 format.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: runEnvelopeMeasurements,
|
||||
}
|
||||
|
||||
cmd.SetOut(os.Stdout)
|
||||
cmd.Flags().String("version", "", "Shortname of the os image version.")
|
||||
cmd.Flags().String("csp", "", "CSP of this image measurement.")
|
||||
cmd.Flags().String("attestation-variant", "", "Attestation variant of the image measurements.")
|
||||
cmd.Flags().String("in", "", "Path to read the raw measurements from.")
|
||||
cmd.Flags().String("out", "", "Optional path to write the enveloped result to. If not set, the result is written to stdout.")
|
||||
cmd.Flags().Bool("verbose", false, "Enable verbose output")
|
||||
|
||||
must(cmd.MarkFlagRequired("version"))
|
||||
must(cmd.MarkFlagRequired("csp"))
|
||||
must(cmd.MarkFlagRequired("attestation-variant"))
|
||||
must(cmd.MarkFlagRequired("in"))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runEnvelopeMeasurements(cmd *cobra.Command, _ []string) error {
|
||||
workdir := os.Getenv("BUILD_WORKING_DIRECTORY")
|
||||
if len(workdir) > 0 {
|
||||
must(os.Chdir(workdir))
|
||||
}
|
||||
|
||||
flags, err := parseEnvelopeMeasurementsFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log := logger.New(logger.PlainLog, flags.logLevel)
|
||||
log.Debugf("Parsed flags: %+v", flags)
|
||||
|
||||
f, err := os.Open(flags.in)
|
||||
if err != nil {
|
||||
return fmt.Errorf("enveloping measurements: opening input file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
var measuremnt rawMeasurements
|
||||
if err := json.NewDecoder(f).Decode(&measuremnt); err != nil {
|
||||
return fmt.Errorf("enveloping measurements: reading input file: %w", err)
|
||||
}
|
||||
|
||||
enveloped := measurements.ImageMeasurementsV2{
|
||||
Ref: flags.version.Ref,
|
||||
Stream: flags.version.Stream,
|
||||
Version: flags.version.Version,
|
||||
List: []measurements.ImageMeasurementsV2Entry{
|
||||
{
|
||||
CSP: flags.csp,
|
||||
AttestationVariant: flags.attestationVariant,
|
||||
Measurements: measuremnt.Measurements,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out := cmd.OutOrStdout()
|
||||
if len(flags.out) > 0 {
|
||||
outF, err := os.Create(flags.out)
|
||||
if err != nil {
|
||||
return fmt.Errorf("enveloping measurements: opening output file: %w", err)
|
||||
}
|
||||
defer outF.Close()
|
||||
out = outF
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(out).Encode(enveloped); err != nil {
|
||||
return fmt.Errorf("enveloping measurements: writing output file: %w", err)
|
||||
}
|
||||
log.Infof("Enveloped image measurements")
|
||||
return nil
|
||||
}
|
||||
|
||||
type rawMeasurements struct {
|
||||
Measurements measurements.M `json:"measurements"`
|
||||
}
|
85
image/upload/internal/cmd/measurementsmerge.go
Normal file
85
image/upload/internal/cmd/measurementsmerge.go
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// newMeasurementsMergeCmd creates a new merge command.
|
||||
func newMeasurementsMergeCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "merge [flags] <measurements.json>...",
|
||||
Short: "Merge OS image measurements",
|
||||
Long: "Merge OS image measurements.",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: runMergeMeasurements,
|
||||
}
|
||||
|
||||
cmd.SetOut(os.Stdout)
|
||||
cmd.Flags().String("out", "", "Optional path to write the merge result to. If not set, the result is written to stdout.")
|
||||
cmd.Flags().Bool("verbose", false, "Enable verbose output")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runMergeMeasurements(cmd *cobra.Command, args []string) error {
|
||||
workdir := os.Getenv("BUILD_WORKING_DIRECTORY")
|
||||
if len(workdir) > 0 {
|
||||
must(os.Chdir(workdir))
|
||||
}
|
||||
|
||||
flags, err := parseMergeMeasurementsFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log := logger.New(logger.PlainLog, flags.logLevel)
|
||||
log.Debugf("Parsed flags: %+v", flags)
|
||||
|
||||
mergedMeasurements, err := readMeasurementsArgs(args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("merging measurements: reading input files: %w", err)
|
||||
}
|
||||
|
||||
out := cmd.OutOrStdout()
|
||||
if len(flags.out) > 0 {
|
||||
outF, err := os.Create(flags.out)
|
||||
if err != nil {
|
||||
return fmt.Errorf("merging measurements: opening output file: %w", err)
|
||||
}
|
||||
defer outF.Close()
|
||||
out = outF
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(out).Encode(mergedMeasurements); err != nil {
|
||||
return fmt.Errorf("merging measurements: writing output file: %w", err)
|
||||
}
|
||||
log.Infof("Merged image measurements")
|
||||
return nil
|
||||
}
|
||||
|
||||
func readMeasurementsArgs(paths []string) (measurements.ImageMeasurementsV2, error) {
|
||||
measuremnts := make([]measurements.ImageMeasurementsV2, len(paths))
|
||||
for i, path := range paths {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return measurements.ImageMeasurementsV2{}, err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := json.NewDecoder(f).Decode(&measuremnts[i]); err != nil {
|
||||
return measurements.ImageMeasurementsV2{}, err
|
||||
}
|
||||
}
|
||||
return measurements.MergeImageMeasurementsV2(measuremnts...)
|
||||
}
|
78
image/upload/internal/cmd/measurementsupload.go
Normal file
78
image/upload/internal/cmd/measurementsupload.go
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/osimage/measurementsuploader"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// newMeasurementsUploadCmd creates a new upload command.
|
||||
func newMeasurementsUploadCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "upload",
|
||||
Short: "Uploads OS image measurements to S3",
|
||||
Long: "Uploads OS image measurements to S3.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: runMeasurementsUpload,
|
||||
}
|
||||
|
||||
cmd.SetOut(os.Stdout)
|
||||
|
||||
cmd.Flags().String("measurements", "", "Path to measurements file to upload")
|
||||
cmd.Flags().String("signature", "", "Path to signature file to upload")
|
||||
cmd.Flags().String("region", "eu-central-1", "AWS region of the archive S3 bucket")
|
||||
cmd.Flags().String("bucket", "cdn-constellation-backend", "S3 bucket name of the archive")
|
||||
cmd.Flags().Bool("verbose", false, "Enable verbose output")
|
||||
|
||||
must(cmd.MarkFlagRequired("measurements"))
|
||||
must(cmd.MarkFlagRequired("signature"))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runMeasurementsUpload(cmd *cobra.Command, _ []string) error {
|
||||
workdir := os.Getenv("BUILD_WORKING_DIRECTORY")
|
||||
if len(workdir) > 0 {
|
||||
must(os.Chdir(workdir))
|
||||
}
|
||||
|
||||
flags, err := parseUploadMeasurementsFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log := logger.New(logger.PlainLog, flags.logLevel)
|
||||
log.Debugf("Parsed flags: %+v", flags)
|
||||
|
||||
uploadC, err := measurementsuploader.New(cmd.Context(), flags.region, flags.bucket, log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image info: %w", err)
|
||||
}
|
||||
|
||||
measurements, err := os.Open(flags.measurementsPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image measurements: opening measurements file: %w", err)
|
||||
}
|
||||
defer measurements.Close()
|
||||
signature, err := os.Open(flags.signaturePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image measurements: opening signature file: %w", err)
|
||||
}
|
||||
defer signature.Close()
|
||||
|
||||
measurementsURL, signatureURL, err := uploadC.Upload(cmd.Context(), measurements, signature)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uploading image info: %w", err)
|
||||
}
|
||||
log.Infof("Uploaded image measurements to %s (and signature to %s)", measurementsURL, signatureURL)
|
||||
return nil
|
||||
}
|
@ -41,6 +41,8 @@ func newRootCmd() *cobra.Command {
|
||||
rootCmd.SetOut(os.Stdout)
|
||||
|
||||
rootCmd.AddCommand(cmd.NewImageCmd())
|
||||
rootCmd.AddCommand(cmd.NewInfoCmd())
|
||||
rootCmd.AddCommand(cmd.NewMeasurementsCmd())
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ go_library(
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/archive",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/constants",
|
||||
"//internal/logger",
|
||||
"//internal/versionsapi",
|
||||
"@com_github_aws_aws_sdk_go_v2_config//:config",
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
s3manager "github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
)
|
||||
@ -58,11 +59,9 @@ func (a *Archivist) Archive(ctx context.Context, version versionsapi.Version, cs
|
||||
Body: img,
|
||||
ChecksumAlgorithm: s3types.ChecksumAlgorithmSha256,
|
||||
})
|
||||
return baseURL + key, err
|
||||
return constants.CDNRepositoryURL + "/" + key, err
|
||||
}
|
||||
|
||||
type uploadClient interface {
|
||||
Upload(ctx context.Context, input *s3.PutObjectInput, opts ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error)
|
||||
}
|
||||
|
||||
const baseURL = "https://cdn.confidential.cloud/"
|
||||
|
17
internal/osimage/imageinfo/BUILD.bazel
Normal file
17
internal/osimage/imageinfo/BUILD.bazel
Normal file
@ -0,0 +1,17 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "imageinfo",
|
||||
srcs = ["imageinfo.go"],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/imageinfo",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/constants",
|
||||
"//internal/logger",
|
||||
"//internal/versionsapi",
|
||||
"@com_github_aws_aws_sdk_go_v2_config//:config",
|
||||
"@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_s3//types",
|
||||
],
|
||||
)
|
78
internal/osimage/imageinfo/imageinfo.go
Normal file
78
internal/osimage/imageinfo/imageinfo.go
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// package imageinfo is used to upload image info JSON files to S3.
|
||||
package imageinfo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
s3manager "github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
)
|
||||
|
||||
// Uploader uploads image info to S3.
|
||||
type Uploader struct {
|
||||
uploadClient uploadClient
|
||||
// bucket is the name of the S3 bucket to use.
|
||||
bucket string
|
||||
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
// New creates a new Uploader.
|
||||
func New(ctx context.Context, region, bucket string, log *logger.Logger) (*Uploader, error) {
|
||||
cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(region))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s3client := s3.NewFromConfig(cfg)
|
||||
uploadClient := s3manager.NewUploader(s3client)
|
||||
|
||||
return &Uploader{
|
||||
uploadClient: uploadClient,
|
||||
bucket: bucket,
|
||||
log: log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Upload marshals the image info to JSON and uploads it to S3.
|
||||
func (a *Uploader) Upload(ctx context.Context, imageInfo versionsapi.ImageInfo) (string, error) {
|
||||
ver := versionsapi.Version{
|
||||
Ref: imageInfo.Ref,
|
||||
Stream: imageInfo.Stream,
|
||||
Version: imageInfo.Version,
|
||||
Kind: versionsapi.VersionKindImage,
|
||||
}
|
||||
key, err := url.JoinPath(ver.ArtifactPath(versionsapi.APIV2), ver.Kind.String(), "info.json")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
a.log.Debugf("Archiving image info to s3://%v/%v", a.bucket, key)
|
||||
buf := &bytes.Buffer{}
|
||||
if err := json.NewEncoder(buf).Encode(imageInfo); err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = a.uploadClient.Upload(ctx, &s3.PutObjectInput{
|
||||
Bucket: &a.bucket,
|
||||
Key: &key,
|
||||
Body: buf,
|
||||
ChecksumAlgorithm: s3types.ChecksumAlgorithmSha256,
|
||||
})
|
||||
return constants.CDNRepositoryURL + "/" + key, err
|
||||
}
|
||||
|
||||
type uploadClient interface {
|
||||
Upload(ctx context.Context, input *s3.PutObjectInput, opts ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error)
|
||||
}
|
18
internal/osimage/measurementsuploader/BUILD.bazel
Normal file
18
internal/osimage/measurementsuploader/BUILD.bazel
Normal file
@ -0,0 +1,18 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "measurementsuploader",
|
||||
srcs = ["measurementsuploader.go"],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/measurementsuploader",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/attestation/measurements",
|
||||
"//internal/constants",
|
||||
"//internal/logger",
|
||||
"//internal/versionsapi",
|
||||
"@com_github_aws_aws_sdk_go_v2_config//:config",
|
||||
"@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_s3//types",
|
||||
],
|
||||
)
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// package measurementsuploader is used to upload measurements (v2) JSON files (and signatures) to S3.
|
||||
package measurementsuploader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
s3manager "github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
)
|
||||
|
||||
// Uploader uploads image info to S3.
|
||||
type Uploader struct {
|
||||
uploadClient uploadClient
|
||||
// bucket is the name of the S3 bucket to use.
|
||||
bucket string
|
||||
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
// New creates a new Uploader.
|
||||
func New(ctx context.Context, region, bucket string, log *logger.Logger) (*Uploader, error) {
|
||||
cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(region))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s3client := s3.NewFromConfig(cfg)
|
||||
uploadClient := s3manager.NewUploader(s3client)
|
||||
|
||||
return &Uploader{
|
||||
uploadClient: uploadClient,
|
||||
bucket: bucket,
|
||||
log: log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Upload uploads the measurements v2 JSON file and its signature to S3.
|
||||
func (a *Uploader) Upload(ctx context.Context, rawMeasurement, signature io.ReadSeeker) (string, string, error) {
|
||||
// parse the measurements to get the ref, stream, and version
|
||||
var measurements measurements.ImageMeasurementsV2
|
||||
if err := json.NewDecoder(rawMeasurement).Decode(&measurements); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if _, err := rawMeasurement.Seek(0, io.SeekStart); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
ver := versionsapi.Version{
|
||||
Ref: measurements.Ref,
|
||||
Stream: measurements.Stream,
|
||||
Version: measurements.Version,
|
||||
Kind: versionsapi.VersionKindImage,
|
||||
}
|
||||
key, err := url.JoinPath(ver.ArtifactPath(versionsapi.APIV2), ver.Kind.String(), "measurements.json")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
sigKey, err := url.JoinPath(ver.ArtifactPath(versionsapi.APIV2), ver.Kind.String(), "measurements.json.sig")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
a.log.Debugf("Archiving image measurements to s3://%v/%v and s3://%v/%v", a.bucket, key, a.bucket, sigKey)
|
||||
if _, err = a.uploadClient.Upload(ctx, &s3.PutObjectInput{
|
||||
Bucket: &a.bucket,
|
||||
Key: &key,
|
||||
Body: rawMeasurement,
|
||||
ChecksumAlgorithm: s3types.ChecksumAlgorithmSha256,
|
||||
}); err != nil {
|
||||
return "", "", fmt.Errorf("uploading measurements: %w", err)
|
||||
}
|
||||
if _, err = a.uploadClient.Upload(ctx, &s3.PutObjectInput{
|
||||
Bucket: &a.bucket,
|
||||
Key: &sigKey,
|
||||
Body: signature,
|
||||
ChecksumAlgorithm: s3types.ChecksumAlgorithmSha256,
|
||||
}); err != nil {
|
||||
return "", "", fmt.Errorf("uploading measurements signature: %w", err)
|
||||
}
|
||||
return constants.CDNRepositoryURL + "/" + key, constants.CDNRepositoryURL + "/" + sigKey, nil
|
||||
}
|
||||
|
||||
type uploadClient interface {
|
||||
Upload(ctx context.Context, input *s3.PutObjectInput, opts ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error)
|
||||
}
|
Loading…
Reference in New Issue
Block a user