NOISSUE - Track TDX policy (#557)

* Add initial implementation of attestation policy for SEV-SNP and TDX, including JSON configuration files and build scripts

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* Update working directory for Rust CI pipeline to sev-snp

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* fix build

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* fix tests

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* fix tests

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

---------

Signed-off-by: Sammy Oina <sammyoina@gmail.com>
This commit is contained in:
Sammy Kerata Oina
2026-01-12 16:59:23 +03:00
committed by GitHub
parent c422afe0a6
commit 3498db14fb
22 changed files with 243 additions and 27 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./scripts/attestation_policy
working-directory: ./scripts/attestation_policy/sev-snp
steps:
- name: Checkout Code
+1
View File
@@ -50,6 +50,7 @@ install: $(SERVICES) $(ATTESTATION_POLICY)
install $(BUILD_DIR)/cocos-cli $(INSTALL_DIR)/cocos-cli
install $(BUILD_DIR)/cocos-manager $(INSTALL_DIR)/cocos-manager
install $(BUILD_DIR)/attestation_policy $(INSTALL_DIR)/attestation_policy
install $(BUILD_DIR)/attestation_policy_tdx $(INSTALL_DIR)/attestation_policy_tdx
install -d $(CONFIG_DIR)
install cocos-manager.env $(CONFIG_DIR)/cocos-manager.env
+46
View File
@@ -301,6 +301,52 @@ func (cli *CLI) NewAzureAttestationPolicy() *cobra.Command {
return cmd
}
func (cli *CLI) NewTDXAttestationPolicy() *cobra.Command {
cmd := &cobra.Command{
Use: "tdx",
Short: "Get attestation policy for TDX CVM",
Example: `tdx <tdx_attestation_report_file>`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
attestationFile := args[0]
// Parse TDX configuration from flags or config file
if err := validateTDXFlags(); err != nil {
printError(cmd, "Error validating TDX flags: %v ❌ ", err)
return
}
// Read and verify the attestation report to extract policy values
attestationBytes, err := os.ReadFile(attestationFile)
if err != nil {
printError(cmd, "Error reading attestation report file: %v ❌ ", err)
return
}
// If the config is not provided via flags, we can extract it from the attestation report
// For now, we'll use the cfgTDX that was populated from flags
if len(attestationBytes) > 0 {
cmd.Printf("Read %d bytes from attestation report\n", len(attestationBytes))
}
attestationPolicyJson, err := json.MarshalIndent(cfgTDX, "", " ")
if err != nil {
printError(cmd, "Error marshaling attestation policy: %v ❌ ", err)
return
}
if err := os.WriteFile("attestation_policy.json", attestationPolicyJson, filePermission); err != nil {
printError(cmd, "Error writing attestation policy file: %v ❌ ", err)
return
}
cmd.Println("Attestation policy file generated successfully ✅")
},
}
return addTDXVerificationOptions(cmd)
}
func (cli *CLI) NewExtendWithManifestCmd() *cobra.Command {
return &cobra.Command{
Use: "extend",
+2 -2
View File
@@ -397,7 +397,7 @@ func TestExtendWithManifestHandling(t *testing.T) {
t.Run("Invalid manifest file", func(t *testing.T) {
cmd := cli.NewExtendWithManifestCmd()
cmd.SetArgs([]string{"../scripts/attestation_policy/attestation_policy.json", "nonexistent.manifest.json"})
cmd.SetArgs([]string{"../scripts/attestation_policy/sev-snp/attestation_policy.json", "nonexistent.manifest.json"})
var buf bytes.Buffer
cmd.SetOut(&buf)
@@ -458,7 +458,7 @@ func TestExtendWithManifestHandling(t *testing.T) {
}
cmd := cli.NewExtendWithManifestCmd()
cmd.SetArgs([]string{"../scripts/attestation_policy/attestation_policy.json", manifestFile.Name()})
cmd.SetArgs([]string{"../scripts/attestation_policy/sev-snp/attestation_policy.json", manifestFile.Name()})
var buf bytes.Buffer
cmd.SetOut(&buf)
+1
View File
@@ -164,6 +164,7 @@ func main() {
attestationPolicyCmd.AddCommand(cliSVC.NewGCPAttestationPolicy())
attestationPolicyCmd.AddCommand(cliSVC.NewDownloadGCPOvmfFile())
attestationPolicyCmd.AddCommand(cliSVC.NewAzureAttestationPolicy())
attestationPolicyCmd.AddCommand(cliSVC.NewTDXAttestationPolicy())
attestationPolicyCmd.AddCommand(cliSVC.NewExtendWithManifestCmd())
if err := rootCmd.Execute(); err != nil {
+1 -1
View File
@@ -596,7 +596,7 @@ func prepVerifyAttReport(t *testing.T) *sevsnp.Attestation {
}
func setAttestationPolicy(rr *sevsnp.Attestation, policyDirectory string) error {
attestationPolicyFile, err := os.ReadFile("../../scripts/attestation_policy/attestation_policy.json")
attestationPolicyFile, err := os.ReadFile("../../scripts/attestation_policy/sev-snp/attestation_policy.json")
if err != nil {
return err
}
+1 -1
View File
@@ -807,7 +807,7 @@ func prepVerifyAttReport(t *testing.T) (*sevsnp.Attestation, []byte) {
}
func setAttestationPolicy(rr *sevsnp.Attestation, policyDirectory string) error {
attestationPolicyFile, err := os.ReadFile("../../../scripts/attestation_policy/attestation_policy.json")
attestationPolicyFile, err := os.ReadFile("../../../scripts/attestation_policy/sev-snp/attestation_policy.json")
if err != nil {
return err
}
+1 -1
View File
@@ -92,7 +92,7 @@ func TestNewClient(t *testing.T) {
ClientKey: clientKeyFile,
},
AttestedTLS: true,
AttestationPolicy: "../../../scripts/attestation_policy/attestation_policy.json",
AttestationPolicy: "../../../scripts/attestation_policy/sev-snp/attestation_policy.json",
},
wantErr: false,
err: nil,
+12 -20
View File
@@ -1,24 +1,16 @@
CARGO = cargo
TARGET = target
BUILD_DIR = $(TARGET)/release
BIN_NAME = attestation_policy
OUTPUT_DIR ?= $(BUILD_DIR)
OUTPUT_DIR ?= ../../build
PLATFORMS = sev-snp tdx
# Convert OUTPUT_DIR to absolute path
ABS_OUTPUT_DIR := $(shell cd $(OUTPUT_DIR) 2>/dev/null && pwd || mkdir -p $(OUTPUT_DIR) && cd $(OUTPUT_DIR) && pwd)
all: build
.PHONY: all build clean $(PLATFORMS)
build:
$(CARGO) build --release
@if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ]; then \
mkdir -p $(OUTPUT_DIR) && \
cp $(BUILD_DIR)/$(BIN_NAME) $(OUTPUT_DIR)/$(BIN_NAME) && \
echo "Copied $(BIN_NAME) to $(OUTPUT_DIR)/"; \
fi
all: $(PLATFORMS)
$(PLATFORMS):
$(MAKE) -C $@ OUTPUT_DIR=$(ABS_OUTPUT_DIR)
clean:
$(CARGO) clean
@if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ] && [ -f "$(OUTPUT_DIR)/$(BIN_NAME)" ]; then \
rm -f $(OUTPUT_DIR)/$(BIN_NAME) && \
echo "Removed $(BIN_NAME) from $(OUTPUT_DIR)/"; \
fi
.PHONY: all build clean
@for platform in $(PLATFORMS); do \
$(MAKE) -C $$platform OUTPUT_DIR=$(ABS_OUTPUT_DIR) clean; \
done
@@ -0,0 +1,24 @@
CARGO = cargo
TARGET = target
BUILD_DIR = $(TARGET)/release
BIN_NAME = attestation_policy
OUTPUT_DIR ?= $(BUILD_DIR)
all: build
build:
$(CARGO) build --release
@if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ]; then \
mkdir -p $(OUTPUT_DIR) && \
cp $(BUILD_DIR)/$(BIN_NAME) $(OUTPUT_DIR)/$(BIN_NAME) && \
echo "Copied $(BIN_NAME) to $(OUTPUT_DIR)/"; \
fi
clean:
$(CARGO) clean
@if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ] && [ -f "$(OUTPUT_DIR)/$(BIN_NAME)" ]; then \
rm -f $(OUTPUT_DIR)/$(BIN_NAME) && \
echo "Removed $(BIN_NAME) from $(OUTPUT_DIR)/"; \
fi
.PHONY: all build clean
+27
View File
@@ -0,0 +1,27 @@
GO = go
TARGET = target
BUILD_DIR = .
BIN_NAME = attestation_policy_tdx
OUTPUT_DIR ?= $(BUILD_DIR)
all: build
build:
$(GO) build -o $(BUILD_DIR)/$(BIN_NAME) .
@if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ]; then \
mkdir -p $(OUTPUT_DIR) && \
cp $(BUILD_DIR)/$(BIN_NAME) $(OUTPUT_DIR)/$(BIN_NAME) && \
echo "Copied $(BIN_NAME) to $(OUTPUT_DIR)/"; \
fi
clean:
@if [ -f "$(BUILD_DIR)/$(BIN_NAME)" ]; then \
rm -f $(BUILD_DIR)/$(BIN_NAME) && \
echo "Removed $(BIN_NAME) from $(BUILD_DIR)/"; \
fi
@if [ "$(OUTPUT_DIR)" != "$(BUILD_DIR)" ] && [ -f "$(OUTPUT_DIR)/$(BIN_NAME)" ]; then \
rm -f $(OUTPUT_DIR)/$(BIN_NAME) && \
echo "Removed $(BIN_NAME) from $(OUTPUT_DIR)/"; \
fi
.PHONY: all build clean
+7
View File
@@ -0,0 +1,7 @@
module tdxpolicy
go 1.24.2
require github.com/google/go-tdx-guest v0.3.1
require google.golang.org/protobuf v1.36.10
+6
View File
@@ -0,0 +1,6 @@
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-tdx-guest v0.3.1 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw=
github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+112
View File
@@ -0,0 +1,112 @@
package main
import (
"encoding/hex"
"os"
ccpb "github.com/google/go-tdx-guest/proto/checkconfig"
"google.golang.org/protobuf/encoding/protojson"
)
var (
SGXVendorID = []byte{0x93, 0x9A, 0x72, 0x33, 0xF7, 0x9C, 0x4C, 0xA9, 0x94, 0x0A, 0x0D, 0xB3, 0x95, 0x7F, 0x06, 0x07}
MinTdxSvn = []byte{0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
MrSeam = "5b38e33a6487958b72c3c12a938eaa5e3fd4510c51aeeab58c7d5ecee41d7c436489d6c8e4f92f160b7cad34207b00c1"
TdAttributes = []byte{
0x00, 0x00, 0x00, 0x10,
0x00, 0x00, 0x00, 0x00,
}
Xfam = []byte{
0xe7, 0x02, 0x06, 0x00,
0x00, 0x00, 0x00, 0x00,
}
MrTd = []byte{
0x91, 0xeb, 0x2b, 0x44, 0xd1, 0x41, 0xd4, 0xec,
0xe0, 0x9f, 0x0c, 0x75, 0xc2, 0xc5, 0x3d, 0x24,
0x7a, 0x3c, 0x68, 0xed, 0xd7, 0xfa, 0xfe, 0x8a,
0x35, 0x20, 0xc9, 0x42, 0xa6, 0x04, 0xa4, 0x07,
0xde, 0x03, 0xae, 0x6d, 0xc5, 0xf8, 0x7f, 0x27,
0x42, 0x8b, 0x25, 0x38, 0x87, 0x31, 0x18, 0xb7,
}
rtmr = []string{
"ce0891f46a18db93e7691f1cf73ed76593f7dec1b58f0927ccb56a99242bf63bc9551561f9ee7833d40395fae59547ab",
"062ac322e26b10874a84977a09735408a856aec77ff62b4975b1e90e33c18f05220ea522cdbffc3b2cf4451cc209e418",
"5fd86e8c3d5e45386f1ed0852de7e83ae1b774ee4366bd5213c9890e8e3ac8fad3f7e690891d37f7c81ac20a445cc0ff",
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
}
)
func main() {
cfgTdx := &ccpb.Config{
RootOfTrust: &ccpb.RootOfTrust{},
Policy: &ccpb.Policy{HeaderPolicy: &ccpb.HeaderPolicy{}, TdQuoteBodyPolicy: &ccpb.TDQuoteBodyPolicy{}},
}
cfgTdx.RootOfTrust.CheckCrl = true
cfgTdx.RootOfTrust.GetCollateral = true
cfgTdx.Policy.HeaderPolicy.QeVendorId = SGXVendorID
cfgTdx.Policy.TdQuoteBodyPolicy.MinimumTeeTcbSvn = MinTdxSvn
seam, err := hex.DecodeString(MrSeam)
if err != nil {
panic(err)
}
cfgTdx.Policy.TdQuoteBodyPolicy.MrSeam = seam
cfgTdx.Policy.TdQuoteBodyPolicy.TdAttributes = TdAttributes
cfgTdx.Policy.TdQuoteBodyPolicy.Xfam = Xfam
cfgTdx.Policy.TdQuoteBodyPolicy.MrTd = MrTd
for _, reg := range rtmr {
r, err := hex.DecodeString(reg)
if err != nil {
panic(err)
}
cfgTdx.Policy.TdQuoteBodyPolicy.Rtmrs = append(cfgTdx.Policy.TdQuoteBodyPolicy.Rtmrs, r)
}
marshaler := protojson.MarshalOptions{
Multiline: true,
Indent: " ",
}
tdxPolicy, err := marshaler.Marshal(cfgTdx)
if err != nil {
panic(err)
}
err = os.WriteFile("tdx_policy.json", tdxPolicy, 0644)
if err != nil {
panic(err)
}
_, err = os.Stdout.Write(tdxPolicy)
if err != nil {
panic(err)
}
Policy := &ccpb.Config{
RootOfTrust: &ccpb.RootOfTrust{},
Policy: &ccpb.Policy{HeaderPolicy: &ccpb.HeaderPolicy{}, TdQuoteBodyPolicy: &ccpb.TDQuoteBodyPolicy{}},
}
if err := ReadTDXAttestationPolicy("/home/cocosai/work/test/tdxpolicy/tdx_policy.json", Policy); err != nil {
panic(err)
}
}
func ReadTDXAttestationPolicy(policyPath string, policy *ccpb.Config) error {
policyByte, err := os.ReadFile(policyPath)
if err != nil {
return err
}
if err := protojson.Unmarshal(policyByte, policy); err != nil {
return err
}
// fmt.Print("Read TDX Attestation Policy:\n")
// fmt.Printf(policy.String())
return nil
}
+1 -1
View File
@@ -64,7 +64,7 @@ cd ../..
./build/cocos-cli policy hostdata '<host-data>' '<attestation_policy.json>'
# For attested TLS, also define the path to the attestation_policy.json that contains reference values for the fields of the attestation report
export AGENT_GRPC_ATTESTATION_POLICY=./scripts/attestation_policy/attestation_policy.json
export AGENT_GRPC_ATTESTATION_POLICY=./scripts/attestation_policy/sev-snp/attestation_policy.json
export AGENT_GRPC_ATTESTED_TLS=true
# Retrieve Attestation