mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-11-12 00:26:35 -05:00
attestation: add option for MAA fallback to verify azure's snp-sev id key digest (#1257)
* Convert enforceIDKeyDigest setting to enum * Use MAA fallback in Azure SNP attestation * Only create MAA provider if MAA fallback is enabled --------- Signed-off-by: Daniel Weiße <dw@edgeless.systems> Co-authored-by: Thomas Tendyck <tt@edgeless.systems>
This commit is contained in:
parent
9a9688583d
commit
5a0234b3f2
66 changed files with 1073 additions and 542 deletions
|
|
@ -3,7 +3,10 @@ load("//bazel/go:go_test.bzl", "go_test")
|
|||
|
||||
go_library(
|
||||
name = "idkeydigest",
|
||||
srcs = ["idkeydigest.go"],
|
||||
srcs = [
|
||||
"enforceidkeydigest_string.go",
|
||||
"idkeydigest.go",
|
||||
],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = ["//internal/cloud/cloudprovider"],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
// Code generated by "stringer -type=EnforceIDKeyDigest"; DO NOT EDIT.
|
||||
|
||||
package idkeydigest
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[Unknown-0]
|
||||
_ = x[StrictChecking-1]
|
||||
_ = x[MAAFallback-2]
|
||||
_ = x[WarnOnly-3]
|
||||
}
|
||||
|
||||
const _EnforceIDKeyDigest_name = "UnknownStrictCheckingMAAFallbackWarnOnly"
|
||||
|
||||
var _EnforceIDKeyDigest_index = [...]uint8{0, 7, 21, 32, 40}
|
||||
|
||||
func (i EnforceIDKeyDigest) String() string {
|
||||
if i >= EnforceIDKeyDigest(len(_EnforceIDKeyDigest_index)-1) {
|
||||
return "EnforceIDKeyDigest(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _EnforceIDKeyDigest_name[_EnforceIDKeyDigest_index[i]:_EnforceIDKeyDigest_index[i+1]]
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ Copyright (c) Edgeless Systems GmbH
|
|||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// Package idkeydigest contains policies and type definitions
|
||||
// for checking the ID Key Digest value in SEV-SNP attestation.
|
||||
package idkeydigest
|
||||
|
||||
import (
|
||||
|
|
@ -11,10 +13,105 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
)
|
||||
|
||||
// Config contains the configuration for ID Key Digest validation.
|
||||
type Config struct {
|
||||
IDKeyDigests IDKeyDigests `json:"idKeyDigests"`
|
||||
EnforcementPolicy EnforceIDKeyDigest `json:"enforcementPolicy"`
|
||||
MAAURL string `json:"maaURL,omitempty"`
|
||||
}
|
||||
|
||||
//go:generate stringer -type=EnforceIDKeyDigest
|
||||
|
||||
// EnforceIDKeyDigest defines the behavior of the validator when the ID key digest is not found in the expected list.
|
||||
type EnforceIDKeyDigest uint32
|
||||
|
||||
// TODO: Decide on final value naming.
|
||||
const (
|
||||
// Unknown is reserved for invalid configurations.
|
||||
Unknown EnforceIDKeyDigest = iota
|
||||
// StrictChecking will return an error if the ID key digest is not found in the expected list.
|
||||
StrictChecking
|
||||
// MAAFallback attempts to verify the attestation using Microsoft Azure Attestation (MAA),
|
||||
// if the ID key digest is not found in the expected list.
|
||||
MAAFallback
|
||||
// WarnOnly logs a warning if the ID key digest is not found in the expected list.
|
||||
// No error is returned.
|
||||
WarnOnly
|
||||
)
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (e *EnforceIDKeyDigest) UnmarshalJSON(b []byte) error {
|
||||
return e.unmarshal(func(val any) error {
|
||||
return json.Unmarshal(b, val)
|
||||
})
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (e EnforceIDKeyDigest) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(e.String())
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (e *EnforceIDKeyDigest) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
return e.unmarshal(unmarshal)
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
func (e EnforceIDKeyDigest) MarshalYAML() (any, error) {
|
||||
return e.String(), nil
|
||||
}
|
||||
|
||||
func (e *EnforceIDKeyDigest) unmarshal(unmarshalFunc func(any) error) error {
|
||||
// Check for legacy format: EnforceIDKeyDigest might be a boolean.
|
||||
// If set to true, the value will be set to StrictChecking.
|
||||
// If set to false, the value will be set to WarnOnly.
|
||||
var legacyEnforce bool
|
||||
legacyErr := unmarshalFunc(&legacyEnforce)
|
||||
if legacyErr == nil {
|
||||
if legacyEnforce {
|
||||
*e = StrictChecking
|
||||
} else {
|
||||
*e = WarnOnly
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var enforce string
|
||||
if err := unmarshalFunc(&enforce); err != nil {
|
||||
return errors.Join(
|
||||
err,
|
||||
fmt.Errorf("trying legacy format: %w", legacyErr),
|
||||
)
|
||||
}
|
||||
|
||||
*e = EnforcePolicyFromString(enforce)
|
||||
if *e == Unknown {
|
||||
return fmt.Errorf("unknown EnforceIDKeyDigest value: %q", enforce)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnforcePolicyFromString returns EnforceIDKeyDigest from string.
|
||||
func EnforcePolicyFromString(s string) EnforceIDKeyDigest {
|
||||
s = strings.ToLower(s)
|
||||
switch s {
|
||||
case "strictchecking":
|
||||
return StrictChecking
|
||||
case "maafallback":
|
||||
return MAAFallback
|
||||
case "warnonly":
|
||||
return WarnOnly
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
// IDKeyDigests is a list of trusted digest values for the ID key.
|
||||
type IDKeyDigests [][]byte
|
||||
|
||||
|
|
|
|||
|
|
@ -117,3 +117,126 @@ func TestUnmarshal(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnforceIDKeyDigestMarshal(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input EnforceIDKeyDigest
|
||||
wantJSON string
|
||||
wantYAML string
|
||||
}{
|
||||
"strict": {
|
||||
input: StrictChecking,
|
||||
wantJSON: `"StrictChecking"`,
|
||||
wantYAML: "StrictChecking",
|
||||
},
|
||||
"maaFallback": {
|
||||
input: MAAFallback,
|
||||
wantJSON: `"MAAFallback"`,
|
||||
wantYAML: "MAAFallback",
|
||||
},
|
||||
"warnOnly": {
|
||||
input: WarnOnly,
|
||||
wantJSON: `"WarnOnly"`,
|
||||
wantYAML: "WarnOnly",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
{
|
||||
// YAML
|
||||
yaml, err := yaml.Marshal(tc.input)
|
||||
require.NoError(err)
|
||||
assert.YAMLEq(tc.wantYAML, string(yaml))
|
||||
}
|
||||
|
||||
{
|
||||
// JSON
|
||||
json, err := json.Marshal(tc.input)
|
||||
require.NoError(err)
|
||||
assert.JSONEq(tc.wantJSON, string(json))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnforceIDKeyDigestUnmarshal(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
inputJSON string
|
||||
inputYAML string
|
||||
want EnforceIDKeyDigest
|
||||
wantErr bool
|
||||
}{
|
||||
"strict": {
|
||||
inputJSON: `"StrictChecking"`,
|
||||
inputYAML: "StrictChecking",
|
||||
want: StrictChecking,
|
||||
},
|
||||
"maaFallback": {
|
||||
inputJSON: `"MAAFallback"`,
|
||||
inputYAML: "MAAFallback",
|
||||
want: MAAFallback,
|
||||
},
|
||||
"warnOnly": {
|
||||
inputJSON: `"WarnOnly"`,
|
||||
inputYAML: "WarnOnly",
|
||||
want: WarnOnly,
|
||||
},
|
||||
"legacyTrue": {
|
||||
inputJSON: `true`,
|
||||
inputYAML: "true",
|
||||
want: StrictChecking,
|
||||
},
|
||||
"legacyFalse": {
|
||||
inputJSON: `false`,
|
||||
inputYAML: "false",
|
||||
want: WarnOnly,
|
||||
},
|
||||
"invalid": {
|
||||
inputJSON: `"invalid"`,
|
||||
inputYAML: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"invalidType": {
|
||||
inputJSON: `{"object": "invalid"}`,
|
||||
inputYAML: "object: invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
{
|
||||
// YAML
|
||||
var got EnforceIDKeyDigest
|
||||
err := yaml.Unmarshal([]byte(tc.inputYAML), &got)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.want, got)
|
||||
}
|
||||
|
||||
{
|
||||
// JSON
|
||||
var got EnforceIDKeyDigest
|
||||
err := json.Unmarshal([]byte(tc.inputJSON), &got)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue