config: add separate option for handling attestation parameters (#1623)

* Add attestation options to config

* Add join-config migration path for clusters with old measurement format

* Always create MAA provider for Azure SNP clusters

* Remove confidential VM option from provider in favor of attestation options

* cli: add config migrate command to handle config migration (#1678)

---------

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-05-03 11:11:53 +02:00 committed by GitHub
parent 6027b066e5
commit d7a2ddd939
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
74 changed files with 1339 additions and 1282 deletions

View file

@ -14,8 +14,7 @@ import (
"strings"
"time"
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
@ -147,9 +146,8 @@ func (c *Client) Upgrade(ctx context.Context, config *config.Config, timeout tim
return fmt.Errorf("creating CR backup: %w", err)
}
fileHandler := file.NewHandler(afero.NewOsFs())
for _, chart := range upgradeReleases {
err = c.upgradeRelease(ctx, timeout, config, chart, allowDestructive, fileHandler)
err = c.upgradeRelease(ctx, timeout, config, chart, allowDestructive)
if err != nil {
return fmt.Errorf("upgrading %s: %w", chart.Metadata.Name, err)
}
@ -245,7 +243,7 @@ func (s ServiceVersions) ConstellationServices() string {
}
func (c *Client) upgradeRelease(
ctx context.Context, timeout time.Duration, conf *config.Config, chart *chart.Chart, allowDestructive bool, fileHandler file.Handler,
ctx context.Context, timeout time.Duration, conf *config.Config, chart *chart.Chart, allowDestructive bool,
) error {
// We need to load all values that can be statically loaded before merging them with the cluster
// values. Otherwise the templates are not rendered correctly.
@ -289,7 +287,7 @@ func (c *Client) upgradeRelease(
return fmt.Errorf("loading values: %w", err)
}
if err := c.applyMigrations(releaseName, values, conf, fileHandler); err != nil {
if err := c.applyMigrations(ctx, releaseName, values, conf); err != nil {
return fmt.Errorf("applying migrations: %w", err)
}
default:
@ -312,7 +310,7 @@ func (c *Client) upgradeRelease(
// applyMigrations checks the from version and applies the necessary migrations.
// The function assumes the caller has verified that our version drift restriction is not violated,
// Currently, this is done during config validation.
func (c *Client) applyMigrations(releaseName string, values map[string]any, conf *config.Config, fileHandler file.Handler) error {
func (c *Client) applyMigrations(ctx context.Context, releaseName string, values map[string]any, conf *config.Config) error {
current, err := c.currentVersion(releaseName)
if err != nil {
return fmt.Errorf("getting %s version: %w", releaseName, err)
@ -322,37 +320,50 @@ func (c *Client) applyMigrations(releaseName string, values map[string]any, conf
return fmt.Errorf("parsing current version: %w", err)
}
if currentV.Major == 2 && currentV.Minor == 6 {
return migrateFrom2_6(values, conf, fileHandler)
if currentV.Major == 2 && currentV.Minor == 7 {
return migrateFrom2_7(ctx, values, conf, c.kubectl)
}
return nil
}
// migrateFrom2_6 applies the necessary migrations for upgrading from v2.6.x to v2.7.x.
// migrateFrom2_6 should be applied for v2.6.x --> v2.7.x.
// migrateFrom2_6 should NOT be applied for v2.7.0 --> v2.7.x.
// This function can be removed once we are sure that we will no longer provide backports for v2.6.
func migrateFrom2_6(values map[string]any, conf *config.Config, fileHandler file.Handler) error {
// Manually setting attestationVariant is required here since upgrade normally isn't allowed to change this value.
// However, to introduce the value into a 2.6 cluster for the first time we have to set it nevertheless.
if err := setAttestationVariant(values, conf.AttestationVariant); err != nil {
return fmt.Errorf("setting attestationVariant: %w", err)
// migrateFrom2_7 applies the necessary migrations for upgrading from v2.7.x to v2.8.x.
// migrateFrom2_7 should be applied for v2.7.x --> v2.8.x.
// migrateFrom2_7 should NOT be applied for v2.8.0 --> v2.8.x.
// Remove after release of v2.8.0.
func migrateFrom2_7(ctx context.Context, values map[string]any, conf *config.Config, kubeclient crdClient) error {
if conf.GetProvider() == cloudprovider.GCP {
if err := updateGCPStorageClass(ctx, kubeclient); err != nil {
return fmt.Errorf("applying migration for GCP storage class: %w", err)
}
}
// Manually setting idKeyConfig is required here since upgrade normally isn't allowed to change this value.
// However, to introduce the value into a 2.6 cluster for the first time we have to set it nevertheless.
var idFile clusterid.File
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
return fmt.Errorf("reading cluster ID file: %w", err)
return updateJoinConfig(values, conf)
}
func updateGCPStorageClass(ctx context.Context, kubeclient crdClient) error {
// v2.8 updates the disk type of GCP default storage class
// This value is not updatable in Kubernetes, but we can use a workaround to update it:
// First, we delete the storage class, then we upgrade the chart,
// which will recreate the storage class with the new disk type.
if err := kubeclient.DeleteStorageClass(ctx, "encrypted-rwo"); err != nil {
return fmt.Errorf("deleting storage class for update: %w", err)
}
// Disallow users to set MAAFallback as ID key digest policy for upgrades, since it requires extra cloud resources.
if conf.IDKeyDigestPolicy() == idkeydigest.MAAFallback {
return fmt.Errorf("ID key digest policy %s is not supported for upgrades", conf.IDKeyDigestPolicy())
return nil
}
func updateJoinConfig(values map[string]any, conf *config.Config) error {
joinServiceVals, ok := values["join-service"].(map[string]interface{})
if !ok {
return errors.New("invalid join-service config")
}
if err := setIdkeyConfig(values, conf, idFile.AttestationURL); err != nil {
return fmt.Errorf("setting id key config: %w", err)
attestationConfigJSON, err := json.Marshal(conf.GetAttestationConfig())
if err != nil {
return fmt.Errorf("marshalling attestation config: %w", err)
}
joinServiceVals["attestationConfig"] = string(attestationConfigJSON)
return nil
}
@ -404,44 +415,6 @@ func (c *Client) updateCRDs(ctx context.Context, chart *chart.Chart) error {
return nil
}
// setAttestationVariant sets the attesationVariant value on verification-service and join-service value maps.
func setAttestationVariant(values map[string]any, variant string) error {
joinServiceVals, ok := values["join-service"].(map[string]any)
if !ok {
return errors.New("invalid join-service values")
}
joinServiceVals["attestationVariant"] = variant
verifyServiceVals, ok := values["verification-service"].(map[string]any)
if !ok {
return errors.New("invalid verification-service values")
}
verifyServiceVals["attestationVariant"] = variant
return nil
}
// setIdkeyConfig sets the idkeyconfig value on the join-service value maps.
func setIdkeyConfig(values map[string]any, cfg *config.Config, maaURL string) error {
joinServiceVals, ok := values["join-service"].(map[string]any)
if !ok {
return errors.New("invalid join-service values")
}
idKeyCfg := config.SNPFirmwareSignerConfig{
AcceptedKeyDigests: cfg.IDKeyDigests(),
EnforcementPolicy: cfg.IDKeyDigestPolicy(),
MAAURL: maaURL,
}
marshalledCfg, err := json.Marshal(idKeyCfg)
if err != nil {
return fmt.Errorf("marshalling id key digest config: %w", err)
}
joinServiceVals["idKeyConfig"] = string(marshalledCfg)
return nil
}
type debugLog interface {
Debugf(format string, args ...any)
Sync()
@ -452,6 +425,7 @@ type crdClient interface {
ApplyCRD(ctx context.Context, rawCRD []byte) error
GetCRDs(ctx context.Context) ([]apiextensionsv1.CustomResourceDefinition, error)
GetCRs(ctx context.Context, gvr schema.GroupVersionResource) ([]unstructured.Unstructured, error)
DeleteStorageClass(ctx context.Context, name string) error // TODO: remove with v2.9
}
type actionWrapper interface {