AB#2413: Add workflow for snp-report-verify

* Extend azure-snp-report-verify to also report fw SVNs.
* Add workflow based on azure-cvm to get maa-jwt and
verify it on a second runner.
This commit is contained in:
Otto Bittner 2022-09-21 10:47:38 +02:00
parent d85b281570
commit 0eb4a7831b
6 changed files with 133 additions and 18 deletions

View file

@ -10,7 +10,7 @@ RUN wget -q https://github.com/Azure/confidential-computing-cvm-guest-attestatio
&& touch Utils.h \
&& g++ -Os -I/usr/include/azguestattestation1 -oclient main.cpp -lazguestattestation
FROM ubuntu:20.04
FROM ubuntu:20.04 AS release
COPY --from=build client azguestattestation1_1.0.2_amd64.deb /
RUN apt-get update && apt-get install -y /azguestattestation1_1.0.2_amd64.deb
ENTRYPOINT ["/client"]

View file

@ -1,20 +1,21 @@
# Obtain current Azure SNP ID key digest
# Obtain current Azure SNP ID key digest & firmware versions
On Azure, Constellation verifies that the SNP attestation report contains Azure's ID key digest.
Currently, the only way to verify this digest's origin is to perform guest attestation with the help of the Microsoft Azure Attestation (MAA) service.
Additionally, some firmware security version numbers (SVNs) are validated.
Currently, the only way to verify the digest's origin is to perform guest attestation with the help of the Microsoft Azure Attestation (MAA) service.
There's a [sample](https://github.com/Azure/confidential-computing-cvm-guest-attestation) on how to do this, but it's not straightforward.
So we created tooling to make things easier.
Perform the following steps to get the ID key digest:
Perform the following steps to get the ID key digest & firmware versions:
1. Create an Ubuntu CVM on Azure with secure boot enabled and ssh into it.
2. Run
```
docker run --rm --privileged -v/sys/kernel/security:/sys/kernel/security ghcr.io/edgelesssys/constellation/get-azure-snp-jwt
docker run --rm --privileged -v/sys/kernel/security:/sys/kernel/security ghcr.io/edgelesssys/constellation/azure-snp-reporter
```
This executes the guest attestation and prints the JWT received from the MAA. (It's the long base64 blob.)
3. Copy the JWT and run **on your local trusted machine**:
```
go run verify.go <jwt>
```
On success it prints the ID key digest.
On success it prints the ID key digest and relevant firmware SVNs.

View file

@ -23,33 +23,52 @@ import (
"gopkg.in/square/go-jose.v2/jwt"
)
type IsolationTEE struct {
IDKeyDigest string `json:"x-ms-sevsnpvm-idkeydigest"`
TEESvn int `json:"x-ms-sevsnpvm-tee-svn"`
SNPFwSvn int `json:"x-ms-sevsnpvm-snpfw-svn"`
MicrocodeSvn int `json:"x-ms-sevsnpvm-microcode-svn"`
BootloaderSvn int `json:"x-ms-sevsnpvm-bootloader-svn"`
GuestSvn int `json:"x-ms-sevsnpvm-guestsvn"`
}
func (i *IsolationTEE) PrintSVNs() {
fmt.Println("\tTEE SVN:", i.TEESvn)
fmt.Println("\tSNP FW SVN:", i.SNPFwSvn)
fmt.Println("\tMicrocode SVN:", i.MicrocodeSvn)
fmt.Println("\tBootloader SVN:", i.BootloaderSvn)
fmt.Println("\tGuest SVN:", i.GuestSvn)
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage:", os.Args[0], "<maa-jwt>")
return
}
idKeyDigest, err := getIDKeyDigest(os.Args[1])
report, err := getTEEReport(os.Args[1])
if err != nil {
panic(err)
}
fmt.Println("Successfully validated ID key digest:", idKeyDigest)
fmt.Println("Successfully validated ID key digest:", report.IDKeyDigest)
fmt.Println("Currently reported SVNs:")
report.PrintSVNs()
}
func getIDKeyDigest(rawToken string) (string, error) {
func getTEEReport(rawToken string) (IsolationTEE, error) {
// Parse token.
token, err := jwt.ParseSigned(rawToken)
if err != nil {
return "", err
return IsolationTEE{}, err
}
// Get JSON Web Key set.
keySetBytes, err := httpGet(context.Background(), "https://sharedeus.eus.attest.azure.net/certs")
if err != nil {
return "", err
return IsolationTEE{}, err
}
keySet, err := parseKeySet(keySetBytes)
if err != nil {
return "", err
return IsolationTEE{}, err
}
// Get claims. Private claims contain ID Key digest.
@ -57,19 +76,17 @@ func getIDKeyDigest(rawToken string) (string, error) {
var publicClaims jwt.Claims
var privateClaims struct {
IsolationTEE struct {
IDKeyDigest string `json:"x-ms-sevsnpvm-idkeydigest"`
} `json:"x-ms-isolation-tee"`
IsolationTEE IsolationTEE `json:"x-ms-isolation-tee"`
}
if err := token.Claims(&keySet, &publicClaims, &privateClaims); err != nil {
return "", err
return IsolationTEE{}, err
}
if err := publicClaims.Validate(jwt.Expected{Time: time.Now()}); err != nil {
return "", err
return IsolationTEE{}, err
}
return privateClaims.IsolationTEE.IDKeyDigest, nil
return privateClaims.IsolationTEE, nil
}
func parseKeySet(keySetBytes []byte) (jose.JSONWebKeySet, error) {