config: dynamic attestation configuration through S3 backed API (#1808)

This commit is contained in:
Adrian Stobbe 2023-05-25 17:43:44 +01:00 committed by GitHub
parent 25211dc154
commit 0a6e5ec02e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
92 changed files with 1020 additions and 302 deletions

View File

@ -13,11 +13,11 @@ RUN go mod download
COPY . .
# Build
WORKDIR /workspace/internal/versionsapi/cli
WORKDIR /workspace/internal/api/versionsapi/cli
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o versionsapi .
FROM scratch as release
COPY --from=builder /workspace/internal/versionsapi/cli/versionsapi .
COPY --from=builder /workspace/internal/api/versionsapi/cli/versionsapi .
CMD ["/notIntendedToBeExecuted"]

View File

@ -6,7 +6,7 @@ on:
branches:
- main
paths:
- "internal/versionsapi/**"
- "internal/api/versionsapi/**"
- ".github/workflows/build-versionsapi-ci-image.yml"
- ".github/actions/versionsapi/**"

View File

@ -36,7 +36,7 @@
/internal/retry @katexochen
/internal/sigstore @malt3
/internal/versions @katexochen
/internal/versionsapi @katexochen
/internal/api/versionsapi @katexochen
/joinservice @daniel-weisse
/keyservice @daniel-weisse
/operators @malt3

View File

@ -46,6 +46,8 @@ go_library(
"//cli/internal/terraform",
"//cli/internal/upgrade",
"//disk-mapper/recoverproto",
"//internal/api/fetcher",
"//internal/api/versionsapi",
"//internal/atls",
"//internal/attestation/measurements",
"//internal/cloud/azureshared",
@ -71,8 +73,6 @@ go_library(
"//internal/sigstore",
"//internal/variant",
"//internal/versions",
"//internal/versionsapi",
"//internal/versionsapi/fetcher",
"//operators/constellation-node-operator/api/v1alpha1",
"//verify/verifyproto",
"@com_github_mattn_go_isatty//:go-isatty",
@ -126,6 +126,7 @@ go_test(
"//cli/internal/terraform",
"//cli/internal/upgrade",
"//disk-mapper/recoverproto",
"//internal/api/versionsapi",
"//internal/atls",
"//internal/attestation/measurements",
"//internal/cloud/cloudprovider",
@ -142,7 +143,6 @@ go_test(
"//internal/logger",
"//internal/variant",
"//internal/versions",
"//internal/versionsapi",
"//operators/constellation-node-operator/api/v1alpha1",
"//verify/verifyproto",
"@com_github_spf13_afero//:afero",

View File

@ -14,12 +14,12 @@ import (
"net/url"
"time"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/sigstore"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/spf13/afero"
"github.com/spf13/cobra"
)
@ -77,7 +77,7 @@ func (cfm *configFetchMeasurementsCmd) configFetchMeasurements(
cfm.log.Debugf("Using flags %v", flags)
cfm.log.Debugf("Loading configuration file from %q", flags.configPath)
conf, err := config.New(fileHandler, flags.configPath, flags.force)
conf, err := config.NewWithClient(fileHandler, flags.configPath, client, flags.force)
var configValidationErr *config.ValidationError
if errors.As(err, &configValidationErr) {
cmd.PrintErrln(configValidationErr.LongMessage())

View File

@ -15,12 +15,12 @@ import (
"net/url"
"testing"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -17,6 +17,8 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/helm"
"github.com/edgelesssys/constellation/v2/cli/internal/kubernetes"
"github.com/edgelesssys/constellation/v2/internal/api/fetcher"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility"
@ -28,8 +30,6 @@ import (
"github.com/edgelesssys/constellation/v2/internal/sigstore"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versions"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/versionsapi/fetcher"
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
"github.com/spf13/afero"
"github.com/spf13/cobra"
@ -67,7 +67,7 @@ func runUpgradeCheck(cmd *cobra.Command, _ []string) error {
if err != nil {
return err
}
versionListFetcher := fetcher.NewFetcher()
versionListFetcher := fetcher.NewVersionAPIFetcher()
rekor, err := sigstore.NewRekor()
if err != nil {
return fmt.Errorf("constructing Rekor client: %w", err)
@ -83,7 +83,7 @@ func runUpgradeCheck(cmd *cobra.Command, _ []string) error {
flags: flags,
cliVersion: compatibility.EnsurePrefixV(constants.VersionInfo()),
log: log,
versionsapi: fetcher.NewFetcher(),
versionsapi: fetcher.NewVersionAPIFetcher(),
},
log: log,
}

View File

@ -15,6 +15,7 @@ import (
"strings"
"testing"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
@ -22,7 +23,6 @@ import (
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -14,6 +14,7 @@ go_library(
"//cli/internal/helm",
"//cli/internal/terraform",
"//cli/internal/upgrade",
"//internal/api/versionsapi",
"//internal/attestation/measurements",
"//internal/cloud/cloudprovider",
"//internal/compatibility",
@ -26,7 +27,6 @@ go_library(
"//internal/variant",
"//internal/versions",
"//internal/versions/components",
"//internal/versionsapi",
"//operators/constellation-node-operator/api/v1alpha1",
"@io_k8s_api//core/v1:core",
"@io_k8s_apimachinery//pkg/api/errors",

View File

@ -18,6 +18,7 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/helm"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/cli/internal/upgrade"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility"
@ -30,7 +31,6 @@ import (
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versions"
"github.com/edgelesssys/constellation/v2/internal/versions/components"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
updatev1alpha1 "github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api/v1alpha1"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"

View File

@ -22,7 +22,7 @@ With `cdbg` and `yq` installed in your path:
1. Run `constellation config generate` to create a new default configuration
2. Locate the latest debugd images by running `(cd internal/versionsapi/cli && go build -o versionsapi . && ./versionsapi latest --ref main --stream debug)`
2. Locate the latest debugd images by running `(cd internal/api/versionsapi/cli && go build -o versionsapi . && ./versionsapi latest --ref main --stream debug)`
3. Modify the `constellation-conf.yaml` to use an image with the debugd already included and add required firewall rules:

View File

@ -11,13 +11,13 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/e2e/internal/upgrade",
visibility = ["//e2e:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//internal/attestation/measurements",
"//internal/cloud/cloudprovider",
"//internal/constants",
"//internal/imagefetcher",
"//internal/logger",
"//internal/variant",
"//internal/versionsapi",
"@sh_helm_helm_v3//pkg/action",
"@sh_helm_helm_v3//pkg/cli",
],

View File

@ -12,11 +12,11 @@ import (
"context"
"net/http"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/imagefetcher"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
type upgradeInfo struct {

View File

@ -6,10 +6,10 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/hack/cli-k8s-compatibility",
visibility = ["//visibility:private"],
deps = [
"//internal/api/versionsapi",
"//internal/api/versionsapi/client",
"//internal/logger",
"//internal/versions",
"//internal/versionsapi",
"//internal/versionsapi/client",
"@org_uber_go_zap//zapcore",
],
)

View File

@ -11,10 +11,10 @@ import (
"context"
"flag"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi/client"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/versions"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/versionsapi/client"
"go.uber.org/zap/zapcore"
)

View File

@ -0,0 +1,19 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_library(
name = "configapi_lib",
srcs = ["main.go"],
importpath = "github.com/edgelesssys/constellation/v2/hack/configapi",
visibility = ["//visibility:private"],
deps = [
"//internal/api/configapi",
"//internal/kms/uri",
"@com_github_spf13_cobra//:cobra",
],
)
go_binary(
name = "upload",
embed = [":configapi_lib"],
visibility = ["//visibility:public"],
)

91
hack/configapi/main.go Normal file
View File

@ -0,0 +1,91 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package main
import (
"context"
"fmt"
"time"
"github.com/edgelesssys/constellation/v2/internal/api/configapi"
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
"github.com/spf13/cobra"
)
const (
awsRegion = "eu-central-1"
awsBucket = "cdn-constellation-backend"
invalidDefault = 0
)
var (
// AWS S3 credentials.
awsAccessKeyID string
awsAccessKey string
// Azure SEV-SNP version numbers.
bootloaderVersion uint8
teeVersion uint8
snpVersion uint8
microcodeVersion uint8
)
func handleError(err error) {
if err != nil {
panic(err)
}
}
func main() {
myCmd := &cobra.Command{
Use: "upload a set of versions specific to the azure-sev-snp attestation variant to the config api",
Short: "upload a set of versions specific to the azure-sev-snp attestation variant to the config api",
Run: func(cmd *cobra.Command, args []string) {
ctx := context.Background()
cfg := uri.AWSS3Config{
Bucket: awsBucket,
AccessKeyID: awsAccessKeyID,
AccessKey: awsAccessKey,
Region: awsRegion,
}
sut, err := configapi.NewAttestationVersionRepo(ctx, cfg)
if err != nil {
panic(err)
}
versions := configapi.AzureSEVSNPVersion{
Bootloader: bootloaderVersion,
TEE: teeVersion,
SNP: snpVersion,
Microcode: microcodeVersion,
}
if err := sut.UploadAzureSEVSNP(ctx, versions, time.Now()); err != nil {
panic(err)
} else {
fmt.Println("Successfully uploaded version numbers", versions)
}
},
}
myCmd.PersistentFlags().Uint8VarP(&bootloaderVersion, "bootloader-version", "b", invalidDefault, "Bootloader version number")
handleError(myCmd.MarkPersistentFlagRequired("bootloader-version"))
myCmd.PersistentFlags().Uint8VarP(&teeVersion, "tee-version", "t", invalidDefault, "TEE version number")
handleError(myCmd.MarkPersistentFlagRequired("tee-version"))
myCmd.PersistentFlags().Uint8VarP(&snpVersion, "snp-version", "s", invalidDefault, "SNP version number")
handleError(myCmd.MarkPersistentFlagRequired("snp-version"))
myCmd.PersistentFlags().Uint8VarP(&microcodeVersion, "microcode-version", "m", invalidDefault, "Microcode version number")
handleError(myCmd.MarkPersistentFlagRequired("microcode-version"))
myCmd.PersistentFlags().StringVar(&awsAccessKeyID, "key-id", "", "ID of the Access key to use for AWS tests. Required for AWS KMS and storage test.")
handleError(myCmd.MarkPersistentFlagRequired("key-id"))
myCmd.PersistentFlags().StringVar(&awsAccessKey, "key", "", "Access key to use for AWS tests. Required for AWS KMS and storage test.")
handleError(myCmd.MarkPersistentFlagRequired("key"))
handleError(myCmd.Execute())
}

View File

@ -24,6 +24,7 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/image/upload/internal/cmd",
visibility = ["//image/upload:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//internal/attestation/measurements",
"//internal/cloud/cloudprovider",
"//internal/logger",
@ -36,7 +37,6 @@ go_library(
"//internal/osimage/measurementsuploader",
"//internal/osimage/nop",
"//internal/osimage/secureboot",
"//internal/versionsapi",
"@com_github_spf13_afero//:afero",
"@com_github_spf13_cobra//:cobra",
"@org_uber_go_zap//zapcore",

View File

@ -10,8 +10,8 @@ import (
"context"
"io"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/osimage"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
type archivist interface {

View File

@ -12,8 +12,8 @@ import (
"path/filepath"
"time"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/spf13/cobra"
"go.uber.org/zap/zapcore"
)

View File

@ -11,9 +11,9 @@ import (
"fmt"
"os"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"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"
)

View File

@ -13,8 +13,8 @@ import (
"io"
"strings"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/osimage"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
func uploadImage(ctx context.Context, archiveC archivist, uploadC uploader, req *osimage.UploadRequest, out io.Writer) error {

View File

@ -0,0 +1,36 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("//bazel/go:go_test.bzl", "go_test")
go_library(
name = "configapi",
srcs = [
"attestation.go",
"attestationversion.go",
"configapi.go",
"repo.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/api/configapi",
visibility = ["//:__subpackages__"],
deps = [
"//internal/constants",
"//internal/kms/storage",
"//internal/kms/storage/awss3",
"//internal/kms/uri",
"//internal/variant",
],
)
go_test(
name = "configapi_test",
srcs = [
"attestationversion_test.go",
"repo_test.go",
],
embed = [":configapi"],
deps = [
"//internal/kms/uri",
"//internal/variant",
"@com_github_stretchr_testify//require",
"@in_gopkg_yaml_v3//:yaml_v3",
],
)

View File

@ -0,0 +1,99 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package configapi
import (
"fmt"
"net/url"
"path"
"strings"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/variant"
)
// attestationURLPath is the URL path to the attestation versions.
const attestationURLPath = "constellation/v1/attestation"
// AzureSEVSNPVersionType is the type of the version to be requested.
type AzureSEVSNPVersionType string
// AzureSEVSNPVersion tracks the latest version of each component of the Azure SEVSNP.
type AzureSEVSNPVersion struct {
// Bootloader is the latest version of the Azure SEVSNP bootloader.
Bootloader uint8 `json:"bootloader"`
// TEE is the latest version of the Azure SEVSNP TEE.
TEE uint8 `json:"tee"`
// SNP is the latest version of the Azure SEVSNP SNP.
SNP uint8 `json:"snp"`
// Microcode is the latest version of the Azure SEVSNP microcode.
Microcode uint8 `json:"microcode"`
}
// AzureSEVSNPVersionGet is the request to get the version information of the specific version in the config api.
type AzureSEVSNPVersionGet struct {
Version string `json:"-"`
AzureSEVSNPVersion
}
// URL returns the URL for the request to the config api.
func (i AzureSEVSNPVersionGet) URL() (string, error) {
url, err := url.Parse(constants.CDNRepositoryURL)
if err != nil {
return "", fmt.Errorf("parsing CDN URL: %w", err)
}
url.Path = i.JSONPath()
return url.String(), nil
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i AzureSEVSNPVersionGet) JSONPath() string {
return path.Join(attestationURLPath, variant.AzureSEVSNP{}.String(), i.Version)
}
// ValidateRequest validates the request.
func (i AzureSEVSNPVersionGet) ValidateRequest() error {
if !strings.HasSuffix(i.Version, ".json") {
return fmt.Errorf("version has no .json suffix")
}
return nil
}
// Validate is a No-Op at the moment.
func (i AzureSEVSNPVersionGet) Validate() error {
return nil
}
// AzureSEVSNPVersionList is the request to list all versions in the config api.
type AzureSEVSNPVersionList []string
// URL returns the URL for the request to the config api.
func (i AzureSEVSNPVersionList) URL() (string, error) {
url, err := url.Parse(constants.CDNRepositoryURL)
if err != nil {
return "", fmt.Errorf("parsing CDN URL: %w", err)
}
url.Path = i.JSONPath()
return url.String(), nil
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i AzureSEVSNPVersionList) JSONPath() string {
return path.Join(attestationURLPath, variant.AzureSEVSNP{}.String(), "list")
}
// ValidateRequest is a NoOp as there is no input.
func (i AzureSEVSNPVersionList) ValidateRequest() error {
return nil
}
// Validate validates the response.
func (i AzureSEVSNPVersionList) Validate() error {
if len(i) < 1 {
return fmt.Errorf("no versions found in /list")
}
return nil
}

View File

@ -0,0 +1,81 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package configapi
import (
"encoding/json"
"fmt"
"strings"
)
const placeholderVersionValue = 0
// NewLatestPlaceholderVersion returns the latest version with a placeholder version value.
func NewLatestPlaceholderVersion() AttestationVersion {
return AttestationVersion{
Value: placeholderVersionValue,
IsLatest: true,
}
}
// AttestationVersion is a type that represents a version of a SNP.
type AttestationVersion struct {
Value uint8
IsLatest bool
}
// MarshalYAML implements a custom marshaller to resolve "latest" values.
func (v AttestationVersion) MarshalYAML() (any, error) {
if v.IsLatest {
return "latest", nil
}
return v.Value, nil
}
// UnmarshalYAML implements a custom unmarshaller to resolve "atest" values.
func (v *AttestationVersion) UnmarshalYAML(unmarshal func(any) error) error {
var rawUnmarshal any
if err := unmarshal(&rawUnmarshal); err != nil {
return fmt.Errorf("raw unmarshal: %w", err)
}
return v.parseRawUnmarshal(rawUnmarshal)
}
// MarshalJSON implements a custom marshaller to resolve "latest" values.
func (v AttestationVersion) MarshalJSON() ([]byte, error) {
if v.IsLatest {
return json.Marshal("latest")
}
return json.Marshal(v.Value)
}
// UnmarshalJSON implements a custom unmarshaller to resolve "latest" values.
func (v *AttestationVersion) UnmarshalJSON(data []byte) (err error) {
var rawUnmarshal any
if err := json.Unmarshal(data, &rawUnmarshal); err != nil {
return fmt.Errorf("raw unmarshal: %w", err)
}
return v.parseRawUnmarshal(rawUnmarshal)
}
func (v *AttestationVersion) parseRawUnmarshal(rawUnmarshal any) error {
switch s := rawUnmarshal.(type) {
case string:
if strings.ToLower(s) == "latest" {
v.IsLatest = true
v.Value = placeholderVersionValue
} else {
return fmt.Errorf("invalid version value: %s", s)
}
case int:
v.Value = uint8(s)
default:
return fmt.Errorf("invalid version value type: %s", s)
}
return nil
}

View File

@ -0,0 +1,46 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package configapi
import (
"testing"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
func TestVersionMarshalYAML(t *testing.T) {
tests := []struct {
name string
sut AttestationVersion
want string
}{
{
name: "isLatest resolves to latest",
sut: AttestationVersion{
Value: 1,
IsLatest: true,
},
want: "latest\n",
},
{
name: "value 5 resolves to 5",
sut: AttestationVersion{
Value: 5,
IsLatest: false,
},
want: "5\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bt, err := yaml.Marshal(tt.sut)
require.NoError(t, err)
require.Equal(t, tt.want, string(bt))
})
}
}

View File

@ -0,0 +1,20 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
/*
# Config API
The Config API provides information about versions of Constellation components.
This package defines API types that represents objects of the config API.
The types provide helper methods for validation and commonly used operations on the
information contained in the objects. Especially the paths used for the API are defined
in these helper methods.
The package also provides helper functions that can be used in context of the config API,
e.g. to validate versions.
*/
package configapi

View File

@ -0,0 +1,96 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package configapi
import (
"context"
"encoding/json"
"errors"
"fmt"
"path"
"sort"
"time"
"github.com/edgelesssys/constellation/v2/internal/kms/storage"
"github.com/edgelesssys/constellation/v2/internal/kms/storage/awss3"
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
"github.com/edgelesssys/constellation/v2/internal/variant"
)
// AttestationVersionRepo manages (modifies) the version information for the attestation variants.
type AttestationVersionRepo struct {
*awss3.Storage
}
// NewAttestationVersionRepo returns a new AttestationVersionRepo.
func NewAttestationVersionRepo(ctx context.Context, cfg uri.AWSS3Config) (*AttestationVersionRepo, error) {
s3, err := awss3.New(ctx, cfg)
if err != nil {
return nil, fmt.Errorf("failed to create s3 storage: %w", err)
}
return &AttestationVersionRepo{s3}, nil
}
// UploadAzureSEVSNP uploads the latest version numbers of the Azure SEVSNP.
func (a AttestationVersionRepo) UploadAzureSEVSNP(ctx context.Context, versions AzureSEVSNPVersion, date time.Time) error {
bt, err := json.Marshal(versions)
if err != nil {
return err
}
variant := variant.AzureSEVSNP{}
fname := date.Format("2006-01-02-15-04") + ".json"
err = a.Put(ctx, fmt.Sprintf("%s/%s/%s", attestationURLPath, variant.String(), fname), bt)
if err != nil {
return err
}
return a.addVersionToList(ctx, variant, fname)
}
// List returns the list of versions for the given attestation type.
func (a AttestationVersionRepo) List(ctx context.Context, attestation variant.Variant) ([]string, error) {
key := path.Join(attestationURLPath, attestation.String(), "list")
bt, err := a.Get(ctx, key)
if err != nil {
return nil, err
}
var versions []string
if err := json.Unmarshal(bt, &versions); err != nil {
return nil, err
}
return versions, nil
}
// DeleteList empties the list of versions for the given attestation type.
func (a AttestationVersionRepo) DeleteList(ctx context.Context, attestation variant.Variant) error {
versions := []string{}
bt, err := json.Marshal(&versions)
if err != nil {
return err
}
return a.Put(ctx, path.Join(attestationURLPath, attestation.String(), "list"), bt)
}
func (a AttestationVersionRepo) addVersionToList(ctx context.Context, attestation variant.Variant, fname string) error {
versions := []string{}
key := path.Join(attestationURLPath, attestation.String(), "list")
bt, err := a.Get(ctx, key)
if err == nil {
if err := json.Unmarshal(bt, &versions); err != nil {
return err
}
} else if !errors.Is(err, storage.ErrDEKUnset) {
return err
}
versions = append(versions, fname)
versions = variant.RemoveDuplicate(versions)
sort.Sort(sort.Reverse(sort.StringSlice(versions)))
json, err := json.Marshal(versions)
if err != nil {
return err
}
return a.Put(ctx, key, json)
}

View File

@ -0,0 +1,85 @@
//go:build e2e
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package configapi_test
import (
"context"
"flag"
"fmt"
"os"
"testing"
"time"
"github.com/edgelesssys/constellation/v2/internal/api/configapi"
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/stretchr/testify/require"
)
var (
awsRegion = flag.String("aws-region", "us-east-1", "Region to use for AWS tests. Required for AWS KMS test.")
awsAccessKeyID = flag.String("aws-access-key-id", "", "ID of the Access key to use for AWS tests. Required for AWS KMS and storage test.")
awsAccessKey = flag.String("aws-access-key", "", "Access key to use for AWS tests. Required for AWS KMS and storage test.")
awsBucket = flag.String("aws-bucket", "", "Name of the S3 bucket to use for AWS storage test. Required for AWS storage test.")
)
func TestMain(m *testing.M) {
flag.Parse()
if *awsAccessKey == "" || *awsAccessKeyID == "" || *awsBucket == "" || *awsRegion == "" {
flag.Usage()
fmt.Println("Required flags not set: --aws-access-key, --aws-access-key-id, --aws-bucket, --aws-region. Skipping tests.")
os.Exit(0)
}
os.Exit(m.Run())
}
var cfg = uri.AWSS3Config{
Bucket: *awsBucket,
AccessKeyID: *awsAccessKeyID,
AccessKey: *awsAccessKey,
Region: *awsRegion,
}
var versionValues = configapi.AzureSEVSNPVersion{
Bootloader: 2,
TEE: 0,
SNP: 6,
Microcode: 93,
}
func TestUploadAzureSEVSNPVersions(t *testing.T) {
ctx := context.Background()
sut, err := configapi.NewAttestationVersionRepo(ctx, cfg)
require.NoError(t, err)
d := time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC)
require.NoError(t, sut.UploadAzureSEVSNP(ctx, versionValues, d))
}
func TestListVersions(t *testing.T) {
ctx := context.Background()
sut, err := configapi.NewAttestationVersionRepo(ctx, cfg)
require.NoError(t, err)
err = sut.DeleteList(ctx, variant.AzureSEVSNP{})
require.NoError(t, err)
res, err := sut.List(ctx, variant.AzureSEVSNP{})
require.NoError(t, err)
require.Equal(t, []string{}, res)
d := time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC)
err = sut.UploadAzureSEVSNP(ctx, versionValues, d)
require.NoError(t, err)
res, err = sut.List(ctx, variant.AzureSEVSNP{})
require.NoError(t, err)
require.Equal(t, []string{"2021-01-01-01-01.json"}, res)
err = sut.DeleteList(ctx, variant.AzureSEVSNP{})
require.NoError(t, err)
}

View File

@ -3,18 +3,29 @@ load("//bazel/go:go_test.bzl", "go_test")
go_library(
name = "fetcher",
srcs = ["fetcher.go"],
importpath = "github.com/edgelesssys/constellation/v2/internal/versionsapi/fetcher",
srcs = [
"configapi.go",
"fetcher.go",
"versionapi.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/api/fetcher",
visibility = ["//:__subpackages__"],
deps = ["//internal/versionsapi"],
deps = [
"//internal/api/configapi",
"//internal/api/versionsapi",
],
)
go_test(
name = "fetcher_test",
srcs = ["fetcher_test.go"],
srcs = [
"configapi_test.go",
"versionapi_test.go",
],
embed = [":fetcher"],
deps = [
"//internal/versionsapi",
"//internal/api/configapi",
"//internal/api/versionsapi",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_uber_go_goleak//:goleak",

View File

@ -0,0 +1,55 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package fetcher
import (
"context"
"fmt"
"github.com/edgelesssys/constellation/v2/internal/api/configapi"
)
// ConfigAPIFetcher fetches config API resources without authentication.
type ConfigAPIFetcher struct {
*fetcher
}
// NewConfigAPIFetcher returns a new Fetcher.
func NewConfigAPIFetcher() *ConfigAPIFetcher {
return &ConfigAPIFetcher{newFetcher()}
}
// NewConfigAPIFetcherWithClient returns a new Fetcher with custom http client.
func NewConfigAPIFetcherWithClient(client HTTPClient) *ConfigAPIFetcher {
return &ConfigAPIFetcher{newFetcherWith(client)}
}
// FetchAzureSEVSNPVersionList fetches the version list information from the config API.
func (f *ConfigAPIFetcher) FetchAzureSEVSNPVersionList(ctx context.Context, attestation configapi.AzureSEVSNPVersionList) (configapi.AzureSEVSNPVersionList, error) {
return fetch(ctx, f.httpc, attestation)
}
// FetchAzureSEVSNPVersion fetches the version information from the config API.
func (f *ConfigAPIFetcher) FetchAzureSEVSNPVersion(ctx context.Context, attestation configapi.AzureSEVSNPVersionGet) (configapi.AzureSEVSNPVersionGet, error) {
// TODO(elchead): follow-up PR for AB#3045 to check signature (sigstore.VerifySignature)
return fetch(ctx, f.httpc, attestation)
}
// FetchLatestAzureSEVSNPVersion returns the latest versions of the given type.
func (f *ConfigAPIFetcher) FetchLatestAzureSEVSNPVersion(ctx context.Context) (res configapi.AzureSEVSNPVersion, err error) {
var versions configapi.AzureSEVSNPVersionList
versions, err = f.FetchAzureSEVSNPVersionList(ctx, versions)
if err != nil {
return res, fmt.Errorf("fetching versions list: %w", err)
}
get := configapi.AzureSEVSNPVersionGet{Version: versions[0]} // get latest version (as sorted reversely alphanumerically)
get, err = f.FetchAzureSEVSNPVersion(ctx, get)
if err != nil {
return res, fmt.Errorf("failed fetching version: %w", err)
}
return get.AzureSEVSNPVersion, nil
}

View File

@ -0,0 +1,65 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package fetcher
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"net/http"
"testing"
"github.com/edgelesssys/constellation/v2/internal/api/configapi"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetVersion(t *testing.T) {
client := &http.Client{
Transport: &fakeConfigAPIHandler{},
}
fetcher := NewConfigAPIFetcherWithClient(client)
res, err := fetcher.FetchLatestAzureSEVSNPVersion(context.Background())
require.NoError(t, err)
assert.Equal(t, uint8(2), res.Bootloader)
}
type fakeConfigAPIHandler struct{}
// RoundTrip resolves the request and returns a dummy response.
func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, error) {
if req.URL.Path == "/constellation/v1/attestation/azure-sev-snp/list" {
res := &http.Response{}
data := []string{"2021-01-01-01-01.json"}
bt, err := json.Marshal(data)
if err != nil {
return nil, err
}
res.Body = io.NopCloser(bytes.NewReader(bt))
res.Header = http.Header{}
res.Header.Set("Content-Type", "application/json")
res.StatusCode = http.StatusOK
return res, nil
} else if req.URL.Path == "/constellation/v1/attestation/azure-sev-snp/2021-01-01-01-01.json" {
res := &http.Response{}
bt, err := json.Marshal(configapi.AzureSEVSNPVersion{
Microcode: 93,
TEE: 0,
SNP: 6,
Bootloader: 2,
})
if err != nil {
return nil, err
}
res.Body = io.NopCloser(bytes.NewReader(bt))
res.StatusCode = http.StatusOK
return res, nil
}
return nil, errors.New("no endpoint found")
}

View File

@ -18,40 +18,26 @@ import (
"encoding/json"
"fmt"
"net/http"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
// Fetcher fetches versions API resources without authentication.
type Fetcher struct {
httpc httpc
// fetcher fetches versions API resources without authentication.
type fetcher struct {
httpc HTTPClient
}
// NewFetcher returns a new Fetcher.
func NewFetcher() *Fetcher {
return &Fetcher{
httpc: http.DefaultClient,
// NewHTTPClient returns a new http client.
func NewHTTPClient() HTTPClient {
return &http.Client{Transport: &http.Transport{DisableKeepAlives: true}} // DisableKeepAlives fixes concurrency issue see https://stackoverflow.com/a/75816347
}
func newFetcherWith(client HTTPClient) *fetcher {
return &fetcher{
httpc: client,
}
}
// FetchVersionList fetches the given version list from the versions API.
func (f *Fetcher) FetchVersionList(ctx context.Context, list versionsapi.List) (versionsapi.List, error) {
return fetch(ctx, f.httpc, list)
}
// FetchVersionLatest fetches the latest version from the versions API.
func (f *Fetcher) FetchVersionLatest(ctx context.Context, latest versionsapi.Latest) (versionsapi.Latest, error) {
return fetch(ctx, f.httpc, latest)
}
// FetchImageInfo fetches the given image info from the versions API.
func (f *Fetcher) FetchImageInfo(ctx context.Context, imageInfo versionsapi.ImageInfo) (versionsapi.ImageInfo, error) {
return fetch(ctx, f.httpc, imageInfo)
}
// FetchCLIInfo fetches the given cli info from the versions API.
func (f *Fetcher) FetchCLIInfo(ctx context.Context, cliInfo versionsapi.CLIInfo) (versionsapi.CLIInfo, error) {
return fetch(ctx, f.httpc, cliInfo)
func newFetcher() *fetcher {
return newFetcherWith(NewHTTPClient()) // DisableKeepAlives fixes concurrency issue see https://stackoverflow.com/a/75816347
}
type apiObject interface {
@ -60,7 +46,7 @@ type apiObject interface {
URL() (string, error)
}
func fetch[T apiObject](ctx context.Context, c httpc, obj T) (T, error) {
func fetch[T apiObject](ctx context.Context, c HTTPClient, obj T) (T, error) {
if err := obj.ValidateRequest(); err != nil {
return *new(T), fmt.Errorf("validating request for %T: %w", obj, err)
}
@ -113,6 +99,7 @@ func (e *NotFoundError) Unwrap() error {
return e.err
}
type httpc interface {
// HTTPClient is an interface for http clients.
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}

View File

@ -0,0 +1,43 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package fetcher
import (
"context"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
)
// VersionAPIFetcher fetches version API resources without authentication.
type VersionAPIFetcher struct {
*fetcher
}
// NewVersionAPIFetcher returns a new Fetcher.
func NewVersionAPIFetcher() *VersionAPIFetcher {
return &VersionAPIFetcher{newFetcher()}
}
// FetchVersionList fetches the given version list from the versions API.
func (f *VersionAPIFetcher) FetchVersionList(ctx context.Context, list versionsapi.List) (versionsapi.List, error) {
return fetch(ctx, f.httpc, list)
}
// FetchVersionLatest fetches the latest version from the versions API.
func (f *VersionAPIFetcher) FetchVersionLatest(ctx context.Context, latest versionsapi.Latest) (versionsapi.Latest, error) {
return fetch(ctx, f.httpc, latest)
}
// FetchImageInfo fetches the given image info from the versions API.
func (f *VersionAPIFetcher) FetchImageInfo(ctx context.Context, imageInfo versionsapi.ImageInfo) (versionsapi.ImageInfo, error) {
return fetch(ctx, f.httpc, imageInfo)
}
// FetchCLIInfo fetches the given cli info from the versions API.
func (f *VersionAPIFetcher) FetchCLIInfo(ctx context.Context, cliInfo versionsapi.CLIInfo) (versionsapi.CLIInfo, error) {
return fetch(ctx, f.httpc, cliInfo)
}

View File

@ -14,7 +14,7 @@ import (
"net/http"
"testing"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
@ -190,7 +190,7 @@ func TestFetchVersionList(t *testing.T) {
return tc.serverResp
})
fetcher := &Fetcher{httpc: client}
fetcher := VersionAPIFetcher{&fetcher{httpc: client}}
list, err := fetcher.FetchVersionList(context.Background(), tc.list)

View File

@ -12,7 +12,7 @@ go_library(
"version.go",
"versionsapi.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/versionsapi",
importpath = "github.com/edgelesssys/constellation/v2/internal/api/versionsapi",
visibility = ["//:__subpackages__"],
deps = [
"//internal/constants",

View File

@ -9,12 +9,12 @@ go_library(
"main.go",
"rm.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/versionsapi/cli",
importpath = "github.com/edgelesssys/constellation/v2/internal/api/versionsapi/cli",
visibility = ["//visibility:private"],
deps = [
"//internal/api/versionsapi",
"//internal/api/versionsapi/client",
"//internal/logger",
"//internal/versionsapi",
"//internal/versionsapi/client",
"@com_github_aws_aws_sdk_go_v2_config//:config",
"@com_github_aws_aws_sdk_go_v2_service_ec2//:ec2",
"@com_github_aws_smithy_go//:smithy-go",

View File

@ -11,9 +11,9 @@ import (
"errors"
"fmt"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
verclient "github.com/edgelesssys/constellation/v2/internal/api/versionsapi/client"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
verclient "github.com/edgelesssys/constellation/v2/internal/versionsapi/client"
"github.com/spf13/cobra"
"go.uber.org/zap/zapcore"
"golang.org/x/mod/semver"

View File

@ -10,9 +10,9 @@ import (
"encoding/json"
"fmt"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
verclient "github.com/edgelesssys/constellation/v2/internal/api/versionsapi/client"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
verclient "github.com/edgelesssys/constellation/v2/internal/versionsapi/client"
"github.com/spf13/cobra"
"go.uber.org/zap/zapcore"
)

View File

@ -16,9 +16,9 @@ import (
"go.uber.org/zap/zapcore"
"golang.org/x/mod/semver"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
verclient "github.com/edgelesssys/constellation/v2/internal/api/versionsapi/client"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
verclient "github.com/edgelesssys/constellation/v2/internal/versionsapi/client"
)
func newListCmd() *cobra.Command {

View File

@ -24,9 +24,9 @@ import (
awsconfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/smithy-go"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
verclient "github.com/edgelesssys/constellation/v2/internal/api/versionsapi/client"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
verclient "github.com/edgelesssys/constellation/v2/internal/versionsapi/client"
gaxv2 "github.com/googleapis/gax-go/v2"
"github.com/spf13/cobra"
"go.uber.org/zap/zapcore"

View File

@ -3,12 +3,12 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "client",
srcs = ["client.go"],
importpath = "github.com/edgelesssys/constellation/v2/internal/versionsapi/client",
importpath = "github.com/edgelesssys/constellation/v2/internal/api/versionsapi/client",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//internal/constants",
"//internal/logger",
"//internal/versionsapi",
"@com_github_aws_aws_sdk_go_v2//aws",
"@com_github_aws_aws_sdk_go_v2_config//:config",
"@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager",

View File

@ -42,9 +42,9 @@ import (
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
"golang.org/x/mod/semver"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
// Client is the client for the versions API.

View File

@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
/*
# Versions API
The Versions API is a provides information about versions of Constellation components.
The Versions API provides information about versions of Constellation components.
This package defines API types that represents objects of the versions API.
The types provide helper methods for validation and commonly used operations on the

View File

@ -148,7 +148,7 @@ func (v *Validator) validateSNPReport(
return errDebugEnabled
}
if !report.CommittedTCB.isVersion(v.config.BootloaderVersion, v.config.TEEVersion, v.config.SNPVersion, v.config.MicrocodeVersion) {
if !report.CommittedTCB.isVersion(v.config.BootloaderVersion.Value, v.config.TEEVersion.Value, v.config.SNPVersion.Value, v.config.MicrocodeVersion.Value) {
return &versionError{"COMMITTED_TCB", report.CommittedTCB}
}
if report.LaunchTCB != report.CommittedTCB {

View File

@ -348,7 +348,7 @@ func TestNewSNPReportFromBytes(t *testing.T) {
wantErr: true,
},
}
cfg := config.DefaultForAzureSEVSNP()
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
@ -363,8 +363,7 @@ func TestNewSNPReportFromBytes(t *testing.T) {
assert.NotNil(report)
assert.Equal(hex.EncodeToString(report.IDKeyDigest[:]), "57e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc1")
// This is a canary for us: If this fails in the future we possibly downgraded a SVN.
cfg := config.DefaultForAzureSEVSNP()
assert.True(report.LaunchTCB.isVersion(cfg.BootloaderVersion, cfg.TEEVersion, cfg.SNPVersion, cfg.MicrocodeVersion))
assert.True(report.LaunchTCB.isVersion(cfg.BootloaderVersion.Value, cfg.TEEVersion.Value, cfg.SNPVersion.Value, cfg.MicrocodeVersion.Value))
}
})
}

View File

@ -13,10 +13,10 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/measurements",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//internal/cloud/cloudprovider",
"//internal/sigstore",
"//internal/variant",
"//internal/versionsapi",
"@com_github_google_go_tpm//tpmutil",
"@com_github_siderolabs_talos_pkg_machinery//config/encoder",
"@in_gopkg_yaml_v3//:yaml_v3",
@ -28,9 +28,9 @@ go_test(
srcs = ["measurements_test.go"],
embed = [":measurements"],
deps = [
"//internal/api/versionsapi",
"//internal/cloud/cloudprovider",
"//internal/variant",
"//internal/versionsapi",
"@com_github_siderolabs_talos_pkg_machinery//config/encoder",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",

View File

@ -7,13 +7,13 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/measurements/measurement-generator",
visibility = ["//visibility:private"],
deps = [
"//internal/api/versionsapi",
"//internal/attestation/measurements",
"//internal/cloud/cloudprovider",
"//internal/config",
"//internal/constants",
"//internal/sigstore",
"//internal/variant",
"//internal/versionsapi",
"@org_golang_x_tools//go/ast/astutil",
],
)

View File

@ -23,13 +23,13 @@ import (
"sort"
"strings"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/sigstore"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
"golang.org/x/tools/go/ast/astutil"
)

View File

@ -32,10 +32,10 @@ import (
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
"gopkg.in/yaml.v3"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/sigstore"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
const (

View File

@ -20,9 +20,9 @@ import (
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
func TestMarshal(t *testing.T) {

View File

@ -18,18 +18,19 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/config",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/configapi",
"//internal/api/fetcher",
"//internal/api/versionsapi",
"//internal/attestation/idkeydigest",
"//internal/attestation/measurements",
"//internal/cloud/cloudprovider",
"//internal/compatibility",
"//internal/config/imageversion",
"//internal/config/instancetypes",
"//internal/config/snpversion",
"//internal/constants",
"//internal/file",
"//internal/variant",
"//internal/versions",
"//internal/versionsapi",
"@com_github_go_playground_locales//en",
"@com_github_go_playground_universal_translator//:universal-translator",
"@com_github_go_playground_validator_v10//:validator",
@ -49,6 +50,7 @@ go_test(
data = glob(["testdata/**"]),
embed = [":config"],
deps = [
"//internal/api/configapi",
"//internal/attestation/measurements",
"//internal/cloud/cloudprovider",
"//internal/config/instancetypes",

View File

@ -8,14 +8,14 @@ package config
import (
"bytes"
"context"
"fmt"
"strconv"
"strings"
"github.com/edgelesssys/constellation/v2/internal/api/configapi"
"github.com/edgelesssys/constellation/v2/internal/api/fetcher"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config/snpversion"
"github.com/edgelesssys/constellation/v2/internal/variant"
)
@ -26,16 +26,16 @@ type AzureSEVSNP struct {
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
// description: |
// Lowest acceptable bootloader version.
BootloaderVersion uint8 `json:"bootloaderVersion" yaml:"bootloaderVersion"`
BootloaderVersion configapi.AttestationVersion `json:"bootloaderVersion" yaml:"bootloaderVersion"`
// description: |
// Lowest acceptable TEE version.
TEEVersion uint8 `json:"teeVersion" yaml:"teeVersion"`
TEEVersion configapi.AttestationVersion `json:"teeVersion" yaml:"teeVersion"`
// description: |
// Lowest acceptable SEV-SNP version.
SNPVersion uint8 `json:"snpVersion" yaml:"snpVersion"`
SNPVersion configapi.AttestationVersion `json:"snpVersion" yaml:"snpVersion"`
// description: |
// Lowest acceptable microcode version.
MicrocodeVersion uint8 `json:"microcodeVersion" yaml:"microcodeVersion"`
MicrocodeVersion configapi.AttestationVersion `json:"microcodeVersion" yaml:"microcodeVersion"`
// description: |
// Configuration for validating the firmware signature.
FirmwareSignerConfig SNPFirmwareSignerConfig `json:"firmwareSignerConfig" yaml:"firmwareSignerConfig"`
@ -45,14 +45,14 @@ type AzureSEVSNP struct {
}
// DefaultForAzureSEVSNP returns the default configuration for Azure SEV-SNP attestation.
// Version numbers are hard coded and should be updated with each new release.
// Version numbers have placeholder values and the latest available values can be fetched using [AzureSEVSNP.FetchAndSetLatestVersionNumbers].
func DefaultForAzureSEVSNP() *AzureSEVSNP {
return &AzureSEVSNP{
Measurements: measurements.DefaultsFor(cloudprovider.Azure, variant.AzureSEVSNP{}),
BootloaderVersion: snpversion.GetLatest(snpversion.Bootloader),
TEEVersion: snpversion.GetLatest(snpversion.TEE),
SNPVersion: snpversion.GetLatest(snpversion.SNP),
MicrocodeVersion: snpversion.GetLatest(snpversion.Microcode),
BootloaderVersion: configapi.NewLatestPlaceholderVersion(),
TEEVersion: configapi.NewLatestPlaceholderVersion(),
SNPVersion: configapi.NewLatestPlaceholderVersion(),
MicrocodeVersion: configapi.NewLatestPlaceholderVersion(),
FirmwareSignerConfig: SNPFirmwareSignerConfig{
AcceptedKeyDigests: idkeydigest.DefaultList(),
EnforcementPolicy: idkeydigest.MAAFallback,
@ -95,26 +95,25 @@ func (c AzureSEVSNP) EqualTo(old AttestationCfg) (bool, error) {
return firmwareSignerCfgEqual && measurementsEqual && bootloaderEqual && teeEqual && snpEqual && microcodeEqual && rootKeyEqual, nil
}
// UnmarshalYAML implements a custom unmarshaler to support setting "latest" as version.
func (c *AzureSEVSNP) UnmarshalYAML(unmarshal func(any) error) error {
aux := &fusedAzureSEVSNP{
auxAzureSEVSNP: (*auxAzureSEVSNP)(c),
}
if err := unmarshal(aux); err != nil {
return fmt.Errorf("unmarshal AzureSEVSNP: %w", err)
}
c = (*AzureSEVSNP)(aux.auxAzureSEVSNP)
for _, versionType := range []snpversion.Type{snpversion.Bootloader, snpversion.TEE, snpversion.SNP, snpversion.Microcode} {
if !convertLatestToNumber(c, versionType, aux) {
if err := convertStringToUint(c, versionType, aux); err != nil {
return fmt.Errorf("convert %s version to number: %w", versionType, err)
}
}
// FetchAndSetLatestVersionNumbers fetches the latest version numbers from the configapi and sets them.
func (c *AzureSEVSNP) FetchAndSetLatestVersionNumbers(client fetcher.HTTPClient) error {
fetcher := fetcher.NewConfigAPIFetcherWithClient(client)
versions, err := fetcher.FetchLatestAzureSEVSNPVersion(context.Background())
if err != nil {
return err
}
// set number and keep isLatest flag
c.mergeVersionNumbers(versions)
return nil
}
func (c *AzureSEVSNP) mergeVersionNumbers(versions configapi.AzureSEVSNPVersion) {
c.BootloaderVersion.Value = versions.Bootloader
c.TEEVersion.Value = versions.TEE
c.SNPVersion.Value = versions.SNP
c.MicrocodeVersion.Value = versions.Microcode
}
// AzureTrustedLaunch is the configuration for Azure Trusted Launch attestation.
type AzureTrustedLaunch struct {
// description: |
@ -145,84 +144,3 @@ func (c AzureTrustedLaunch) EqualTo(other AttestationCfg) (bool, error) {
}
return c.Measurements.EqualTo(otherCfg.Measurements), nil
}
// auxAzureSEVSNP is a helper struct for unmarshaling the config from YAML for handling the version parsing.
// The version fields are kept to make it convertable to the native AzureSEVSNP struct.
type auxAzureSEVSNP struct {
// description: |
// Expected TPM measurements.
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
// description: |
// Lowest acceptable bootloader version.
BootloaderVersion uint8 `yaml:"-"`
// description: |
// Lowest acceptable TEE version.
TEEVersion uint8 `json:"teeVersion" yaml:"-"`
// description: |
// Lowest acceptable SEV-SNP version.
SNPVersion uint8 `json:"snpVersion" yaml:"-"`
// description: |
// Lowest acceptable microcode version.
MicrocodeVersion uint8 `json:"microcodeVersion" yaml:"-"`
// description: |
// Configuration for validating the firmware signature.
FirmwareSignerConfig SNPFirmwareSignerConfig `json:"firmwareSignerConfig" yaml:"firmwareSignerConfig"`
// description: |
// AMD Root Key certificate used to verify the SEV-SNP certificate chain.
AMDRootKey Certificate `json:"amdRootKey" yaml:"amdRootKey"`
}
// fusedAzureSEVSNP is a helper struct for unmarshaling the config from YAML for handling the version parsing.
type fusedAzureSEVSNP struct {
*auxAzureSEVSNP `yaml:",inline"`
// description: |
// Lowest acceptable bootloader version.
BootloaderVersion string `yaml:"bootloaderVersion"`
// description: |
// Lowest acceptable bootloader version.
TEEVersion string `yaml:"teeVersion"`
// description: |
// Lowest acceptable bootloader version.
SNPVersion string `yaml:"snpVersion"`
// description: |
// Lowest acceptable bootloader version.
MicrocodeVersion string `yaml:"microcodeVersion"`
}
func convertStringToUint(c *AzureSEVSNP, versionType snpversion.Type, aux *fusedAzureSEVSNP) error {
v, stringV := getUintAndStringPtrToVersion(c, versionType, aux)
bvInt, err := strconv.ParseInt(*stringV, 10, 8)
if err != nil {
return err
}
*v = uint8(bvInt)
return nil
}
func convertLatestToNumber(c *AzureSEVSNP, versionType snpversion.Type, aux *fusedAzureSEVSNP) bool {
v, stringV := getUintAndStringPtrToVersion(c, versionType, aux)
if strings.ToLower(*stringV) == "latest" {
*v = snpversion.GetLatest(versionType)
return true
}
return false
}
func getUintAndStringPtrToVersion(c *AzureSEVSNP, versionType snpversion.Type, aux *fusedAzureSEVSNP) (versionUint *uint8, versionString *string) {
switch versionType {
case snpversion.Bootloader:
versionUint = &c.BootloaderVersion
versionString = &aux.BootloaderVersion
case snpversion.TEE:
versionUint = &c.TEEVersion
versionString = &aux.TEEVersion
case snpversion.SNP:
versionUint = &c.SNPVersion
versionString = &aux.SNPVersion
case snpversion.Microcode:
versionUint = &c.MicrocodeVersion
versionString = &aux.MicrocodeVersion
}
return
}

View File

@ -31,6 +31,7 @@ import (
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
"github.com/edgelesssys/constellation/v2/internal/api/fetcher"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
@ -381,14 +382,25 @@ func fromFile(fileHandler file.Handler, name string) (*Config, error) {
// New creates a new config by:
// 1. Reading config file via provided fileHandler from file with name.
// 2. Read secrets from environment variables.
// 3. Validate config. If `--force` is set the version validation will be disabled and any version combination is allowed.
// 2. For "latest" version values of the attestation variants fetch the version numbers.
// 3. Read secrets from environment variables.
// 4. Validate config. If `--force` is set the version validation will be disabled and any version combination is allowed.
func New(fileHandler file.Handler, name string, force bool) (*Config, error) {
return NewWithClient(fileHandler, name, fetcher.NewHTTPClient(), force)
}
// NewWithClient is New with a custom HTTP client.
func NewWithClient(fileHandler file.Handler, name string, client fetcher.HTTPClient, force bool) (*Config, error) {
// Read config file
c, err := fromFile(fileHandler, name)
if err != nil {
return nil, err
}
if azure := c.Attestation.AzureSEVSNP; azure != nil {
if err := azure.FetchAndSetLatestVersionNumbers(client); err != nil {
return c, err
}
}
// Read secrets from env-vars.
clientSecretValue := os.Getenv(constants.EnvVarAzureClientSecretValue)

View File

@ -7,7 +7,11 @@ SPDX-License-Identifier: AGPL-3.0-only
package config
import (
"bytes"
"encoding/json"
"errors"
"io"
"net/http"
"reflect"
"testing"
@ -20,6 +24,7 @@ import (
"go.uber.org/goleak"
"gopkg.in/yaml.v3"
"github.com/edgelesssys/constellation/v2/internal/api/configapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config/instancetypes"
@ -37,37 +42,57 @@ func TestDefaultConfig(t *testing.T) {
assert.NotNil(def)
}
func TestSettingLatestAsVersion(t *testing.T) {
func TestDefaultConfigWritesLatestVersion(t *testing.T) {
conf := Default()
bt, err := yaml.Marshal(conf)
require := require.New(t)
require.NoError(err)
var mp configMap
require.NoError(yaml.Unmarshal(bt, &mp))
assert := assert.New(t)
assert.Equal("latest", mp.getAzureSEVSNPVersion("microcodeVersion"))
assert.Equal("latest", mp.getAzureSEVSNPVersion("teeVersion"))
assert.Equal("latest", mp.getAzureSEVSNPVersion("snpVersion"))
assert.Equal("latest", mp.getAzureSEVSNPVersion("bootloaderVersion"))
}
func TestReadConfigFile(t *testing.T) {
testCases := map[string]struct {
config map[string]interface{}
config configMap
configName string
wantResult *Config
wantErr bool
}{
"mix of latest and uint as version value": {
config: func() map[string]interface{} {
"mix of Latest and uint as version value": {
config: func() configMap {
conf := Default()
// modify versions as string
m := getConfigAsMap(conf, t)
m["attestation"].(map[string]interface{})["azureSEVSNP"].(map[string]interface{})["microcodeVersion"] = "latest"
m["attestation"].(map[string]interface{})["azureSEVSNP"].(map[string]interface{})["teeVersion"] = "latest"
m["attestation"].(map[string]interface{})["azureSEVSNP"].(map[string]interface{})["snpVersion"] = "latest"
m["attestation"].(map[string]interface{})["azureSEVSNP"].(map[string]interface{})["bootloaderVersion"] = 1
m.setAzureSEVSNPVersion("microcodeVersion", "Latest") // check uppercase also works
m.setAzureSEVSNPVersion("teeVersion", 2)
m.setAzureSEVSNPVersion("bootloaderVersion", 1)
return m
}(),
configName: constants.ConfigFilename,
wantResult: func() *Config {
conf := Default()
conf.Attestation.AzureSEVSNP.BootloaderVersion = 1
conf.Attestation.AzureSEVSNP.BootloaderVersion = configapi.AttestationVersion{
Value: 1,
IsLatest: false,
}
conf.Attestation.AzureSEVSNP.TEEVersion = configapi.AttestationVersion{
Value: 2,
IsLatest: false,
}
return conf
}(),
},
"refuse invalid version value": {
config: func() map[string]interface{} {
config: func() configMap {
conf := Default()
m := getConfigAsMap(conf, t)
m["attestation"].(map[string]interface{})["azureSEVSNP"].(map[string]interface{})["microcodeVersion"] = "1a"
m.setAzureSEVSNPVersion("microcodeVersion", "1a")
return m
}(),
configName: constants.ConfigFilename,
@ -84,29 +109,16 @@ func TestSettingLatestAsVersion(t *testing.T) {
require.NoError(fileHandler.WriteYAML(tc.configName, tc.config, file.OptNone))
}
result, err := fromFile(fileHandler, tc.configName)
if tc.wantErr {
assert.Error(err)
} else {
require.NoError(err)
assert.NoError(err)
assert.Equal(tc.wantResult, result)
}
})
}
}
// getConfigAsMap returns a map of the config.
func getConfigAsMap(conf *Config, t *testing.T) (res map[string]interface{}) {
bytes, err := yaml.Marshal(&conf)
if err != nil {
t.Fatal(err)
}
if err := yaml.Unmarshal(bytes, &res); err != nil {
t.Fatal(err)
}
return
}
func TestFromFile(t *testing.T) {
testCases := map[string]struct {
config *Config
@ -233,7 +245,7 @@ func TestNewWithDefaultOptions(t *testing.T) {
wantClientSecretValue: "some-secret",
},
}
client := newTestClient(&fakeConfigAPIHandler{})
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
@ -248,8 +260,7 @@ func TestNewWithDefaultOptions(t *testing.T) {
}
// Test
c, err := New(fileHandler, constants.ConfigFilename, false)
c, err := NewWithClient(fileHandler, constants.ConfigFilename, client, false)
if tc.wantErr {
assert.Error(err)
return
@ -846,3 +857,68 @@ func TestConfigVersionCompatibility(t *testing.T) {
})
}
}
// configMap is used to un-/marshal the config as an unstructured map.
type configMap map[string]interface{}
func (c configMap) setAzureSEVSNPVersion(versionType string, value interface{}) {
c["attestation"].(configMap)["azureSEVSNP"].(configMap)[versionType] = value
}
func (c configMap) getAzureSEVSNPVersion(versionType string) interface{} {
return c["attestation"].(configMap)["azureSEVSNP"].(configMap)[versionType]
}
// getConfigAsMap returns a map of the config.
func getConfigAsMap(conf *Config, t *testing.T) (res configMap) {
bytes, err := yaml.Marshal(&conf)
if err != nil {
t.Fatal(err)
}
if err := yaml.Unmarshal(bytes, &res); err != nil {
t.Fatal(err)
}
return
}
type fakeConfigAPIHandler struct{}
// RoundTrip resolves the request and returns a dummy response.
func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, error) {
if req.URL.Path == "/constellation/v1/attestation/azure-sev-snp/list" {
res := &http.Response{}
data := []string{"2021-01-01-01-01.json"}
bt, err := json.Marshal(data)
if err != nil {
return nil, err
}
res.Body = io.NopCloser(bytes.NewReader(bt))
res.Header = http.Header{}
res.Header.Set("Content-Type", "application/json")
res.StatusCode = http.StatusOK
return res, nil
} else if req.URL.Path == "/constellation/v1/attestation/azure-sev-snp/2021-01-01-01-01.json" {
res := &http.Response{}
bt, err := json.Marshal(configapi.AzureSEVSNPVersion{
Microcode: 93,
TEE: 0,
SNP: 6,
Bootloader: 2,
})
if err != nil {
return nil, err
}
res.Body = io.NopCloser(bytes.NewReader(bt))
res.StatusCode = http.StatusOK
return res, nil
}
return nil, errors.New("no endpoint found")
}
// newTestClient returns *http.Client with Transport replaced to avoid making real calls.
func newTestClient(fn *fakeConfigAPIHandler) *http.Client {
return &http.Client{
Transport: fn,
}
}

View File

@ -1,8 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "snpversion",
srcs = ["snpversion.go"],
importpath = "github.com/edgelesssys/constellation/v2/internal/config/snpversion",
visibility = ["//:__subpackages__"],
)

View File

@ -1,33 +0,0 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package snpversion
const (
Bootloader Type = "bootloader" // Bootloader is the version of the Azure SEVSNP bootloader.
TEE Type = "tee" // TEE is the version of the Azure SEVSNP TEE.
SNP Type = "snp" // SNP is the version of the Azure SEVSNP SNP.
Microcode Type = "microcode" // Microcode is the version of the Azure SEVSNP microcode.
)
// Type is the type of the version to be requested.
type Type string
// GetLatest returns the version of the given type.
func GetLatest(t Type) uint8 {
switch t {
case Bootloader:
return 2
case TEE:
return 0
case SNP:
return 6
case Microcode:
return 93
default:
panic("invalid version type")
}
}

View File

@ -19,6 +19,7 @@ import (
"github.com/go-playground/validator/v10"
"golang.org/x/mod/semver"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility"
@ -26,7 +27,6 @@ import (
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versions"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
// ValidationError occurs when the validation of a config fails.

View File

@ -10,10 +10,10 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/imagefetcher",
visibility = ["//cli:__subpackages__"],
deps = [
"//internal/api/fetcher",
"//internal/api/versionsapi",
"//internal/cloud/cloudprovider",
"//internal/variant",
"//internal/versionsapi",
"//internal/versionsapi/fetcher",
"@com_github_schollz_progressbar_v3//:progressbar",
"@com_github_spf13_afero//:afero",
],
@ -27,10 +27,10 @@ go_test(
],
embed = [":imagefetcher"],
deps = [
"//internal/api/versionsapi",
"//internal/cloud/cloudprovider",
"//internal/file",
"//internal/variant",
"//internal/versionsapi",
"@com_github_spf13_afero//:afero",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",

View File

@ -19,10 +19,10 @@ import (
"io/fs"
"regexp"
"github.com/edgelesssys/constellation/v2/internal/api/fetcher"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/versionsapi/fetcher"
"github.com/spf13/afero"
)
@ -35,7 +35,7 @@ type Fetcher struct {
// New returns a new image fetcher.
func New() *Fetcher {
return &Fetcher{
fetcher: fetcher.NewFetcher(),
fetcher: fetcher.NewVersionAPIFetcher(),
fs: &afero.Afero{Fs: afero.NewOsFs()},
}
}

View File

@ -13,10 +13,10 @@ import (
"net/http"
"testing"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -27,6 +27,7 @@ type awsS3ClientAPI interface {
GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error)
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
CreateBucket(ctx context.Context, params *s3.CreateBucketInput, optFns ...func(*s3.Options)) (*s3.CreateBucketOutput, error)
DeleteObject(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectOutput, error)
}
// Storage is an implementation of the Storage interface, storing keys in AWS S3 buckets.
@ -76,6 +77,18 @@ func (s *Storage) Get(ctx context.Context, keyID string) ([]byte, error) {
return io.ReadAll(output.Body)
}
// Delete removes a DEK from AWS S3 Storage by key ID.
func (s *Storage) Delete(ctx context.Context, keyID string) error {
deleteObjectInput := &s3.DeleteObjectInput{
Bucket: &s.bucketID,
Key: &keyID,
}
if _, err := s.client.DeleteObject(ctx, deleteObjectInput); err != nil {
return fmt.Errorf("deleting DEK from storage: %w", err)
}
return nil
}
// Put saves a DEK to AWS S3 Storage by key ID.
func (s *Storage) Put(ctx context.Context, keyID string, data []byte) error {
putObjectInput := &s3.PutObjectInput{

View File

@ -43,6 +43,10 @@ func (s *stubAWSS3StorageClient) PutObject(_ context.Context, params *s3.PutObje
return &s3.PutObjectOutput{}, s.putObjectErr
}
func (s *stubAWSS3StorageClient) DeleteObject(_ context.Context, _ *s3.DeleteObjectInput, _ ...func(*s3.Options)) (*s3.DeleteObjectOutput, error) {
return &s3.DeleteObjectOutput{}, nil
}
func (s *stubAWSS3StorageClient) CreateBucket(_ context.Context, _ *s3.CreateBucketInput, _ ...func(*s3.Options)) (*s3.CreateBucketOutput, error) {
s.createBucketCalled = true
return &s3.CreateBucketOutput{}, s.createBucketErr

View File

@ -6,8 +6,8 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//internal/cloud/cloudprovider",
"//internal/osimage/secureboot",
"//internal/versionsapi",
],
)

View File

@ -6,9 +6,9 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/archive",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//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",

View File

@ -16,9 +16,9 @@ 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/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
// Archivist uploads OS images to S3.

View File

@ -6,10 +6,10 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/aws",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//internal/logger",
"//internal/osimage",
"//internal/osimage/secureboot",
"//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_ec2//:ec2",

View File

@ -23,10 +23,10 @@ import (
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/aws/smithy-go"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/osimage"
"github.com/edgelesssys/constellation/v2/internal/osimage/secureboot"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
// Uploader can upload and remove os images on GCP.

View File

@ -9,9 +9,9 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/azure",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//internal/logger",
"//internal/osimage",
"//internal/versionsapi",
"@com_github_azure_azure_sdk_for_go_sdk_azcore//runtime",
"@com_github_azure_azure_sdk_for_go_sdk_azidentity//:azidentity",
"@com_github_azure_azure_sdk_for_go_sdk_resourcemanager_compute_armcompute_v4//:armcompute",

View File

@ -21,9 +21,9 @@ import (
armcomputev4 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/pageblob"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/osimage"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
// Uploader can upload and remove os images on Azure.

View File

@ -6,10 +6,10 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/gcp",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//internal/logger",
"//internal/osimage",
"//internal/osimage/secureboot",
"//internal/versionsapi",
"@com_github_googleapis_gax_go_v2//:gax-go",
"@com_google_cloud_go_compute//apiv1",
"@com_google_cloud_go_compute//apiv1/computepb",

View File

@ -19,10 +19,10 @@ import (
compute "cloud.google.com/go/compute/apiv1"
"cloud.google.com/go/compute/apiv1/computepb"
"cloud.google.com/go/storage"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/osimage"
"github.com/edgelesssys/constellation/v2/internal/osimage/secureboot"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
gaxv2 "github.com/googleapis/gax-go/v2"
)

View File

@ -6,9 +6,9 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/imageinfo",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//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",

View File

@ -17,9 +17,9 @@ 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/api/versionsapi"
"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.

View File

@ -6,10 +6,10 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/measurementsuploader",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//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",

View File

@ -18,10 +18,10 @@ 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/api/versionsapi"
"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.

View File

@ -6,8 +6,8 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/osimage/nop",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/versionsapi",
"//internal/logger",
"//internal/osimage",
"//internal/versionsapi",
],
)

View File

@ -10,9 +10,9 @@ package nop
import (
"context"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/osimage"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
// Uploader is a no-op uploader.

View File

@ -11,9 +11,9 @@ import (
"io"
"time"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/osimage/secureboot"
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
)
// UploadRequest is a request to upload an os image.

View File

@ -82,7 +82,7 @@ func GetAvailableAttestationTypes() []Variant {
for _, k := range keys {
res = append(res, providerAttestationMapping[k]...)
}
return removeDuplicate(res)
return RemoveDuplicate(res)
}
// Getter returns an ASN.1 Object Identifier.
@ -259,9 +259,10 @@ func (QEMUTDX) Equal(other Getter) bool {
return other.OID().Equal(QEMUTDX{}.OID())
}
func removeDuplicate(sliceList []Variant) []Variant {
allKeys := make(map[Variant]bool)
list := []Variant{}
// RemoveDuplicate removes duplicate elements from a slice.
func RemoveDuplicate[T comparable](sliceList []T) []T {
allKeys := make(map[T]bool)
list := []T{}
for _, item := range sliceList {
if _, value := allKeys[item]; !value {
allKeys[item] = true