Initial attestation container implementation (#4694)

Co-authored-by: Eddy Ashton <ashton.eddy@gmail.com>
Co-authored-by: Julien Maffre <42961061+jumaffre@users.noreply.github.com>
This commit is contained in:
Takuro Sato 2023-01-13 16:39:22 +00:00 коммит произвёл GitHub
Родитель 18963ab2b7
Коммит 5ee261b755
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 1157 добавлений и 99 удалений

Просмотреть файл

@ -1 +1,2 @@
<><><>
<>??<><><><>s
<

Просмотреть файл

@ -4,20 +4,27 @@ pr:
- main
paths:
include:
- .azure_pipelines_attestation_container.yml
- .attestation_container_canary
- attestation-container/*
- .azure_pipelines_attestation_container.yml
- .azure-pipelines-templates/deploy_attestation_container.yml
- .azure-pipelines-templates/test_attestation_container.yml
trigger:
- main
pool:
vmImage: ubuntu-latest
jobs:
- template: .azure-pipelines-templates/configure.yml
steps:
- script: echo Hello, world!
displayName: "Run a one-line script"
- template: .azure-pipelines-templates/deploy_attestation_container.yml
parameters:
used_by:
- test_attestation_container
- script: |
echo Add other tasks to build, test, and deploy your project.
echo See https://aka.ms/yaml
displayName: "Run a multi-line script"
- template: .azure-pipelines-templates/test_attestation_container.yml
parameters:
job_name: test_attestation_container
display_name: "Test Attestation Container"
depends_on: deploy_attestation_container
run_on: $[ dependencies.deploy_attestation_container.outputs['deploy_attestation_container.ipAddresses'] ]
host_private_key: $[ dependencies.deploy_attestation_container.outputs['generate_ssh_key.hostPrivKey'] ]

Просмотреть файл

@ -2,11 +2,7 @@ steps:
- script: |
set -ex
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
az login --service-principal -u $(CCF_SNP_CI_APP_ID) -p $(CCF_SNP_CI_SERVICE_PRINCIPAL_PASSWORD) --tenant $(CCF_SNP_CI_TENANT)
az login --service-principal -u ${{ parameters.app_id }} -p ${{ parameters.service_principal_password }} --tenant ${{ parameters.tenant }}
pip install azure-mgmt-resource azure-identity azure-mgmt-containerinstance
name: setup_azure_cli
displayName: "Install Azure CLI, login, and install Python Packages"
env:
CCF_SNP_CI_APP_ID: $(CCF_SNP_CI_APP_ID)
CCF_SNP_CI_SERVICE_PRINCIPAL_PASSWORD: $(CCF_SNP_CI_SERVICE_PRINCIPAL_PASSWORD)
CCF_SNP_CI_TENANT: $(CCF_SNP_CI_TENANT)

Просмотреть файл

@ -20,6 +20,10 @@ jobs:
displayName: "Generate SSH Key"
- template: azure_cli.yml
parameters:
app_id: $(CCF_SNP_CI_APP_ID)
service_principal_password: $(CCF_SNP_CI_SERVICE_PRINCIPAL_PASSWORD)
tenant: $(CCF_SNP_CI_TENANT)
- script: |
set -ex
@ -44,7 +48,16 @@ jobs:
- script: |
set -ex
python3.8 scripts/azure_deployment/arm_template.py deploy aci --subscription-id $(CCF_AZURE_SUBSCRIPTION_ID) --resource-group ccf-aci --aci-type dynamic-agent --deployment-name ci-$(Build.BuildNumber) --aci-image ccfmsrc.azurecr.io/ccf/ci:pr-$(wait_for_image.gitSha) --aci-storage-account-key $(CCF_AZURE_STORAGE_KEY) > ~/aci_ips
echo "##vso[task.setvariable variable=ipAddresses;isOutput=true]`cat ~/aci_ips`"
# Escape newlines: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/set-variables-scripts?view=azure-devops&tabs=bash
escape_data() {
local data=$1
data="${data//'%'/'%AZP25'}"
data="${data//$'\n'/'%0A'}"
data="${data//$'\r'/'%0D'}"
echo "$data"
}
# Set a variable "ipAddresses" which is a list of `<container group name> <IP address>` separated by newlines.
echo "##vso[task.setvariable variable=ipAddresses;isOutput=true]$(escape_data "$(cat ~/aci_ips)")"
name: deploy_aci
displayName: "Deploy ACI"
env:
@ -62,10 +75,14 @@ jobs:
IpAddresses: $[ dependencies.deploy_aci.outputs['deploy_aci.ipAddresses'] ]
steps:
- template: azure_cli.yml
parameters:
app_id: $(CCF_SNP_CI_APP_ID)
service_principal_password: $(CCF_SNP_CI_SERVICE_PRINCIPAL_PASSWORD)
tenant: $(CCF_SNP_CI_TENANT)
- script: |
set -ex
IFS='\n' read -ra IP_ADDR_LIST <<< "$(IpAddresses)"
mapfile -t IP_ADDR_LIST <<< $(echo "$(IpAddresses)" | awk '{print $2}')
for IP_ADDR in "${IP_ADDR_LIST[@]}"; do
rm -rf /ccfci/workspace_$(Build.BuildNumber)
done

Просмотреть файл

@ -0,0 +1,95 @@
jobs:
- job: deploy_attestation_container
displayName: "Deploy Attestation Container"
variables:
Codeql.SkipTaskAutoInjection: true
skipComponentGovernanceDetection: true
pool:
vmImage: ubuntu-20.04
steps:
- script: |
set -ex
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N ""
echo "##vso[task.setvariable variable=hostPrivKey;isOutput=true;issecret=true]`base64 -w 0 ~/.ssh/id_rsa`"
name: generate_ssh_key
displayName: "Generate SSH Key"
- template: azure_cli.yml
parameters:
app_id: $(ATTESTATION_CONTAINER_CI_APP_ID)
service_principal_password: $(ATTESTATION_CONTAINER_CI_SERVICE_PRINCIPAL_PASSWORD)
tenant: $(ATTESTATION_CONTAINER_CI_TENANT)
- script: |
set -ex
cd attestation-container
docker build -t attestation-container .
az acr login --name attestationcontainerregistry
login_server=$(az acr show --name attestationcontainerregistry --query loginServer --output tsv)
image_version="ci-$(echo $(Build.BuildNumber) | sed 's/\.//g')"
docker tag attestation-container $login_server/attestation-container:$image_version
docker push $login_server/attestation-container:$image_version
echo "##vso[task.setvariable variable=ver;isoutput=true]$image_version"
name: build_and_push_attestation_container
displayName: "Build and Push Attestation Container"
- script: |
set -ex
python3.8 scripts/azure_deployment/arm_template.py deploy aci --subscription-id $(CCF_AZURE_SUBSCRIPTION_ID) --resource-group attestationContainer --aci-type dynamic-agent --deployment-name ci-$(Build.BuildNumber) --attestation-container-e2e True > ~/aci_ips
# Escape newlines: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/set-variables-scripts?view=azure-devops&tabs=bash
escape_data() {
local data=$1
data="${data//'%'/'%AZP25'}"
data="${data//$'\n'/'%0A'}"
data="${data//$'\r'/'%0D'}"
echo "$data"
}
# Set a variable "ipAddresses" which is a list of `<container group name> <IP address>` separated by newlines.
echo "##vso[task.setvariable variable=ipAddresses;isOutput=true]$(escape_data "$(cat ~/aci_ips)")"
name: deploy_attestation_container
displayName: "Deploy Attestation Container"
env:
CCF_AZURE_SUBSCRIPTION_ID: $(CCF_AZURE_SUBSCRIPTION_ID)
- job: cleanup_aci
displayName: "Cleanup Attestation Container"
pool:
vmImage: ubuntu-20.04
dependsOn: ${{ parameters.used_by }}
condition: always()
steps:
- template: azure_cli.yml
parameters:
app_id: $(ATTESTATION_CONTAINER_CI_APP_ID)
service_principal_password: $(ATTESTATION_CONTAINER_CI_SERVICE_PRINCIPAL_PASSWORD)
tenant: $(ATTESTATION_CONTAINER_CI_TENANT)
- script: |
set -ex
python3.8 scripts/azure_deployment/arm_template.py remove aci --subscription-id $(CCF_AZURE_SUBSCRIPTION_ID) --resource-group attestationContainer --aci-type dynamic-agent --deployment-name ci-$(Build.BuildNumber)
name: cleanup_aci
displayName: "Delete the ACI and Azure Deployment"
- job: cleanup_container_image
displayName: "Cleanup Attestation Container image"
pool:
vmImage: ubuntu-20.04
dependsOn:
- deploy_attestation_container
- ${{ parameters.used_by }}
variables:
image_version: $[ dependencies.deploy_attestation_container.outputs['build_and_push_attestation_container.ver'] ]
condition: always()
steps:
- template: azure_cli.yml
parameters:
app_id: $(ATTESTATION_CONTAINER_CI_APP_ID)
service_principal_password: $(ATTESTATION_CONTAINER_CI_SERVICE_PRINCIPAL_PASSWORD)
tenant: $(ATTESTATION_CONTAINER_CI_TENANT)
- script: |
set -ex
az acr repository delete --name attestationcontainerregistry --image attestation-container:$image_version --yes
name: cleanup_container_image
displayName: "Delete the container image"

Просмотреть файл

@ -0,0 +1,67 @@
parameters:
depends_on: ""
condition: ""
jobs:
- job: ${{ parameters.job_name }}
displayName: ${{ parameters.display_name }}
dependsOn: ${{ parameters.depends_on }}
condition: ${{ parameters.condition }}
pool:
vmImage: ubuntu-20.04
timeoutInMinutes: 120
variables:
RUN_ON: ${{ parameters.run_on }}
HOST_PRIVATE_KEY: ${{ parameters.host_private_key }}
Codeql.SkipTaskAutoInjection: true
skipComponentGovernanceDetection: true
steps:
- script: |
set -ex
mkdir ~/.ssh
echo $(HOST_PRIVATE_KEY) | base64 -d > ~/.ssh/id_rsa
sudo chmod 600 ~/.ssh/id_rsa
name: setup_key
displayName: "Install SSH Key from Deployment Step"
- script: |
set -ex
# The following sed is to change 20221213.48 to 2022121348 for example
echo "##vso[task.setvariable variable=BUILD_NUMBER]$(echo $(Build.BuildNumber) | sed 's/\.//g')"
name: set_build_number_var
displayName: "Set BUILD_NUMBER variable"
- script: |
set -ex
echo "##vso[task.setvariable variable=IP_ADDR]$(echo "$(RUN_ON)" | awk -v container_group="ci-$(BUILD_NUMBER)-business-logic" '$1 == container_group {print $2}')"
name: set_ip_addr_var
displayName: "Set IP_ADDR variable"
- script: |
set -ex
echo -e "Testing connection with $IP_ADDR"
ssh agent@$IP_ADDR -o "StrictHostKeyChecking no" 'echo "Connected successfully for port 22" && cd /usr/src/app/attest && /usr/local/go/bin/go test'
name: run_unit_test
displayName: "Unit Test in Attestation Container Instance Deployed ACIs"
- task: GoTool@0
inputs:
version: "1.19.3"
- template: azure_cli.yml
parameters:
app_id: $(ATTESTATION_CONTAINER_CI_APP_ID)
service_principal_password: $(ATTESTATION_CONTAINER_CI_SERVICE_PRINCIPAL_PASSWORD)
tenant: $(ATTESTATION_CONTAINER_CI_TENANT)
- script: |
set -ex
az account set --subscription $(CCF_AZURE_SUBSCRIPTION_ID)
echo "Testing attestation container..."
cd attestation-container
echo "testing against ${IP_ADDR} ..."
go test . --addr $IP_ADDR:50051 -v
az container logs --resource-group attestationContainer --name ci-$(BUILD_NUMBER)-business-logic --container-name ci-$(BUILD_NUMBER)-attestation-container
name: test_attestation_container
displayName: "Test attestation container"

Просмотреть файл

@ -27,7 +27,7 @@ jobs:
- script: |
set -ex
IFS='\n' read -ra IP_ADDR_LIST <<< "$(RUN_ON)"
mapfile -t IP_ADDR_LIST <<< $(echo "$(RUN_ON)" | awk '{print $2}')
ssh agent@${IP_ADDR_LIST[0]} -o "StrictHostKeyChecking no" '
cd /CCF/build
npm config set cache /ccfci/workspace_$(Build.BuildNumber)/.npm
@ -38,7 +38,7 @@ jobs:
- script: |
set -ex
IFS='\n' read -ra IP_ADDR_LIST <<< "$(RUN_ON)"
mapfile -t IP_ADDR_LIST <<< $(echo "$(RUN_ON)" | awk '{print $2}')
ssh agent@${IP_ADDR_LIST[0]} -o "StrictHostKeyChecking no" '
dmesg
'

5
.github/dependabot.yml поставляемый
Просмотреть файл

@ -14,3 +14,8 @@ updates:
directory: "/python" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "gomod"
directory: "/attestation-container" # Location of package manifests
schedule:
interval: "weekly"

1
attestation-container/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
attestation-container

Просмотреть файл

@ -0,0 +1,18 @@
# Based on https://hub.docker.com/_/golang/
# To run this:
# docker build -t attestation-container .
# docker run -it --rm -p 50051:50051 attestation-container
FROM golang:1.19
WORKDIR /usr/src/app
# pre-copy/cache go.mod for pre-downloading dependencies and only redownloading them in subsequent builds if they change
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY . .
RUN go build -v -o /usr/local/bin/app .
CMD ["app"]

Просмотреть файл

@ -0,0 +1,48 @@
# Attestation Container
This is a gRPC server application to fetch SEV-SNP attestation and collateral.
## Environment
This application needs to run on [SEV-SNP VM](https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf).
## Dependencies
- [Go](https://go.dev/doc/install)
- [gRPC](https://grpc.io/docs/languages/go/quickstart/)
## How to start the app
The following command starts the gRPC server application.
```bash
# In the same directory as this README.md
go run .
```
## Test
Unit test:
```bash
cd attest
go test
```
E2E test:
```bash
# Run the app first
go run .
# In another terminal
go test
```
## Update protobuf
When you edit `.proto` file, you also need to update `.pb.go` files by:
```bash
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative protobuf/attestation-container.proto
```

Просмотреть файл

@ -0,0 +1,148 @@
package attest
import (
"bytes"
"encoding/binary"
"fmt"
"unsafe"
"golang.org/x/sys/unix"
)
// Data structures are based on SEV-SNP Firmware ABI Specification
// https://www.amd.com/en/support/tech-docs/sev-secure-nested-paging-firmware-abi-specification
const (
ATTESTATION_REPORT_SIZE = 1184 // Size of ATTESTATION_REPORT (Table 21)
REPORT_DATA_SIZE = 64 // Size of REPORT_DATA_SIZE in ATTESTATION_REPORT
REPORT_REQ_SIZE = 96 // Size of MSG_REPORT_REQ (Table 20)
REPORT_RSP_SIZE = 1280 // Size of MSG_REPORT_RSP (Table 23)
PAYLOAD_SIZE = 40 // Size of sev_snp_guest_request struct from sev-snp driver include/uapi/linux/psp-sev-guest.h
)
// Message Type Encodings (Table 100)
const (
MSG_REPORT_REQ = 5
MSG_REPORT_RSP = 6
)
// From sev-snp driver include/uapi/linux/psp-sev-guest.h
const SEV_SNP_GUEST_MSG_REPORT = 3223868161
const SNP_DEVICE_PATH = "/dev/sev"
/*
Creates and returns MSG_REPORT_REQ message bytes (SEV-SNP Firmware ABI Specification Table 20)
*/
func createReportReqBytes(reportData [REPORT_DATA_SIZE]byte) [REPORT_REQ_SIZE]byte {
reportReqBytes := [REPORT_REQ_SIZE]byte{}
copy(reportReqBytes[0:REPORT_DATA_SIZE], reportData[:])
return reportReqBytes
}
/*
Creates and returns byte array of the following C struct
// From sev-snp driver include/uapi/linux/psp-sev-guest.h
// struct sev_snp_guest_request {
// uint8_t req_msg_type;
// uint8_t rsp_msg_type;
// uint8_t msg_version;
// uint16_t request_len;
// uint64_t request_uaddr;
// uint16_t response_len;
// uint64_t response_uaddr;
// uint32_t error; // firmware error code on failure (see psp-sev.h)
// };
The padding is based on Section 3.1.2 of System V ABI for AMD64
https://www.uclibc.org/docs/psABI-x86_64.pdf
*/
func createPayloadBytes(reportReqPtr uintptr, ReportRespPtr uintptr) ([PAYLOAD_SIZE]byte, error) {
payload := [PAYLOAD_SIZE]byte{}
var buf bytes.Buffer
// req_msg_type
if err := binary.Write(&buf, binary.LittleEndian, uint8(MSG_REPORT_REQ)); err != nil {
return payload, err
}
// rsp_msg_type
if err := binary.Write(&buf, binary.LittleEndian, uint8(MSG_REPORT_RSP)); err != nil {
return payload, err
}
// msg_version
if err := binary.Write(&buf, binary.LittleEndian, uint8(1)); err != nil {
return payload, err
}
// Padding
if err := binary.Write(&buf, binary.LittleEndian, uint8(0)); err != nil {
return payload, err
}
// request_len
if err := binary.Write(&buf, binary.LittleEndian, uint16(REPORT_REQ_SIZE)); err != nil {
return payload, err
}
// Padding
if err := binary.Write(&buf, binary.LittleEndian, uint16(0)); err != nil {
return payload, err
}
// request_uaddr
if err := binary.Write(&buf, binary.LittleEndian, uint64(reportReqPtr)); err != nil {
return payload, err
}
// response_len
if err := binary.Write(&buf, binary.LittleEndian, uint16(REPORT_RSP_SIZE)); err != nil {
return payload, err
}
// Padding
if err := binary.Write(&buf, binary.LittleEndian, [3]uint16{}); err != nil {
return payload, err
}
// response_uaddr
if err := binary.Write(&buf, binary.LittleEndian, uint64(ReportRespPtr)); err != nil {
return payload, err
}
// error
if err := binary.Write(&buf, binary.LittleEndian, uint32(0)); err != nil {
return payload, err
}
// Padding
if err := binary.Write(&buf, binary.LittleEndian, uint32(0)); err != nil {
return payload, err
}
for i, x := range buf.Bytes() {
payload[i] = x
}
return payload, nil
}
func FetchAttestationReportByte(reportData [64]byte) ([]byte, error) {
fd, err := unix.Open(SNP_DEVICE_PATH, unix.O_RDWR|unix.O_CLOEXEC, 0)
if err != nil {
return nil, err
}
reportReqBytes := createReportReqBytes(reportData)
// MSG_REPORT_RSP message bytes (SEV-SNP Firmware Firmware ABI Specification Table 23)
reportRspBytes := [REPORT_RSP_SIZE]byte{}
payload, err := createPayloadBytes(uintptr(unsafe.Pointer(&reportReqBytes[0])), uintptr(unsafe.Pointer(&reportRspBytes[0])))
if err != nil {
return nil, err
}
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(fd),
uintptr(SEV_SNP_GUEST_MSG_REPORT),
uintptr(unsafe.Pointer(&payload[0])),
)
if errno != 0 {
return nil, fmt.Errorf("ioctl failed:%v", errno)
}
if status := binary.LittleEndian.Uint32(reportRspBytes[0:4]); status != 0 {
return nil, fmt.Errorf("fetching attestation report failed. status: %v", status)
}
const SNP_REPORT_OFFSET = 32
return reportRspBytes[SNP_REPORT_OFFSET : SNP_REPORT_OFFSET+ATTESTATION_REPORT_SIZE], nil
}

Просмотреть файл

@ -0,0 +1,31 @@
package attest
import (
"encoding/hex"
"testing"
)
func assertEqual[T comparable](t *testing.T, description string, expect T, actual T) {
if expect != actual {
t.Fatalf("%s: Expected %v, but got %v", description, expect, actual)
}
}
func TestFetchReport(t *testing.T) {
// Report data for test
reportData := [REPORT_DATA_SIZE]byte{}
for i := 0; i < REPORT_DATA_SIZE; i++ {
reportData[i] = byte(i)
}
reportBytes, err := FetchAttestationReportByte(reportData)
if err != nil {
t.Fatalf("Fetching report failed: %v", err)
}
expectedByteString := hex.EncodeToString(reportData[:])
// Confirm `report data` (user provided 64 byte data) is correct
// Offset of `report data` is specified in SEV-SNP Firmware ABI Specification Table 21
// https://www.amd.com/en/support/tech-docs/sev-secure-nested-paging-firmware-abi-specification
const REPORT_DATA_OFFSET = 80
assertEqual(t, "Check report data", expectedByteString, hex.EncodeToString(reportBytes[REPORT_DATA_OFFSET:REPORT_DATA_OFFSET+REPORT_DATA_SIZE]))
}

Просмотреть файл

@ -0,0 +1,61 @@
package main
import (
"context"
"errors"
"flag"
"fmt"
"log"
"net"
"os"
"microsoft/attestation-container/attest"
pb "microsoft/attestation-container/protobuf"
"google.golang.org/grpc"
)
var (
port = flag.Int("port", 50051, "The server port")
)
type server struct {
pb.UnimplementedAttestationContainerServer
}
func (s *server) FetchAttestation(ctx context.Context, in *pb.FetchAttestationRequest) (*pb.FetchAttestationReply, error) {
reportData := [attest.REPORT_DATA_SIZE]byte{}
if len(in.GetReportData()) > attest.REPORT_DATA_SIZE {
return nil, fmt.Errorf("`report_data` needs to be smaller than %d bytes. size: %d bytes", attest.REPORT_DATA_SIZE, len(in.GetReportData()))
}
copy(reportData[:], in.GetReportData())
reportBytes, err := attest.FetchAttestationReportByte(reportData)
if err != nil {
return nil, fmt.Errorf("failed to fetch attestation report: %s", err)
}
return &pb.FetchAttestationReply{Attestation: reportBytes}, nil
}
func main() {
fmt.Println("Attestation container started.")
if _, err := os.Stat(attest.SNP_DEVICE_PATH); err == nil {
fmt.Printf("%s is detected", attest.SNP_DEVICE_PATH)
} else if errors.Is(err, os.ErrNotExist) {
log.Fatalf("%s is not detected", attest.SNP_DEVICE_PATH)
} else {
log.Fatalf("Unknown error: %s", err)
}
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterAttestationContainerServer(s, &server{})
log.Printf("Server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

Просмотреть файл

@ -0,0 +1,60 @@
package main
import (
"context"
"encoding/hex"
"flag"
"log"
"testing"
"time"
pb "microsoft/attestation-container/protobuf"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
var (
addr = flag.String("addr", "localhost:50051", "the address to connect to")
)
func TestFetchReport(t *testing.T) {
flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewAttestationContainerClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// public key bytes in UTF-8 (https://go.dev/blog/strings)
publicKey := []byte("public-key-contents")
r, err := c.FetchAttestation(ctx, &pb.FetchAttestationRequest{ReportData: publicKey})
if err != nil {
log.Fatalf("could not get attestation: %v", err)
}
log.Printf("Attestation: %v", hex.EncodeToString(r.GetAttestation()))
}
func TestInputError(t *testing.T) {
flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewAttestationContainerClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
publicKey := []byte("too long (longer than 64 bytes in utf-8) ------------------------")
if _, err := c.FetchAttestation(ctx, &pb.FetchAttestationRequest{ReportData: publicKey}); err == nil {
log.Fatalf("server should return input error for too large input")
}
}

Просмотреть файл

@ -0,0 +1,16 @@
module microsoft/attestation-container
go 1.19
require (
golang.org/x/sys v0.3.0
google.golang.org/grpc v1.51.0
google.golang.org/protobuf v1.28.1
)
require (
github.com/golang/protobuf v1.5.2 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/text v0.5.0 // indirect
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd // indirect
)

Просмотреть файл

@ -0,0 +1,21 @@
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=

Просмотреть файл

@ -0,0 +1,222 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.12.4
// source: protobuf/attestation-container.proto
package protobuf
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type FetchAttestationRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ReportData []byte `protobuf:"bytes,1,opt,name=report_data,json=reportData,proto3" json:"report_data,omitempty"`
}
func (x *FetchAttestationRequest) Reset() {
*x = FetchAttestationRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_protobuf_attestation_container_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *FetchAttestationRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*FetchAttestationRequest) ProtoMessage() {}
func (x *FetchAttestationRequest) ProtoReflect() protoreflect.Message {
mi := &file_protobuf_attestation_container_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use FetchAttestationRequest.ProtoReflect.Descriptor instead.
func (*FetchAttestationRequest) Descriptor() ([]byte, []int) {
return file_protobuf_attestation_container_proto_rawDescGZIP(), []int{0}
}
func (x *FetchAttestationRequest) GetReportData() []byte {
if x != nil {
return x.ReportData
}
return nil
}
type FetchAttestationReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Attestation []byte `protobuf:"bytes,1,opt,name=attestation,proto3" json:"attestation,omitempty"`
}
func (x *FetchAttestationReply) Reset() {
*x = FetchAttestationReply{}
if protoimpl.UnsafeEnabled {
mi := &file_protobuf_attestation_container_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *FetchAttestationReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*FetchAttestationReply) ProtoMessage() {}
func (x *FetchAttestationReply) ProtoReflect() protoreflect.Message {
mi := &file_protobuf_attestation_container_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use FetchAttestationReply.ProtoReflect.Descriptor instead.
func (*FetchAttestationReply) Descriptor() ([]byte, []int) {
return file_protobuf_attestation_container_proto_rawDescGZIP(), []int{1}
}
func (x *FetchAttestationReply) GetAttestation() []byte {
if x != nil {
return x.Attestation
}
return nil
}
var File_protobuf_attestation_container_proto protoreflect.FileDescriptor
var file_protobuf_attestation_container_proto_rawDesc = []byte{
0x0a, 0x24, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73,
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x3a, 0x0a,
0x17, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6f,
0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72,
0x65, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x22, 0x39, 0x0a, 0x15, 0x46, 0x65, 0x74,
0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70,
0x6c, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x32, 0x8a, 0x01, 0x0a, 0x14, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x72, 0x0a,
0x10, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x2e, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41,
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x2c, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41,
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22,
0x00, 0x42, 0x2a, 0x5a, 0x28, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2f, 0x61,
0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x61,
0x69, 0x6e, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_protobuf_attestation_container_proto_rawDescOnce sync.Once
file_protobuf_attestation_container_proto_rawDescData = file_protobuf_attestation_container_proto_rawDesc
)
func file_protobuf_attestation_container_proto_rawDescGZIP() []byte {
file_protobuf_attestation_container_proto_rawDescOnce.Do(func() {
file_protobuf_attestation_container_proto_rawDescData = protoimpl.X.CompressGZIP(file_protobuf_attestation_container_proto_rawDescData)
})
return file_protobuf_attestation_container_proto_rawDescData
}
var file_protobuf_attestation_container_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_protobuf_attestation_container_proto_goTypes = []interface{}{
(*FetchAttestationRequest)(nil), // 0: attestation_container.FetchAttestationRequest
(*FetchAttestationReply)(nil), // 1: attestation_container.FetchAttestationReply
}
var file_protobuf_attestation_container_proto_depIdxs = []int32{
0, // 0: attestation_container.AttestationContainer.FetchAttestation:input_type -> attestation_container.FetchAttestationRequest
1, // 1: attestation_container.AttestationContainer.FetchAttestation:output_type -> attestation_container.FetchAttestationReply
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_protobuf_attestation_container_proto_init() }
func file_protobuf_attestation_container_proto_init() {
if File_protobuf_attestation_container_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_protobuf_attestation_container_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*FetchAttestationRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_protobuf_attestation_container_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*FetchAttestationReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_protobuf_attestation_container_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_protobuf_attestation_container_proto_goTypes,
DependencyIndexes: file_protobuf_attestation_container_proto_depIdxs,
MessageInfos: file_protobuf_attestation_container_proto_msgTypes,
}.Build()
File_protobuf_attestation_container_proto = out.File
file_protobuf_attestation_container_proto_rawDesc = nil
file_protobuf_attestation_container_proto_goTypes = nil
file_protobuf_attestation_container_proto_depIdxs = nil
}

Просмотреть файл

@ -0,0 +1,20 @@
syntax = "proto3";
option go_package = "microsoft/attestation-container/protobuf";
package attestation_container;
// attestation_container service definition.
service AttestationContainer {
// Fetchs and returns attestation report and collateral.
// In future it returns Certificate Revocation List (CRL) as well.
rpc FetchAttestation (FetchAttestationRequest) returns (FetchAttestationReply) {}
}
message FetchAttestationRequest {
bytes report_data = 1;
}
message FetchAttestationReply {
bytes attestation = 1;
}

Просмотреть файл

@ -0,0 +1,109 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.12.4
// source: protobuf/attestation-container.proto
package protobuf
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// AttestationContainerClient is the client API for AttestationContainer service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type AttestationContainerClient interface {
// Fetchs and returns attestation report and collateral.
// In future it returns Certificate Revocation List (CRL) as well.
FetchAttestation(ctx context.Context, in *FetchAttestationRequest, opts ...grpc.CallOption) (*FetchAttestationReply, error)
}
type attestationContainerClient struct {
cc grpc.ClientConnInterface
}
func NewAttestationContainerClient(cc grpc.ClientConnInterface) AttestationContainerClient {
return &attestationContainerClient{cc}
}
func (c *attestationContainerClient) FetchAttestation(ctx context.Context, in *FetchAttestationRequest, opts ...grpc.CallOption) (*FetchAttestationReply, error) {
out := new(FetchAttestationReply)
err := c.cc.Invoke(ctx, "/attestation_container.AttestationContainer/FetchAttestation", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// AttestationContainerServer is the server API for AttestationContainer service.
// All implementations must embed UnimplementedAttestationContainerServer
// for forward compatibility
type AttestationContainerServer interface {
// Fetchs and returns attestation report and collateral.
// In future it returns Certificate Revocation List (CRL) as well.
FetchAttestation(context.Context, *FetchAttestationRequest) (*FetchAttestationReply, error)
mustEmbedUnimplementedAttestationContainerServer()
}
// UnimplementedAttestationContainerServer must be embedded to have forward compatible implementations.
type UnimplementedAttestationContainerServer struct {
}
func (UnimplementedAttestationContainerServer) FetchAttestation(context.Context, *FetchAttestationRequest) (*FetchAttestationReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method FetchAttestation not implemented")
}
func (UnimplementedAttestationContainerServer) mustEmbedUnimplementedAttestationContainerServer() {}
// UnsafeAttestationContainerServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to AttestationContainerServer will
// result in compilation errors.
type UnsafeAttestationContainerServer interface {
mustEmbedUnimplementedAttestationContainerServer()
}
func RegisterAttestationContainerServer(s grpc.ServiceRegistrar, srv AttestationContainerServer) {
s.RegisterService(&AttestationContainer_ServiceDesc, srv)
}
func _AttestationContainer_FetchAttestation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FetchAttestationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AttestationContainerServer).FetchAttestation(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/attestation_container.AttestationContainer/FetchAttestation",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AttestationContainerServer).FetchAttestation(ctx, req.(*FetchAttestationRequest))
}
return interceptor(ctx, in, info, handler)
}
// AttestationContainer_ServiceDesc is the grpc.ServiceDesc for AttestationContainer service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var AttestationContainer_ServiceDesc = grpc.ServiceDesc{
ServiceName: "attestation_container.AttestationContainer",
HandlerType: (*AttestationContainerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "FetchAttestation",
Handler: _AttestationContainer_FetchAttestation_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "protobuf/attestation-container.proto",
}

Просмотреть файл

@ -11,6 +11,7 @@ from azure.mgmt.resource.resources.models import (
Deployment,
DeploymentProperties,
DeploymentMode,
DeploymentPropertiesExtended,
)
from azure.mgmt.containerinstance import ContainerInstanceManagementClient
@ -90,6 +91,13 @@ def make_aci_deployment(parser: ArgumentParser) -> Deployment:
type=lambda comma_sep_str: comma_sep_str.split(","),
)
parser.add_argument(
"--attestation-container-e2e",
help="Deploy attestation container for its E2E test if this flag is true. Default=False",
default=False,
type=bool,
)
parser.add_argument(
"--aci-storage-account-key",
help="The storage account key used to authorise access to the file share",
@ -98,87 +106,144 @@ def make_aci_deployment(parser: ArgumentParser) -> Deployment:
args = parser.parse_args()
return Deployment(
properties=DeploymentProperties(
mode=DeploymentMode.INCREMENTAL,
parameters={},
template={
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.ContainerInstance/containerGroups",
"apiVersion": "2022-04-01-preview",
"name": f"{args.deployment_name}-{i}",
"location": "eastus2euap",
"properties": {
"sku": "Standard",
"confidentialComputeProperties": {
"isolationType": "SevSnp",
"ccePolicy": "cGFja2FnZSBwb2xpY3kKCmFwaV9zdm4gOj0gIjAuMTAuMCIKCm1vdW50X2RldmljZSA6PSB7ImFsbG93ZWQiOiB0cnVlfQptb3VudF9vdmVybGF5IDo9IHsiYWxsb3dlZCI6IHRydWV9CmNyZWF0ZV9jb250YWluZXIgOj0geyJhbGxvd2VkIjogdHJ1ZSwgImFsbG93X3N0ZGlvX2FjY2VzcyI6IHRydWV9CnVubW91bnRfZGV2aWNlIDo9IHsiYWxsb3dlZCI6IHRydWV9CnVubW91bnRfb3ZlcmxheSA6PSB7ImFsbG93ZWQiOiB0cnVlfQpleGVjX2luX2NvbnRhaW5lciA6PSB7ImFsbG93ZWQiOiB0cnVlfQpleGVjX2V4dGVybmFsIDo9IHsiYWxsb3dlZCI6IHRydWUsICJhbGxvd19zdGRpb19hY2Nlc3MiOiB0cnVlfQpzaHV0ZG93bl9jb250YWluZXIgOj0geyJhbGxvd2VkIjogdHJ1ZX0Kc2lnbmFsX2NvbnRhaW5lcl9wcm9jZXNzIDo9IHsiYWxsb3dlZCI6IHRydWV9CnBsYW45X21vdW50IDo9IHsiYWxsb3dlZCI6IHRydWV9CnBsYW45X3VubW91bnQgOj0geyJhbGxvd2VkIjogdHJ1ZX0KZ2V0X3Byb3BlcnRpZXMgOj0geyJhbGxvd2VkIjogdHJ1ZX0KZHVtcF9zdGFja3MgOj0geyJhbGxvd2VkIjogdHJ1ZX0KcnVudGltZV9sb2dnaW5nIDo9IHsiYWxsb3dlZCI6IHRydWV9CmxvYWRfZnJhZ21lbnQgOj0geyJhbGxvd2VkIjogdHJ1ZX0Kc2NyYXRjaF9tb3VudCA6PSB7ImFsbG93ZWQiOiB0cnVlfQpzY3JhdGNoX3VubW91bnQgOj0geyJhbGxvd2VkIjogdHJ1ZX0K",
},
"containers": [
{
"name": f"{args.deployment_name}-{i}",
"properties": {
"image": args.aci_image,
"command": [
"/bin/sh",
"-c",
" && ".join(
[
*STARTUP_COMMANDS[args.aci_type](
args,
i,
),
"tail -f /dev/null",
]
common_resource_attributes = {
"type": "Microsoft.ContainerInstance/containerGroups",
"apiVersion": "2022-04-01-preview",
"location": "eastus2euap",
}
common_resource_properties = {
"sku": "Standard",
"confidentialComputeProperties": {
"isolationType": "SevSnp",
"ccePolicy": "cGFja2FnZSBwb2xpY3kKCmFwaV9zdm4gOj0gIjAuMTAuMCIKCm1vdW50X2RldmljZSA6PSB7ImFsbG93ZWQiOiB0cnVlfQptb3VudF9vdmVybGF5IDo9IHsiYWxsb3dlZCI6IHRydWV9CmNyZWF0ZV9jb250YWluZXIgOj0geyJhbGxvd2VkIjogdHJ1ZSwgImFsbG93X3N0ZGlvX2FjY2VzcyI6IHRydWV9CnVubW91bnRfZGV2aWNlIDo9IHsiYWxsb3dlZCI6IHRydWV9CnVubW91bnRfb3ZlcmxheSA6PSB7ImFsbG93ZWQiOiB0cnVlfQpleGVjX2luX2NvbnRhaW5lciA6PSB7ImFsbG93ZWQiOiB0cnVlfQpleGVjX2V4dGVybmFsIDo9IHsiYWxsb3dlZCI6IHRydWUsICJhbGxvd19zdGRpb19hY2Nlc3MiOiB0cnVlfQpzaHV0ZG93bl9jb250YWluZXIgOj0geyJhbGxvd2VkIjogdHJ1ZX0Kc2lnbmFsX2NvbnRhaW5lcl9wcm9jZXNzIDo9IHsiYWxsb3dlZCI6IHRydWV9CnBsYW45X21vdW50IDo9IHsiYWxsb3dlZCI6IHRydWV9CnBsYW45X3VubW91bnQgOj0geyJhbGxvd2VkIjogdHJ1ZX0KZ2V0X3Byb3BlcnRpZXMgOj0geyJhbGxvd2VkIjogdHJ1ZX0KZHVtcF9zdGFja3MgOj0geyJhbGxvd2VkIjogdHJ1ZX0KcnVudGltZV9sb2dnaW5nIDo9IHsiYWxsb3dlZCI6IHRydWV9CmxvYWRfZnJhZ21lbnQgOj0geyJhbGxvd2VkIjogdHJ1ZX0Kc2NyYXRjaF9tb3VudCA6PSB7ImFsbG93ZWQiOiB0cnVlfQpzY3JhdGNoX3VubW91bnQgOj0geyJhbGxvd2VkIjogdHJ1ZX0K",
},
"initContainers": [],
"restartPolicy": "Never",
"osType": "Linux",
}
# Mount external volume only when account key is provided
volume_mounts = []
if args.aci_storage_account_key is not None:
common_resource_properties["volumes"] = [
{
"name": "ccfcivolume",
"azureFile": {
"shareName": "ccfcishare",
"storageAccountName": "ccfcistorage",
"storageAccountKey": args.aci_storage_account_key,
},
}
]
volume_mounts = [
{
"name": "ccfcivolume",
"mountPath": "/ccfci",
}
]
template = {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
**common_resource_attributes,
"name": f"{args.deployment_name}-{i}",
"properties": {
**common_resource_properties,
"containers": [
{
"name": f"{args.deployment_name}-{i}",
"properties": {
"image": args.aci_image,
"command": [
"/bin/sh",
"-c",
" && ".join(
[
*STARTUP_COMMANDS[args.aci_type](
args,
i,
),
],
"ports": [
{"protocol": "TCP", "port": 8000},
{"protocol": "TCP", "port": 22},
],
"environmentVariables": [],
"resources": {
"requests": {"memoryInGB": 16, "cpu": 4}
},
"volumeMounts": [
{
"name": "ccfcivolume",
"mountPath": "/ccfci",
}
],
},
}
],
"initContainers": [],
"restartPolicy": "Never",
"ipAddress": {
"tail -f /dev/null",
]
),
],
"ports": [
{"protocol": "TCP", "port": 8000},
{"protocol": "TCP", "port": 22},
],
"type": "Public",
"environmentVariables": [],
"resources": {"requests": {"memoryInGB": 16, "cpu": 4}},
"volumeMounts": volume_mounts,
},
"osType": "Linux",
"volumes": [
{
"name": "ccfcivolume",
"azureFile": {
"shareName": "ccfcishare",
"storageAccountName": "ccfcistorage",
"storageAccountKey": args.aci_storage_account_key,
},
}
],
},
}
for i in range(args.count)
],
},
}
],
"ipAddress": {
"ports": [
{"protocol": "TCP", "port": 8000},
{"protocol": "TCP", "port": 22},
],
"type": "Public",
},
},
}
for i in range(args.count)
],
}
if args.attestation_container_e2e:
# NOTE: It currently exposes the attestation-container's port (50051) to outside of the container group for e2e testing purpose,
# but it shouldn't be done in actual CCF application.
template["resources"].append(
{
**common_resource_attributes,
"name": f"{args.deployment_name}-business-logic",
"properties": {
**common_resource_properties,
"containers": [
{
"name": f"{args.deployment_name}-attestation-container",
"properties": {
"image": f"attestationcontainerregistry.azurecr.io/attestation-container:{args.deployment_name}",
"command": [
"/bin/sh",
"-c",
" && ".join(
[
*STARTUP_COMMANDS[args.aci_type](
args,
None,
),
"app",
]
),
],
"ports": [
{"protocol": "TCP", "port": 22},
{"protocol": "TCP", "port": 50051},
],
"environmentVariables": [],
"resources": {"requests": {"memoryInGB": 16, "cpu": 4}},
},
}
],
"ipAddress": {
"ports": [
{"protocol": "TCP", "port": 22},
{"protocol": "TCP", "port": 50051},
],
"type": "Public",
},
},
}
)
return Deployment(
properties=DeploymentProperties(
mode=DeploymentMode.INCREMENTAL, parameters={}, template=template
)
)
@ -196,16 +261,26 @@ def remove_aci_deployment(args: Namespace, deployment: Deployment):
).wait()
def check_aci_deployment(args: Namespace, deployment: Deployment) -> str:
def check_aci_deployment(
args: Namespace, deployment: DeploymentPropertiesExtended
) -> str:
"""
Outputs the list of container group deployed to stdout.
The format of each line is `<container group name> <IP address>`.
example output:
container_group_a 10.10.10.10
container_group_b 10.10.10.11
"""
container_client = ContainerInstanceManagementClient(
DefaultAzureCredential(), args.subscription_id
)
for resource in deployment.properties.output_resources:
container_name = resource.id.split("/")[-1]
container_group_name = resource.id.split("/")[-1]
container_group = container_client.container_groups.get(
args.resource_group, container_name
args.resource_group, container_group_name
)
# Check that container commands have been completed
@ -228,7 +303,7 @@ def check_aci_deployment(args: Namespace, deployment: Deployment) -> str:
)
== b"test\n"
)
print(container_group.ip_address.ip)
print(container_group_name, container_group.ip_address.ip)
break
except Exception:
time.sleep(5)

Просмотреть файл

@ -125,3 +125,43 @@ endgroup
group "Python types"
git ls-files python/ | grep -e '\.py$' | xargs mypy
endgroup
group "Go dependencies"
GO_VERSION="1.19"
if command -v go &> /dev/null
then
# go is found
if ! go version | grep go$GO_VERSION &> /dev/null
then
echo "Wrong version of go is installed. Please make sure version $GO_VERSION.x is installed."
echo -n "Current install version: "
go version
exit 1
fi
else
# go is not found
# Install the latest bugfix version of GO_VERSION
# https://github.com/golang/go/issues/36898
install_version=$(curl -sL 'https://go.dev/dl/?mode=json&include=all' | jq -r '.[].version' | grep -m 1 go$GO_VERSION)
tar_filename=$install_version.linux-amd64.tar.gz
curl -sLO "https://go.dev/dl/$tar_filename"
function clean_up_tar {
rm "$tar_filename"
}
trap clean_up_tar EXIT
tar -C /usr/local -xzf "$tar_filename"
# shellcheck disable=SC2016,SC1090
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc && source ~/.bashrc
fi
group "Go format"
if [ $FIX -ne 0 ]; then
git ls-files attestation-container/ | grep -e '\.go$' | xargs gofmt -w
else
GOFMT_RES=$(git ls-files attestation-container/ | grep -e '\.go$' | xargs gofmt -d)
if [ "$GOFMT_RES" != "" ];
then
echo "Format of go codes is broken"
echo "$GOFMT_RES"
fi
fi