mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
Add measurement reader (#1381)
Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
parent
5bad5f768b
commit
8c87bba755
@ -16,7 +16,7 @@ set(CLI_BUILD_TAGS "" CACHE STRING "Tags passed to go build of Constellation CLI
|
||||
enable_testing()
|
||||
|
||||
#
|
||||
# core-os disk-mapper
|
||||
# disk-mapper
|
||||
#
|
||||
add_custom_target(disk-mapper ALL
|
||||
DOCKER_BUILDKIT=1 docker build -o ${CMAKE_BINARY_DIR} --build-arg PROJECT_VERSION="${PROJECT_VERSION}" -f Dockerfile.build --target disk-mapper .
|
||||
@ -24,6 +24,15 @@ add_custom_target(disk-mapper ALL
|
||||
BYPRODUCTS disk-mapper
|
||||
)
|
||||
|
||||
#
|
||||
# measurement-reader
|
||||
#
|
||||
add_custom_target(measurement-reader ALL
|
||||
DOCKER_BUILDKIT=1 docker build -o ${CMAKE_BINARY_DIR} --build-arg PROJECT_VERSION="${PROJECT_VERSION}" -f Dockerfile.build --target measurement-reader .
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
BYPRODUCTS measurement-reader
|
||||
)
|
||||
|
||||
#
|
||||
# bootstrapper
|
||||
#
|
||||
|
@ -42,6 +42,11 @@ WORKDIR /constellation/upgrade-agent/
|
||||
ARG PROJECT_VERSION
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build go build -o upgrade-agent -ldflags "-s -w -buildid='' -X github.com/edgelesssys/constellation/v2/internal/constants.versionInfo=${PROJECT_VERSION}" ./cmd/
|
||||
|
||||
FROM build AS build-measurement-reader
|
||||
WORKDIR /constellation/measurement-reader/
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build go build -o measurement-reader -ldflags "-s -w -buildid=''" ./cmd/
|
||||
|
||||
FROM scratch AS bootstrapper
|
||||
COPY --from=build-bootstrapper /constellation/bootstrapper/bootstrapper /
|
||||
|
||||
@ -50,3 +55,6 @@ COPY --from=build-disk-mapper /constellation/disk-mapper/disk-mapper /
|
||||
|
||||
FROM scratch AS upgrade-agent
|
||||
COPY --from=build-upgrade-agent /constellation/upgrade-agent/upgrade-agent /
|
||||
|
||||
FROM scratch AS measurement-reader
|
||||
COPY --from=build-measurement-reader /constellation/measurement-reader/measurement-reader /
|
||||
|
@ -5,6 +5,7 @@ BOOTSTRAPPER_BINARY ?= $(BASE_PATH)/../build/bootstrapper
|
||||
DISK_MAPPER_BINARY ?= $(BASE_PATH)/../build/disk-mapper
|
||||
UPGRADE_AGENT_BINARY ?= $(BASE_PATH)/../build/upgrade-agent
|
||||
DEBUGD_BINARY ?= $(BASE_PATH)/../build/debugd
|
||||
MEASUREMENT_READER_BINARY ?= $(BASE_PATH)/../build/measurement-reader
|
||||
PKI ?= $(BASE_PATH)/pki
|
||||
MKOSI_EXTRA ?= $(BASE_PATH)/mkosi.extra
|
||||
IMAGE_VERSION ?= v0.0.0
|
||||
@ -65,9 +66,11 @@ inject-bins: $(PREBUILD_RPMS_SYSTEMD) $(PREBUILT_RPMS_AZURE) $(PREBUILT_RPMS_GCP
|
||||
mkdir -p $(MKOSI_EXTRA)/usr/sbin
|
||||
cp $(UPGRADE_AGENT_BINARY) $(MKOSI_EXTRA)/usr/bin/upgrade-agent
|
||||
cp $(DISK_MAPPER_BINARY) $(MKOSI_EXTRA)/usr/sbin/disk-mapper
|
||||
cp $(MEASUREMENT_READER_BINARY) $(MKOSI_EXTRA)/usr/sbin/measurement-reader
|
||||
if [ "$(DEBUG)" = "true" ]; then \
|
||||
cp $(DEBUGD_BINARY) $(MKOSI_EXTRA)/usr/bin/debugd; \
|
||||
rm -f $(MKOSI_EXTRA)/usr/bin/bootstrapper; \
|
||||
rm -f $(MKOSI_EXTRA)/usr/bin/upgrade-agent; \
|
||||
else \
|
||||
cp $(BOOTSTRAPPER_BINARY) $(MKOSI_EXTRA)/usr/bin/bootstrapper; \
|
||||
rm -f $(MKOSI_EXTRA)/usr/bin/debugd; \
|
||||
|
@ -5,4 +5,3 @@ Packages=
|
||||
sbsigntools,
|
||||
efitools,
|
||||
mokutil,
|
||||
tpm2-tools,
|
||||
|
@ -4,5 +4,5 @@ enable constellation-bootstrapper.service
|
||||
enable containerd.service
|
||||
enable kubelet.service
|
||||
enable systemd-networkd.service
|
||||
enable tpm-pcrs.service
|
||||
enable measurements.service
|
||||
enable export_constellation_debug.service
|
||||
|
@ -1,10 +1,11 @@
|
||||
[Unit]
|
||||
Description=Print PCR state on startup
|
||||
Description=Print image measurements on startup
|
||||
Before=constellation-bootstrapper.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
EnvironmentFile=/run/constellation.env
|
||||
ExecStart=/usr/libexec/constellation-pcrs
|
||||
|
||||
[Install]
|
@ -3,12 +3,12 @@
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
# This script reads the PCR state of the system
|
||||
# This script reads the measurements of the system
|
||||
# and prints the message to the serial console
|
||||
|
||||
main() {
|
||||
pcr_state="$(tpm2_pcrread sha256)"
|
||||
echo -e "PCR state:\n${pcr_state}\n" > /run/issue.d/35_constellation_pcrs.issue
|
||||
pcr_state="$(/usr/sbin/measurement-reader)"
|
||||
echo -e "${pcr_state}\n" > /run/issue.d/35_constellation_pcrs.issue
|
||||
}
|
||||
|
||||
main
|
||||
|
@ -25,7 +25,7 @@ package oid
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Getter returns an ASN.1 Object Identifier.
|
||||
@ -49,7 +49,7 @@ func FromString(oid string) (Getter, error) {
|
||||
case qemuVTPM:
|
||||
return QEMUVTPM{}, nil
|
||||
}
|
||||
return nil, errors.New("unknown OID")
|
||||
return nil, fmt.Errorf("unknown OID: %q", oid)
|
||||
}
|
||||
|
||||
// Dummy OID for testing.
|
||||
|
45
measurement-reader/cmd/main.go
Normal file
45
measurement-reader/cmd/main.go
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/edgelesssys/constellation/v2/measurement-reader/internal/sorted"
|
||||
"github.com/edgelesssys/constellation/v2/measurement-reader/internal/tpm"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log := logger.New(logger.JSONLog, zapcore.InfoLevel)
|
||||
variant := os.Getenv(constants.AttestationVariant)
|
||||
attestationVariant, err := oid.FromString(variant)
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to parse attestation variant")
|
||||
}
|
||||
|
||||
var m []sorted.Measurement
|
||||
switch attestationVariant {
|
||||
case oid.AWSNitroTPM{}, oid.AzureSEVSNP{}, oid.AzureTrustedLaunch{}, oid.GCPSEVES{}, oid.QEMUVTPM{}:
|
||||
m, err = tpm.Measurements()
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to read TPM measurements")
|
||||
}
|
||||
default:
|
||||
log.With(zap.String("attestationVariant", variant)).Fatalf("Unsupported attestation variant")
|
||||
}
|
||||
|
||||
fmt.Println("Measurements:")
|
||||
for _, measurement := range m {
|
||||
fmt.Printf("\t%s : 0x%0X\n", measurement.Index, measurement.Value)
|
||||
}
|
||||
}
|
14
measurement-reader/internal/sorted/sorted.go
Normal file
14
measurement-reader/internal/sorted/sorted.go
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// Type definition for sorted measurements.
|
||||
package sorted
|
||||
|
||||
// Measurement wraps a measurement custom index and value.
|
||||
type Measurement struct {
|
||||
Index string
|
||||
Value []byte
|
||||
}
|
50
measurement-reader/internal/tpm/tpm.go
Normal file
50
measurement-reader/internal/tpm/tpm.go
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// Package tpm reads measurements from a TPM.
|
||||
package tpm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/edgelesssys/constellation/v2/measurement-reader/internal/sorted"
|
||||
tpmClient "github.com/google/go-tpm-tools/client"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
)
|
||||
|
||||
// Measurements returns a sorted list of TPM PCR measurements.
|
||||
func Measurements() ([]sorted.Measurement, error) {
|
||||
m, err := vtpm.GetSelectedMeasurements(vtpm.OpenVTPM, tpmClient.FullPcrSel(tpm2.AlgSHA256))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sortMeasurements(m), nil
|
||||
}
|
||||
|
||||
func sortMeasurements(m measurements.M) []sorted.Measurement {
|
||||
keys := make([]uint32, 0, len(m))
|
||||
for idx := range m {
|
||||
keys = append(keys, idx)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i] < keys[j]
|
||||
})
|
||||
|
||||
var measurements []sorted.Measurement
|
||||
for _, idx := range keys {
|
||||
expected := m[idx].Expected
|
||||
measurements = append(measurements, sorted.Measurement{
|
||||
Index: fmt.Sprintf("PCR[%02d]", idx),
|
||||
Value: expected[:],
|
||||
})
|
||||
}
|
||||
|
||||
return measurements
|
||||
}
|
78
measurement-reader/internal/tpm/tpm_test.go
Normal file
78
measurement-reader/internal/tpm/tpm_test.go
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package tpm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/measurement-reader/internal/sorted"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSortMeasurements(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input measurements.M
|
||||
want []sorted.Measurement
|
||||
}{
|
||||
"pre sorted": {
|
||||
input: measurements.M{
|
||||
0: measurements.WithAllBytes(0x11, false),
|
||||
1: measurements.WithAllBytes(0x22, false),
|
||||
2: measurements.WithAllBytes(0x33, false),
|
||||
},
|
||||
want: []sorted.Measurement{
|
||||
{
|
||||
Index: "PCR[00]",
|
||||
Value: bytes.Repeat([]byte{0x11}, 32),
|
||||
},
|
||||
{
|
||||
Index: "PCR[01]",
|
||||
Value: bytes.Repeat([]byte{0x22}, 32),
|
||||
},
|
||||
{
|
||||
Index: "PCR[02]",
|
||||
Value: bytes.Repeat([]byte{0x33}, 32),
|
||||
},
|
||||
},
|
||||
},
|
||||
"unsorted": {
|
||||
input: measurements.M{
|
||||
1: measurements.WithAllBytes(0x22, false),
|
||||
0: measurements.WithAllBytes(0x11, false),
|
||||
2: measurements.WithAllBytes(0x33, false),
|
||||
},
|
||||
want: []sorted.Measurement{
|
||||
{
|
||||
Index: "PCR[00]",
|
||||
Value: bytes.Repeat([]byte{0x11}, 32),
|
||||
},
|
||||
{
|
||||
Index: "PCR[01]",
|
||||
Value: bytes.Repeat([]byte{0x22}, 32),
|
||||
},
|
||||
{
|
||||
Index: "PCR[02]",
|
||||
Value: bytes.Repeat([]byte{0x33}, 32),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
got := sortMeasurements(tc.input)
|
||||
for i := range got {
|
||||
assert.Equal(got[i].Index, tc.want[i].Index)
|
||||
assert.Equal(got[i].Value, tc.want[i].Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user