Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow specifying accepted avisories for SWHardeningNeeded TCB status #733

Merged
merged 7 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/vale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:

jobs:
vale:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
Expand Down
5 changes: 5 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func VerifyCoordinator(ctx context.Context, endpoint string, opts VerifyOptions)
Debug: opts.Debug,
Nonce: opts.Nonce,
AcceptedTCBStatuses: opts.AcceptedTCBStatuses,
AcceptedAdvisories: opts.AcceptedAdvisories,
}); err != nil {
return nil, nil, nil, fmt.Errorf("verifying Coordinator quote: %w", err)
}
Expand Down Expand Up @@ -467,6 +468,10 @@ type VerifyOptions struct {
// If not set, defaults to ["UpToDate", "SWHardeningNeeded"].
// If the Coordinator returns a TCB status not listed, an [attestation.TCBStatusError] is returned.
AcceptedTCBStatuses []string `json:"AcceptedTCBStatuses"`
// AcceptedAdvisories is a list of Intel Security Advisories that are acceptable.
// If the Coordinator returns TCB status "SWHardeningNeeded", the list of advisories for that report must be a subset of this list.
// If not set, all advisories are accepted.
AcceptedAdvisories []string `json:"AcceptedAdvisories"`

// Nonce is an optional, user-defined nonce to be included in the Coordinator's attestation statement.
// If set, the Coordinator will generate an SGX quote over sha256(Coordinator_root_cert, Nonce).
Expand Down
25 changes: 23 additions & 2 deletions api/attestation/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,27 @@ import (
type TCBStatusError struct {
// TCBStatus is the TCB status of the Coordinator enclave.
TCBStatus tcbstatus.Status
// Advisories is a list of Intel Security Advisories if the TCB status is SWHardeningNeeded.
Advisories []string
}

// NewTCBStatusError creates a new TCBStatusError.
func NewTCBStatusError(tcbStatus tcbstatus.Status) error {
return &TCBStatusError{TCBStatus: tcbStatus}
}

// NewTCBStatusErrorWithAdvisories creates a new TCBStatusError with a list of Intel Security Advisories.
func NewTCBStatusErrorWithAdvisories(tcbStatus tcbstatus.Status, advisories []string) error {
return &TCBStatusError{TCBStatus: tcbStatus, Advisories: advisories}
}

// Error returns the error message.
func (e *TCBStatusError) Error() string {
return fmt.Sprintf("invalid TCB status: %s", e.TCBStatus)
var advisoryMsg string
if len(e.Advisories) > 0 {
advisoryMsg = fmt.Sprintf(": advisories not accepted by configuration: %s", e.Advisories)
}
return fmt.Sprintf("invalid TCB status: %s%s", e.TCBStatus, advisoryMsg)
}

// Config is the expected attestation metadata of a MarbleRun Coordinator enclave.
Expand All @@ -48,6 +59,7 @@ type Config struct {
Debug bool
Nonce []byte
AcceptedTCBStatuses []string
AcceptedAdvisories []string
}

// VerifyCertificate verifies the Coordinator's TLS certificate against the Coordinator's SGX quote.
Expand All @@ -73,6 +85,15 @@ func verifyCertificate(
if err != nil {
return NewTCBStatusError(report.TCBStatus)
}

notAccepted, err := tcb.CheckAdvisories(report, config.AcceptedAdvisories)
if err != nil {
return err
}
if len(notAccepted) > 0 {
return NewTCBStatusErrorWithAdvisories(report.TCBStatus, notAccepted)
}

switch validity {
case tcb.ValidityUnconditional:
case tcb.ValidityConditional:
Expand All @@ -83,7 +104,7 @@ func verifyCertificate(

if validity != tcb.ValidityUnconditional {
if report.TCBAdvisoriesErr != nil {
fmt.Fprintln(out, "Error: TCB Advisories:", err)
fmt.Fprintln(out, "Error: TCB Advisories:", report.TCBAdvisoriesErr)
} else {
fmt.Fprintln(out, "TCB Advisories:", report.TCBAdvisories)
}
Expand Down
72 changes: 72 additions & 0 deletions api/attestation/attestation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,78 @@ func TestVerifyCertificate(t *testing.T) {
}, attestation.ErrTCBLevelInvalid
},
},
"tcb error with advisories": {
config: defaultConfig,
verify: func([]byte) (attestation.Report, error) {
return attestation.Report{
Data: quoteData[:],
SecurityVersion: 2,
ProductID: []byte{0x03, 0x00},
SignerID: []byte{0xAB, 0xCD},
TCBStatus: tcbstatus.SWHardeningNeeded,
TCBAdvisories: []string{"INTEL-SA-0001"},
}, attestation.ErrTCBLevelInvalid
},
wantErr: true,
wantTCBErr: true,
},
"tcb error with advisories rejected": {
config: func() Config {
config := defaultConfig
config.AcceptedTCBStatuses = []string{"SWHardeningNeeded"}
config.AcceptedAdvisories = []string{"INTEL-SA-0002"}
daniel-weisse marked this conversation as resolved.
Show resolved Hide resolved
return config
}(),
verify: func([]byte) (attestation.Report, error) {
return attestation.Report{
Data: quoteData[:],
SecurityVersion: 2,
ProductID: []byte{0x03, 0x00},
SignerID: []byte{0xAB, 0xCD},
TCBStatus: tcbstatus.SWHardeningNeeded,
TCBAdvisories: []string{"INTEL-SA-0001"},
}, attestation.ErrTCBLevelInvalid
},
wantErr: true,
wantTCBErr: true,
},
"tcb error with advisories accepted": {
config: func() Config {
config := defaultConfig
config.AcceptedTCBStatuses = []string{"SWHardeningNeeded"}
config.AcceptedAdvisories = []string{"INTEL-SA-0001"}
return config
}(),
verify: func([]byte) (attestation.Report, error) {
return attestation.Report{
Data: quoteData[:],
SecurityVersion: 2,
ProductID: []byte{0x03, 0x00},
SignerID: []byte{0xAB, 0xCD},
TCBStatus: tcbstatus.SWHardeningNeeded,
TCBAdvisories: []string{"INTEL-SA-0001"},
}, attestation.ErrTCBLevelInvalid
},
},
"report contains tcb advisory parsing error": {
config: func() Config {
config := defaultConfig
config.AcceptedTCBStatuses = []string{"SWHardeningNeeded"}
config.AcceptedAdvisories = []string{"INTEL-SA-0001"}
return config
}(),
verify: func([]byte) (attestation.Report, error) {
return attestation.Report{
Data: quoteData[:],
SecurityVersion: 2,
ProductID: []byte{0x03, 0x00},
SignerID: []byte{0xAB, 0xCD},
TCBStatus: tcbstatus.SWHardeningNeeded,
TCBAdvisoriesErr: assert.AnError,
}, attestation.ErrTCBLevelInvalid
},
wantErr: true,
},
}

for name, tc := range testCases {
Expand Down
1 change: 1 addition & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ To install and configure MarbleRun, run:
rootCmd.PersistentFlags().String("era-config", "", "Path to a remote-attestation config file in JSON format. If none is provided, the command attempts to use './coordinator-era.json'. If that does not exist, the command will attempt to load a matching config file from the MarbleRun GitHub repository")
rootCmd.PersistentFlags().BoolP("insecure", "i", false, "Set to skip quote verification, needed when running in simulation mode")
rootCmd.PersistentFlags().StringSlice("accepted-tcb-statuses", []string{"UpToDate", "SWHardeningNeeded"}, "Comma-separated list of user accepted TCB statuses")
rootCmd.PersistentFlags().StringSlice("accepted-advisories", nil, "Comma-separated list of user accepted Intel Security Advisories for SWHardeningNeeded TCB status. If empty, all advisories are accepted")
rootCmd.PersistentFlags().StringP("namespace", "n", helm.Namespace, "Kubernetes namespace of the MarbleRun installation")
rootCmd.PersistentFlags().String("nonce", "", "(Optional) nonce to use for quote verification. If set, the Coordinator will generate a quote over sha256(CoordinatorCert + nonce)")
rootCmd.PersistentFlags().String("save-sgx-quote", "", "If set, save the Coordinator's SGX quote to the specified file")
Expand Down
5 changes: 5 additions & 0 deletions cli/internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func parseRestFlags(cmd *cobra.Command) (api.VerifyOptions, string, error) {
if err != nil {
return api.VerifyOptions{}, "", err
}
acceptedAdvisories, err := cmd.Flags().GetStringSlice("accepted-advisories")
if err != nil {
return api.VerifyOptions{}, "", err
}
k8sNamespace, err := cmd.Flags().GetString("namespace")
if err != nil {
return api.VerifyOptions{}, "", err
Expand Down Expand Up @@ -83,6 +87,7 @@ func parseRestFlags(cmd *cobra.Command) (api.VerifyOptions, string, error) {
}
}
verifyOptions.AcceptedTCBStatuses = acceptedTCBStatuses
verifyOptions.AcceptedAdvisories = acceptedAdvisories
verifyOptions.Nonce = []byte(nonce)

return verifyOptions, sgxQuotePath, nil
Expand Down
16 changes: 3 additions & 13 deletions coordinator/clientapi/clientapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,19 +556,9 @@ func (a *ClientAPI) UpdateManifest(ctx context.Context, rawUpdateManifest []byte

// update manifest was valid, increase svn and regenerate secrets
for pkgName, pkg := range updateManifest.Packages {
if currentPackages[pkgName].SecurityVersion == nil {
currentPkg := currentPackages[pkgName]
currentPackages[pkgName] = quote.PackageProperties{
Debug: currentPkg.Debug,
UniqueID: currentPkg.UniqueID,
SecurityVersion: pkg.SecurityVersion,
ProductID: currentPkg.ProductID,
SignerID: currentPkg.SignerID,
AcceptedTCBStatuses: currentPkg.AcceptedTCBStatuses,
}
} else {
*currentPackages[pkgName].SecurityVersion = *pkg.SecurityVersion
}
currentPkg := currentPackages[pkgName]
currentPkg.SecurityVersion = pkg.SecurityVersion
currentPackages[pkgName] = currentPkg
}

rootCert, err := txdata.GetCertificate(constants.SKCoordinatorRootCert)
Expand Down
7 changes: 7 additions & 0 deletions coordinator/quote/ert.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ type PackageProperties struct {
SecurityVersion *uint
// AcceptedTCBStatuses is a list of TCB levels an enclave is allowed to have.
AcceptedTCBStatuses []string
// AcceptedAdvisories is a list of open Intel Security Advisories an enclave is allowed to run on,
// when it reports an SWHardeningNeeded TCB status.
// An empty list allows all advisories.
AcceptedAdvisories []string
daniel-weisse marked this conversation as resolved.
Show resolved Hide resolved
daniel-weisse marked this conversation as resolved.
Show resolved Hide resolved
}

// InfrastructureProperties contains the infrastructure-specific properties of a SGX DCAP quote.
Expand Down Expand Up @@ -107,6 +111,9 @@ func (p PackageProperties) String() string {
if len(p.AcceptedTCBStatuses) > 0 {
values = append(values, fmt.Sprintf("AcceptedTCBStatuses: %v", p.AcceptedTCBStatuses))
}
if len(p.AcceptedAdvisories) > 0 {
values = append(values, fmt.Sprintf("AcceptedAdvisories: %v", p.AcceptedAdvisories))
}
return fmt.Sprintf("{%s}", strings.Join(values, ", "))
}

Expand Down
8 changes: 8 additions & 0 deletions coordinator/quote/ertvalidator/ertvalidator.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ func (v *ERTValidator) Validate(givenQuote []byte, cert []byte, pp quote.Package
v.log.Error("TCB Advisories", zap.Error(report.TCBAdvisoriesErr))
}

notAccepted, err := tcb.CheckAdvisories(report, pp.AcceptedAdvisories)
if err != nil {
return err
}
if len(notAccepted) > 0 {
return fmt.Errorf("TCB status %s contains advisories not accepted by configuration: %s", report.TCBStatus, notAccepted)
daniel-weisse marked this conversation as resolved.
Show resolved Hide resolved
}

switch validity {
case tcb.ValidityUnconditional:
case tcb.ValidityConditional:
Expand Down
Loading
Loading