mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-01-11 15:39:33 -05: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()
|
enable_testing()
|
||||||
|
|
||||||
#
|
#
|
||||||
# core-os disk-mapper
|
# disk-mapper
|
||||||
#
|
#
|
||||||
add_custom_target(disk-mapper ALL
|
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 .
|
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
|
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
|
# bootstrapper
|
||||||
#
|
#
|
||||||
|
@ -42,6 +42,11 @@ WORKDIR /constellation/upgrade-agent/
|
|||||||
ARG PROJECT_VERSION
|
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/
|
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
|
FROM scratch AS bootstrapper
|
||||||
COPY --from=build-bootstrapper /constellation/bootstrapper/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
|
FROM scratch AS upgrade-agent
|
||||||
COPY --from=build-upgrade-agent /constellation/upgrade-agent/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
|
DISK_MAPPER_BINARY ?= $(BASE_PATH)/../build/disk-mapper
|
||||||
UPGRADE_AGENT_BINARY ?= $(BASE_PATH)/../build/upgrade-agent
|
UPGRADE_AGENT_BINARY ?= $(BASE_PATH)/../build/upgrade-agent
|
||||||
DEBUGD_BINARY ?= $(BASE_PATH)/../build/debugd
|
DEBUGD_BINARY ?= $(BASE_PATH)/../build/debugd
|
||||||
|
MEASUREMENT_READER_BINARY ?= $(BASE_PATH)/../build/measurement-reader
|
||||||
PKI ?= $(BASE_PATH)/pki
|
PKI ?= $(BASE_PATH)/pki
|
||||||
MKOSI_EXTRA ?= $(BASE_PATH)/mkosi.extra
|
MKOSI_EXTRA ?= $(BASE_PATH)/mkosi.extra
|
||||||
IMAGE_VERSION ?= v0.0.0
|
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
|
mkdir -p $(MKOSI_EXTRA)/usr/sbin
|
||||||
cp $(UPGRADE_AGENT_BINARY) $(MKOSI_EXTRA)/usr/bin/upgrade-agent
|
cp $(UPGRADE_AGENT_BINARY) $(MKOSI_EXTRA)/usr/bin/upgrade-agent
|
||||||
cp $(DISK_MAPPER_BINARY) $(MKOSI_EXTRA)/usr/sbin/disk-mapper
|
cp $(DISK_MAPPER_BINARY) $(MKOSI_EXTRA)/usr/sbin/disk-mapper
|
||||||
|
cp $(MEASUREMENT_READER_BINARY) $(MKOSI_EXTRA)/usr/sbin/measurement-reader
|
||||||
if [ "$(DEBUG)" = "true" ]; then \
|
if [ "$(DEBUG)" = "true" ]; then \
|
||||||
cp $(DEBUGD_BINARY) $(MKOSI_EXTRA)/usr/bin/debugd; \
|
cp $(DEBUGD_BINARY) $(MKOSI_EXTRA)/usr/bin/debugd; \
|
||||||
rm -f $(MKOSI_EXTRA)/usr/bin/bootstrapper; \
|
rm -f $(MKOSI_EXTRA)/usr/bin/bootstrapper; \
|
||||||
|
rm -f $(MKOSI_EXTRA)/usr/bin/upgrade-agent; \
|
||||||
else \
|
else \
|
||||||
cp $(BOOTSTRAPPER_BINARY) $(MKOSI_EXTRA)/usr/bin/bootstrapper; \
|
cp $(BOOTSTRAPPER_BINARY) $(MKOSI_EXTRA)/usr/bin/bootstrapper; \
|
||||||
rm -f $(MKOSI_EXTRA)/usr/bin/debugd; \
|
rm -f $(MKOSI_EXTRA)/usr/bin/debugd; \
|
||||||
|
@ -5,4 +5,3 @@ Packages=
|
|||||||
sbsigntools,
|
sbsigntools,
|
||||||
efitools,
|
efitools,
|
||||||
mokutil,
|
mokutil,
|
||||||
tpm2-tools,
|
|
||||||
|
@ -4,5 +4,5 @@ enable constellation-bootstrapper.service
|
|||||||
enable containerd.service
|
enable containerd.service
|
||||||
enable kubelet.service
|
enable kubelet.service
|
||||||
enable systemd-networkd.service
|
enable systemd-networkd.service
|
||||||
enable tpm-pcrs.service
|
enable measurements.service
|
||||||
enable export_constellation_debug.service
|
enable export_constellation_debug.service
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Print PCR state on startup
|
Description=Print image measurements on startup
|
||||||
Before=constellation-bootstrapper.service
|
Before=constellation-bootstrapper.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
|
EnvironmentFile=/run/constellation.env
|
||||||
ExecStart=/usr/libexec/constellation-pcrs
|
ExecStart=/usr/libexec/constellation-pcrs
|
||||||
|
|
||||||
[Install]
|
[Install]
|
@ -3,12 +3,12 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# 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
|
# and prints the message to the serial console
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
pcr_state="$(tpm2_pcrread sha256)"
|
pcr_state="$(/usr/sbin/measurement-reader)"
|
||||||
echo -e "PCR state:\n${pcr_state}\n" > /run/issue.d/35_constellation_pcrs.issue
|
echo -e "${pcr_state}\n" > /run/issue.d/35_constellation_pcrs.issue
|
||||||
}
|
}
|
||||||
|
|
||||||
main
|
main
|
||||||
|
@ -25,7 +25,7 @@ package oid
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"errors"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Getter returns an ASN.1 Object Identifier.
|
// Getter returns an ASN.1 Object Identifier.
|
||||||
@ -49,7 +49,7 @@ func FromString(oid string) (Getter, error) {
|
|||||||
case qemuVTPM:
|
case qemuVTPM:
|
||||||
return QEMUVTPM{}, nil
|
return QEMUVTPM{}, nil
|
||||||
}
|
}
|
||||||
return nil, errors.New("unknown OID")
|
return nil, fmt.Errorf("unknown OID: %q", oid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dummy OID for testing.
|
// 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