Skip to content
Draft
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
15 changes: 15 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ require (

require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.14 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
github.com/aws/aws-sdk-go-v2/service/ec2 v1.296.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
github.com/aws/smithy-go v1.24.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
Expand Down
32 changes: 31 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@ github.com/GoogleCloudPlatform/confidential-space/server v0.0.0-20260307011055-8
github.com/GoogleCloudPlatform/confidential-space/server v0.0.0-20260307011055-895ec9019dd7/go.mod h1:sNFt/HcARjGxR3/2s7hwlqvHlUzXdaCiS62u7A4rnHg=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2/config v1.32.14 h1:opVIRo/ZbbI8OIqSOKmpFaY7IwfFUOCCXBsUpJOwDdI=
github.com/aws/aws-sdk-go-v2/config v1.32.14/go.mod h1:U4/V0uKxh0Tl5sxmCBZ3AecYny4UNlVmObYjKuuaiOo=
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 h1:n+UcGWAIZHkXzYt87uMFBv/l8THYELoX6gVcUvgl6fI=
github.com/aws/aws-sdk-go-v2/credentials v1.19.14/go.mod h1:cJKuyWB59Mqi0jM3nFYQRmnHVQIcgoxjEMAbLkpr62w=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.296.2 h1:Ytu50ChAxCiDsOlBcBq8jbczXy6+QLb07T65DBJASRs=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.296.2/go.mod h1:R+2BNtUfTfhPY0RH18oL02q116bakeBWjanrbnVBqkM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 h1:lFd1+ZSEYJZYvv9d6kXzhkZu07si3f+GQ1AaYwa2LUM=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6fuOwWlWpD2StNLTceKpys=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw=
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
Expand Down Expand Up @@ -126,4 +156,4 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
39 changes: 39 additions & 0 deletions pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"strings"
"sync"

"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/google/go-attestation/attest"
nodeattestorv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/agent/nodeattestor/v1"
configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1"
Expand All @@ -48,9 +50,15 @@ type Plugin struct {

type Config struct {
trustDomain string
AWS AWSConfig `hcl:"aws"`
PVE PVEConfig `hcl:"pve"`
}

type AWSConfig struct {
Enabled bool `hcl:"enabled"`
DiscoveryMethod string `hcl:"discovery_method"` // "", "smbios" or "metadata"
}

type PVEConfig struct {
Enabled bool `hcl:"enabled"`
}
Expand Down Expand Up @@ -215,6 +223,29 @@ func (p *Plugin) generateAttestationData(ctx context.Context) (*common.Attestati
}

conf := p.getConfig()
if conf.AWS.Enabled {
data.AWS = &common.AWSInstanceData{}
method := strings.ToLower(conf.AWS.DiscoveryMethod)
if method == "" {
method = "smbios"
}

switch method {
case "metadata":
cfg, err := config.LoadDefaultConfig(ctx)
if err == nil {
imdsClient := imds.NewFromConfig(cfg)
output, err := imdsClient.GetInstanceIdentityDocument(ctx, &imds.GetInstanceIdentityDocumentInput{})
if err == nil && output.InstanceIdentityDocument.InstanceID != "" {
data.AWS.InstanceID = output.InstanceIdentityDocument.InstanceID
}
}
case "smbios":
data.AWS.InstanceID = p.getAWSInstanceIDFromSMBIOS()
default:
return nil, nil, fmt.Errorf("bad method")
}
}

if conf.PVE.Enabled {
data.PVE = &common.PVEInstanceData{
Expand All @@ -227,6 +258,14 @@ func (p *Plugin) generateAttestationData(ctx context.Context) (*common.Attestati
return data, aikBytes, nil
}

func (p *Plugin) getAWSInstanceIDFromSMBIOS() string {
data, err := os.ReadFile("/sys/devices/virtual/dmi/id/board_asset_tag")
if err != nil {
return ""
}
return strings.TrimSpace(string(data))
}

func (p *Plugin) getPVEVMIDFromSMBIOS() int32 {
data, err := os.ReadFile("/sys/devices/virtual/dmi/id/product_sku")
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions pkg/common/tpm_attestor.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ const (
type AttestationData struct {
EK []byte
AK *attest.AttestationParameters
AWS *AWSInstanceData
PVE *PVEInstanceData
}

type AWSInstanceData struct {
InstanceID string `json:"instance_id"`
}

type PVEInstanceData struct {
CUID string `json:"cuid"`
UUID string `json:"uuid"`
Expand Down
48 changes: 47 additions & 1 deletion pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ import (
"bytes"
"context"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"sync"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/bloomberg/spire-tpm-plugin/pkg/common"
"github.com/google/go-attestation/attest"
x509ext "github.com/google/go-attestation/x509"
Expand All @@ -43,9 +47,15 @@ type Config struct {
trustDomain string
CaPath string `hcl:"ca_path"`
HashPath string `hcl:"hash_path"`
AWS AWSConfig `hcl:"aws"`
PVE PVEGlobalConfig `hcl:"pve"`
}

type AWSConfig struct {
Enabled bool `hcl:"enabled"`
HashPath string `hcl:"hash_path"`
}

// Plugin implements the nodeattestor Plugin interface
type Plugin struct {
nodeattestorv1.UnsafeNodeAttestorServer
Expand Down Expand Up @@ -162,7 +172,22 @@ func (p *Plugin) Attest(stream nodeattestorv1.NodeAttestor_AttestServer) error {
var selectors []string
caCheck := false
validEK := false
if p.config.PVE.Enabled && attestationData.PVE != nil {
if p.config.AWS.Enabled && attestationData.AWS != nil {
if attestationData.AWS.InstanceID == "" {
return fmt.Errorf("tpm: bad aws data")
}
pubBytes, _ := x509.MarshalPKIXPublicKey(ek.Public)
awsSelectors, err := p.verifyAWSTPM(stream.Context(), attestationData.AWS.InstanceID, pubBytes)
if err == nil {
selectors = append(selectors, awsSelectors...)

if p.config.AWS.HashPath != "" {
validEK = checkHashAllowed(p.config.AWS.HashPath, hashEncoded)
} else {
validEK = true
}
}
} else if p.config.PVE.Enabled && attestationData.PVE != nil {
clusterConf, ok := p.config.PVE.Clusters[attestationData.PVE.CUID]
hashPath := p.config.PVE.Clusters[attestationData.PVE.CUID].HashPath
if !ok {
Expand Down Expand Up @@ -304,6 +329,27 @@ func (p *Plugin) Attest(stream nodeattestorv1.NodeAttestor_AttestServer) error {
})
}

func (p *Plugin) verifyAWSTPM(ctx context.Context, instanceID string, ekPub []byte) ([]string, error) {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return nil, err
}
client := ec2.NewFromConfig(cfg)
out, err := client.GetInstanceTpmEkPub(ctx, &ec2.GetInstanceTpmEkPubInput{
InstanceId: aws.String(instanceID),
KeyFormat: "der",
KeyType: "rsa-2048",
})
if err != nil {
return nil, err
}
decodedAWSKey, _ := base64.StdEncoding.DecodeString(*out.KeyValue)
if !bytes.Equal(decodedAWSKey, ekPub) {
return nil, errors.New("EK mismatch")
}
return []string{"aws:instance_id:" + instanceID}, nil
}

func checkHashAllowed(hashPath, hashEncoded string) bool {
// Check if hashPath is a directory, fail if this is simply a file
fileInfo, err := os.Stat(hashPath)
Expand Down
Loading