Skip to content

Commit

Permalink
read from snp report
Browse files Browse the repository at this point in the history
  • Loading branch information
elchead committed Sep 25, 2023
1 parent 118f789 commit 7f22076
Show file tree
Hide file tree
Showing 7 changed files with 500 additions and 57 deletions.
11 changes: 5 additions & 6 deletions .github/actions/e2e_verify/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,9 @@ runs:
continue
fi
echo "Extracting TCB versions for API update"
startMAAToken="Microsoft Azure Attestation Token:"
endMAAToken="Verification OK"
sed -n "/${startMAAToken}/,/${endMAAToken}/ { /${startMAAToken}/d; /${endMAAToken}/d; p }" <<< "${verifyOut}" > "maa-claims-${node}.json"
echo "Writing report to file"
echo "${verifyOut}" > "snp-report-${node}.txt"
done
- name: Login to AWS
Expand All @@ -94,8 +93,8 @@ runs:
COSIGN_PASSWORD: ${{ inputs.cosignPassword }}
COSIGN_PRIVATE_KEY: ${{ inputs.cosignPrivateKey }}
run: |
for file in $(ls maa-claims-*.json); do
for file in $(ls snp-report-*.txt); do
path=$(realpath "${file}")
cat "${path}"
bazel run //internal/api/attestationconfigapi/cli -- --maa-claims-path "${path}"
bazel run //internal/api/attestationconfigapi/cli -- --snp-report-path "${path}"
done
2 changes: 1 addition & 1 deletion dev-docs/workflows/attestationconfigapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ COSIGN_PASSWORD=$CPW COSIGN_PRIVATE_KEY="$(cat $PATH_TO_KEY)" AWS_ACCESS_KEY_ID=

### Manually upload a version
```
COSIGN_PASSWORD=$CPW COSIGN_PRIVATE_KEY="$(cat $PATH_TO_KEY)" AWS_ACCESS_KEY_ID=$ID AWS_ACCESS_KEY=$KEY bazel run //internal/api/attestationconfigapi/cli -- --force --version 2023-09-02-12-52 --maa-claims-path "${path}"
COSIGN_PASSWORD=$CPW COSIGN_PRIVATE_KEY="$(cat $PATH_TO_KEY)" AWS_ACCESS_KEY_ID=$ID AWS_ACCESS_KEY=$KEY bazel run //internal/api/attestationconfigapi/cli -- --force --version 2023-09-02-12-52 --snp-report-path "${path}"
```
6 changes: 5 additions & 1 deletion internal/api/attestationconfigapi/cli/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ go_library(
srcs = [
"delete.go",
"main.go",
"parse_report.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli",
visibility = ["//visibility:private"],
Expand All @@ -31,7 +32,10 @@ go_library(

go_test(
name = "cli_test",
srcs = ["delete_test.go"],
srcs = [
"delete_test.go",
"parse_report_test.go",
],
embed = [":cli_lib"],
deps = [
"@com_github_stretchr_testify//assert",
Expand Down
81 changes: 45 additions & 36 deletions internal/api/attestationconfigapi/cli/e2e/test.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,54 +30,63 @@ registerExitHandler "rm -rf $tmpdir"
# empty the bucket version state
${configapi_cli} delete recursive --region "$region" --bucket "$bucket"

# the high version numbers ensure that it's newer than the current latest value
readonly current_claim_path="$tmpdir/currentMaaClaim.json"
cat << EOF > "$current_claim_path"
{
"x-ms-isolation-tee": {
"x-ms-sevsnpvm-tee-svn": 1,
"x-ms-sevsnpvm-snpfw-svn": 1,
"x-ms-sevsnpvm-microcode-svn": 1,
"x-ms-sevsnpvm-bootloader-svn": 1
}
}
EOF
# upload a fake latest version for the fetcher
${configapi_cli} --force --maa-claims-path "$current_claim_path" --upload-date "2000-01-01-01-01" --region "$region" --bucket "$bucket"
# this is a shortened version of constellation verify output
readonly current_report_path="$tmpdir/currentReport.txt"
cat << EOF > "$current_report_path"
Launch TCB:
Secure Processor bootloader SVN: 1
Secure Processor operating system SVN: 1
SVN 4 (reserved): 0
SVN 5 (reserved): 0
SVN 6 (reserved): 0
SVN 7 (reserved): 0
SEV-SNP firmware SVN: 1
Microcode SVN: 1
Signature (DER):
3066023100d6606c3fc55b649dc5700c63ed4298aedf2a201a47530fcd31f56c51dfa8c198d6a9b29666164130f36f2fe61f5b9796023100a3a16f9b8c996d60699a32651b458489ba99135484809fdd43098db43e3bb8519ccbd6643c09defb2ddb40ee00d6d9a0
EOF
${configapi_cli} --force --snp-report-path "$current_report_path" --upload-date "2000-01-01-01-01" --region "$region" --bucket "$bucket"

# the high version numbers ensure that it's newer than the current latest value
readonly claim_path="$tmpdir/maaClaim.json"
cat << EOF > "$claim_path"
{
"x-ms-isolation-tee": {
"x-ms-sevsnpvm-tee-svn": 255,
"x-ms-sevsnpvm-snpfw-svn": 255,
"x-ms-sevsnpvm-microcode-svn": 255,
"x-ms-sevsnpvm-bootloader-svn": 255
}
}
readonly report_path="$tmpdir/report.txt"
cat << EOF > "$report_path"
Launch TCB:
Secure Processor bootloader SVN: 255
Secure Processor operating system SVN: 255
SVN 4 (reserved): 0
SVN 5 (reserved): 0
SVN 6 (reserved): 0
SVN 7 (reserved): 0
SEV-SNP firmware SVN: 255
Microcode SVN: 255
Signature (DER):
3066023100d6606c3fc55b649dc5700c63ed4298aedf2a201a47530fcd31f56c51dfa8c198d6a9b29666164130f36f2fe61f5b9796023100a3a16f9b8c996d60699a32651b458489ba99135484809fdd43098db43e3bb8519ccbd6643c09defb2ddb40ee00d6d9a0
EOF

# has an older version
readonly older_claim_path="$tmpdir/maaClaimOld.json"
cat << EOF > "$older_claim_path"
{
"x-ms-isolation-tee": {
"x-ms-sevsnpvm-tee-svn": 255,
"x-ms-sevsnpvm-snpfw-svn": 255,
"x-ms-sevsnpvm-microcode-svn": 254,
"x-ms-sevsnpvm-bootloader-svn": 255
}
}
readonly older_report_path="$tmpdir/oldReport.txt"
cat << EOF > "$older_report_path"
Launch TCB:
Secure Processor bootloader SVN: 254
Secure Processor operating system SVN: 255
SVN 4 (reserved): 0
SVN 5 (reserved): 0
SVN 6 (reserved): 0
SVN 7 (reserved): 0
SEV-SNP firmware SVN: 255
Microcode SVN: 255
Signature (DER):
3066023100d6606c3fc55b649dc5700c63ed4298aedf2a201a47530fcd31f56c51dfa8c198d6a9b29666164130f36f2fe61f5b9796023100a3a16f9b8c996d60699a32651b458489ba99135484809fdd43098db43e3bb8519ccbd6643c09defb2ddb40ee00d6d9a0
EOF

# report 3 versions with different dates to fill the reporter cache
readonly date_oldest="2023-02-01-03-04"
${configapi_cli} --maa-claims-path "$older_claim_path" --upload-date "$date_oldest" --region "$region" --bucket "$bucket" --cache-window-size 3
${configapi_cli} --snp-report-path "$older_report_path" --upload-date "$date_oldest" --region "$region" --bucket "$bucket" --cache-window-size 3
readonly date_older="2023-02-02-03-04"
${configapi_cli} --maa-claims-path "$older_claim_path" --upload-date "$date_older" --region "$region" --bucket "$bucket" --cache-window-size 3
${configapi_cli} --snp-report-path "$older_report_path" --upload-date "$date_older" --region "$region" --bucket "$bucket" --cache-window-size 3
readonly date="2023-02-03-03-04"
${configapi_cli} --maa-claims-path "$claim_path" --upload-date "$date" --region "$region" --bucket "$bucket" --cache-window-size 3
${configapi_cli} --snp-report-path "$report_path" --upload-date "$date" --region "$region" --bucket "$bucket" --cache-window-size 3

# expect that $date_oldest is served as latest version
baseurl="https://d33dzgxuwsgbpw.cloudfront.net/constellation/v1/attestation/azure-sev-snp"
Expand Down
24 changes: 11 additions & 13 deletions internal/api/attestationconfigapi/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ Any version update is then pushed to the API.
package main

import (
"encoding/json"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -67,15 +66,15 @@ func newRootCmd() *cobra.Command {
PreRunE: envCheck,
RunE: runCmd,
}
rootCmd.Flags().StringP("maa-claims-path", "t", "", "File path to a json file containing the MAA claims.")
rootCmd.Flags().StringP("snp-report-path", "t", "", "File path to a file containing the Constellation verify output.")
rootCmd.Flags().StringP("upload-date", "d", "", "upload a version with this date as version name.")
rootCmd.Flags().BoolP("force", "f", false, "Use force to manually push a new latest version."+
" The version gets saved to the cache but the version selection logic is skipped.")
rootCmd.Flags().IntP("cache-window-size", "s", versionWindowSize, "Number of versions to be considered for the latest version.")
rootCmd.PersistentFlags().StringP("region", "r", awsRegion, "region of the targeted bucket.")
rootCmd.PersistentFlags().StringP("bucket", "b", awsBucket, "bucket targeted by all operations.")
rootCmd.PersistentFlags().Bool("testing", false, "upload to S3 test bucket.")
must(rootCmd.MarkFlagRequired("maa-claims-path"))
must(rootCmd.MarkFlagRequired("snp-report-path"))
rootCmd.AddCommand(newDeleteCmd())
return rootCmd
}
Expand Down Expand Up @@ -104,16 +103,15 @@ func runCmd(cmd *cobra.Command, _ []string) (retErr error) {
DistributionID: flags.distribution,
}

log.Infof("Reading MAA claims from file: %s", flags.maaFilePath)
maaClaimsBytes, err := os.ReadFile(flags.maaFilePath)
log.Infof("Reading SNP report from file: %s", flags.snpReportPath)
snpFile, err := os.OpenFile(flags.snpReportPath, os.O_RDONLY, 0o600)
if err != nil {
return fmt.Errorf("reading MAA claims file: %w", err)
return fmt.Errorf("opening snp report file: %w", err)
}
var maaTCB maaTokenTCBClaims
if err = json.Unmarshal(maaClaimsBytes, &maaTCB); err != nil {
return fmt.Errorf("unmarshalling MAA claims file: %w", err)
inputVersion, err := ParseSNPReport(snpFile)
if err != nil {
return fmt.Errorf("parsing snp report: %w", err)
}
inputVersion := maaTCB.ToAzureSEVSNPVersion()
log.Infof("Input version: %+v", inputVersion)

client, clientClose, err := attestationconfigapi.NewClient(ctx, cfg,
Expand Down Expand Up @@ -149,7 +147,7 @@ func runCmd(cmd *cobra.Command, _ []string) (retErr error) {
}

type config struct {
maaFilePath string
snpReportPath string
uploadDate time.Time
region string
bucket string
Expand All @@ -160,7 +158,7 @@ type config struct {
}

func parseCliFlags(cmd *cobra.Command) (config, error) {
maaFilePath, err := cmd.Flags().GetString("maa-claims-path")
snpReportFilePath, err := cmd.Flags().GetString("snp-report-path")
if err != nil {
return config{}, fmt.Errorf("getting maa claims path: %w", err)
}
Expand Down Expand Up @@ -203,7 +201,7 @@ func parseCliFlags(cmd *cobra.Command) (config, error) {
return config{}, fmt.Errorf("getting cache window size: %w", err)
}
return config{
maaFilePath: maaFilePath,
snpReportPath: snpReportFilePath,
uploadDate: uploadDate,
region: region,
bucket: bucket,
Expand Down
76 changes: 76 additions & 0 deletions internal/api/attestationconfigapi/cli/parse_report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/

package main

import (
"bufio"
"fmt"
"io"
"strconv"
"strings"

"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
)

// ParseSNPReport parses the SNP report and returns the version information.
func ParseSNPReport(reader io.Reader) (attestationconfigapi.AzureSEVSNPVersion, error) {
mp := parseLaunchTCBSection(reader)
return parseVersion(mp)
}

func parseLaunchTCBSection(reader io.Reader) map[string]string {
scanner := bufio.NewScanner(reader)
parsedValues := make(map[string]string)
inLaunchTCBSection := false

for scanner.Scan() {
line := scanner.Text()

if strings.Contains(line, "Launch TCB:") {
inLaunchTCBSection = true
continue // skip the current line as it is the section header
}

// Stop scanning if we have reached the end of the Launch TCB section
if inLaunchTCBSection && strings.Contains(line, "Signature (DER)") {
break
}

if inLaunchTCBSection {
parts := strings.SplitN(line, ":", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
parsedValues[key] = value
}
}
}

return parsedValues
}

func parseVersion(versionMap map[string]string) (res attestationconfigapi.AzureSEVSNPVersion, err error) {
version := attestationconfigapi.AzureSEVSNPVersion{}
for key, value := range versionMap {
val, err := strconv.ParseUint(value, 10, 8)
if err != nil {
return res, fmt.Errorf("could not parse value for key %s: %w", key, err)
}

switch key {
case "Secure Processor bootloader SVN":
version.Bootloader = uint8(val)
case "Secure Processor operating system SVN":
version.TEE = uint8(val)
case "SEV-SNP firmware SVN":
version.SNP = uint8(val)
case "Microcode SVN":
version.Microcode = uint8(val)
}
}
return version, nil
}
Loading

0 comments on commit 7f22076

Please sign in to comment.