mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-25 15:39:37 -05:00
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:
parent
d85b281570
commit
0eb4a7831b
12
.github/actions/azure_snp_reporter/action.yaml
vendored
Normal file
12
.github/actions/azure_snp_reporter/action.yaml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
name: Azure SNP Reporter
|
||||||
|
description: "Get SNP MAA statement from Azure."
|
||||||
|
inputs:
|
||||||
|
outputPath:
|
||||||
|
description: "Path to put signed JWT into."
|
||||||
|
required: true
|
||||||
|
runs:
|
||||||
|
using: "composite" # some azure SNP-enabled machine.
|
||||||
|
steps:
|
||||||
|
- name: Fetch report
|
||||||
|
shell: bash
|
||||||
|
run: docker run --rm --privileged -v/sys/kernel/security:/sys/kernel/security ghcr.io/edgelesssys/constellation/azure-snp-reporter | tail -n 1 > ${{ inputs.outputPath }}
|
4
.github/runners/azure-cvm/README.md
vendored
4
.github/runners/azure-cvm/README.md
vendored
@ -5,6 +5,10 @@ This folder contains the files to setup an Azure function and ARM template in or
|
|||||||
- `azure-function`: All necessary files to redeploy the function. Changes in `requirements.txt` are installed during deployment of the function. `cloud-init.txt` is put into the CVM by supplying it as a parameter to the ARM template deployment.
|
- `azure-function`: All necessary files to redeploy the function. Changes in `requirements.txt` are installed during deployment of the function. `cloud-init.txt` is put into the CVM by supplying it as a parameter to the ARM template deployment.
|
||||||
|
|
||||||
# Update cvm-template
|
# Update cvm-template
|
||||||
|
In order to make the Azure function use your changes you will have to publish them in the `cvm-template.json` file.
|
||||||
|
While developing you can point the `template_id` variable in `__init__.py` to a different location.
|
||||||
|
|
||||||
|
Doing the following you can debug your template changes:
|
||||||
- Look for the `Template spec` resource in your Azure project (e.g. "snp-value-reporter-template").
|
- Look for the `Template spec` resource in your Azure project (e.g. "snp-value-reporter-template").
|
||||||
- Click on "Create new version".
|
- Click on "Create new version".
|
||||||
- Select the latest version available.
|
- Select the latest version available.
|
||||||
|
81
.github/workflows/azure-snp-reporter.yml
vendored
Normal file
81
.github/workflows/azure-snp-reporter.yml
vendored
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
name: Fetch, validate and report SNP report data.
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 14 * * 0"
|
||||||
|
|
||||||
|
# Abort runs of *this* workflow, if a new commit with the same ref is pushed that is not main.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-snp-reporter:
|
||||||
|
name: "Build SNP-reporter container"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f
|
||||||
|
with:
|
||||||
|
go-version: 1.18
|
||||||
|
|
||||||
|
- name: Build and upload azure SNP reporter container image
|
||||||
|
id: build-and-upload
|
||||||
|
uses: ./.github/actions/build_micro_service
|
||||||
|
with:
|
||||||
|
name: azure-snp-reporter
|
||||||
|
dockerfile: ./hack/azure-snp-report-verify/Dockerfile
|
||||||
|
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
fetch-snp-report:
|
||||||
|
needs: build-snp-reporter
|
||||||
|
name: "Fetch SNP report"
|
||||||
|
runs-on: [self-hosted, azure-cvm]
|
||||||
|
env:
|
||||||
|
SHELL: /bin/bash
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
|
||||||
|
|
||||||
|
- name: Fetch SNP report
|
||||||
|
uses: ./.github/actions/azure_snp_reporter
|
||||||
|
with:
|
||||||
|
outputPath: ${{ github.workspace }}/maa-report.jwt
|
||||||
|
|
||||||
|
- name: Upload report JWT
|
||||||
|
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8
|
||||||
|
with:
|
||||||
|
name: maa-report.jwt
|
||||||
|
path: "${{ github.workspace }}/maa-report.jwt"
|
||||||
|
|
||||||
|
validate-snp-report:
|
||||||
|
needs: fetch-snp-report
|
||||||
|
name: "Validate SNP report"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
SHELL: /bin/bash
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
token: ${{ secrets.CI_GITHUB_REPOSITORY }}
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f
|
||||||
|
with:
|
||||||
|
go-version: 1.18
|
||||||
|
|
||||||
|
- name: Download report JWT
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: "maa-report.jwt"
|
||||||
|
path: "."
|
||||||
|
|
||||||
|
- name: Verify report
|
||||||
|
shell: bash
|
||||||
|
run: go run ./hack/azure-snp-report-verify/verify.go $(cat ./maa-report.jwt)
|
@ -10,7 +10,7 @@ RUN wget -q https://github.com/Azure/confidential-computing-cvm-guest-attestatio
|
|||||||
&& touch Utils.h \
|
&& touch Utils.h \
|
||||||
&& g++ -Os -I/usr/include/azguestattestation1 -oclient main.cpp -lazguestattestation
|
&& 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 /
|
COPY --from=build client azguestattestation1_1.0.2_amd64.deb /
|
||||||
RUN apt-get update && apt-get install -y /azguestattestation1_1.0.2_amd64.deb
|
RUN apt-get update && apt-get install -y /azguestattestation1_1.0.2_amd64.deb
|
||||||
ENTRYPOINT ["/client"]
|
ENTRYPOINT ["/client"]
|
@ -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.
|
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.
|
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.
|
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.
|
1. Create an Ubuntu CVM on Azure with secure boot enabled and ssh into it.
|
||||||
2. Run
|
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.)
|
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**:
|
3. Copy the JWT and run **on your local trusted machine**:
|
||||||
```
|
```
|
||||||
go run verify.go <jwt>
|
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.
|
@ -23,33 +23,52 @@ import (
|
|||||||
"gopkg.in/square/go-jose.v2/jwt"
|
"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() {
|
func main() {
|
||||||
if len(os.Args) != 2 {
|
if len(os.Args) != 2 {
|
||||||
fmt.Println("Usage:", os.Args[0], "<maa-jwt>")
|
fmt.Println("Usage:", os.Args[0], "<maa-jwt>")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
idKeyDigest, err := getIDKeyDigest(os.Args[1])
|
report, err := getTEEReport(os.Args[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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.
|
// Parse token.
|
||||||
token, err := jwt.ParseSigned(rawToken)
|
token, err := jwt.ParseSigned(rawToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return IsolationTEE{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get JSON Web Key set.
|
// Get JSON Web Key set.
|
||||||
keySetBytes, err := httpGet(context.Background(), "https://sharedeus.eus.attest.azure.net/certs")
|
keySetBytes, err := httpGet(context.Background(), "https://sharedeus.eus.attest.azure.net/certs")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return IsolationTEE{}, err
|
||||||
}
|
}
|
||||||
keySet, err := parseKeySet(keySetBytes)
|
keySet, err := parseKeySet(keySetBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return IsolationTEE{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get claims. Private claims contain ID Key digest.
|
// Get claims. Private claims contain ID Key digest.
|
||||||
@ -57,19 +76,17 @@ func getIDKeyDigest(rawToken string) (string, error) {
|
|||||||
var publicClaims jwt.Claims
|
var publicClaims jwt.Claims
|
||||||
|
|
||||||
var privateClaims struct {
|
var privateClaims struct {
|
||||||
IsolationTEE struct {
|
IsolationTEE IsolationTEE `json:"x-ms-isolation-tee"`
|
||||||
IDKeyDigest string `json:"x-ms-sevsnpvm-idkeydigest"`
|
|
||||||
} `json:"x-ms-isolation-tee"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := token.Claims(&keySet, &publicClaims, &privateClaims); err != nil {
|
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 {
|
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) {
|
func parseKeySet(keySetBytes []byte) (jose.JSONWebKeySet, error) {
|
Loading…
Reference in New Issue
Block a user