mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 23:19:39 -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.
|
||||
|
||||
# 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").
|
||||
- Click on "Create new version".
|
||||
- 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 \
|
||||
&& 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"]
|
@ -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.
|
@ -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) {
|
Loading…
Reference in New Issue
Block a user