Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

treewide: process container OCI runtime configs during attestation #216

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*.o
*.bkp
*.swp
.vscode/

cmcd/cmcd
Expand Down
5 changes: 4 additions & 1 deletion attestationreport/attestationreport.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type DriverConfig struct {
UseCtr bool
CtrPcr int
CtrLog string
ExtCtrLog bool
CtrDriver string
}

Expand Down Expand Up @@ -106,6 +107,7 @@ type MeasureEvent struct {
type CtrData struct {
ConfigSha256 HexByte `json:"configSha256" cbor:"0,keyasint"`
RootfsSha256 HexByte `json:"rootfsSha256" cbor:"1,keyasint"`
OciSpec []byte `json:"ociSpec" cbor:"2,keyasint"`
}

// Measurement represents the attestation report
Expand All @@ -116,7 +118,7 @@ type Measurement struct {
Evidence []byte `json:"evidence,omitempty" cbor:"1,keyasint"`
Certs [][]byte `json:"certs,omitempty" cbor:"3,keyasint"`
Signature []byte `json:"signature,omitempty" cbor:"2,keyasint,omitempty"`
Artifacts []Artifact `json:"details,omitempty" cbor:"4,keyasint,omitempty"`
Artifacts []Artifact `json:"artifacts,omitempty" cbor:"4,keyasint,omitempty"`
}

type SnpPolicy struct {
Expand Down Expand Up @@ -291,6 +293,7 @@ type AppManifest struct {
CertificationLevel int `json:"certificationLevel" cbor:"6,keyasint"`
Validity Validity `json:"validity" cbor:"7,keyasint"`
ReferenceValues []ReferenceValue `json:"referenceValues" cbor:"8,keyasint"`
OciSpec any `json:"ociSpec" cbor:"9,keyasint"`
}

// OsManifest represents the attestation report
Expand Down
93 changes: 80 additions & 13 deletions attestationreport/validationreport.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"time"

"github.com/Fraunhofer-AISEC/cmc/internal"
oci "github.com/opencontainers/runtime-spec/specs-go"
)

// VerificationResult represents the results of all steps taken during
Expand Down Expand Up @@ -104,8 +105,8 @@ type MeasurementResult struct {
}

type TpmResult struct {
PcrMatch []DigestResult `json:"pcrMatch"`
AggPcrQuoteMatch Result `json:"aggPcrQuoteMatch"`
PcrMatch []PcrResult `json:"pcrMatch"`
AggPcrQuoteMatch Result `json:"aggPcrQuoteMatch"`
}

type SnpResult struct {
Expand Down Expand Up @@ -133,16 +134,30 @@ type TdxResult struct {
}

// DigestResult represents a generic result for a digest that was processed
// during attestation or for an entire PCR
// during attestation
type DigestResult struct {
Pcr *int `json:"pcr,omitempty"` // Number for the PCR if present (TPM)
Name string `json:"name,omitempty"` // Name of the software artifact
Digest string `json:"digest,omitempty"` // Reference Digest
Description string `json:"description,omitempty"` // Optional description, measured PCR in case of PCR result
Success bool `json:"success"` // Indicates whether match was found
Type string `json:"type,omitempty"` // On fail, indicates whether digest is reference or measurement
EventData *EventData `json:"eventData,omitempty"` // data that was included from bioseventlog
CtrData *CtrData `json:"ctrData,omitempty"` // data that was included from container log
Success bool `json:"success"` // Indicates if component is valid
Launched bool `json:"launched"` // Indicates if optional component is/was running on the system
Pcr *int `json:"pcr,omitempty"` // Number for the PCR if present (TPM)
Name string `json:"name,omitempty"` // Name of the software artifact
Digest string `json:"digest,omitempty"` // Reference Digest
Description string `json:"description,omitempty"` // Optional description, measured PCR in case of PCR result
Type string `json:"type,omitempty"` // On fail, indicates whether digest is reference or measurement
EventData *EventData `json:"eventData,omitempty"` // data that was included from bioseventlog
CtrDetails *CtrDetails `json:"ctrDetails,omitempty"` // data that was included from container log
}

type PcrResult struct {
Success bool `json:"success"`
Pcr int `json:"pcr"`
Digest string `json:"digest"`
Measured string `json:"measured,omitempty"`
}

type CtrDetails struct {
ConfigSha256 HexByte `json:"configSha256,omitempty"`
RootfsSha256 HexByte `json:"rootfsSha256,omitempty"`
OciSpec *oci.Spec `json:"ociSpec,omitempty"`
}

type VersionCheck struct {
Expand Down Expand Up @@ -706,8 +721,8 @@ func (r *VerificationResult) PrintErr() {
m.TpmResult.AggPcrQuoteMatch.PrintErr("Aggregated PCR verification")
for _, p := range m.TpmResult.PcrMatch {
if !p.Success {
log.Warnf("PCR%v calculated: %v, measured: %v", *p.Pcr, p.Digest,
p.Description)
log.Warnf("PCR%v calculated: %v, measured: %v", p.Pcr, p.Digest,
p.Measured)
}
}
}
Expand Down Expand Up @@ -779,3 +794,55 @@ func (r *VerificationResult) PrintErr() {
}
}
}

func GetCtrDetailsFromRefVal(r *ReferenceValue, s Serializer) *CtrDetails {

if r == nil {
log.Warnf("internal error: reference value is nil")
return nil
}

// Get OCI runtime config from manifest
m, specOk := r.GetManifest().(AppManifest)
if !specOk {
log.Warnf("internal error: failed to get manifest from reference value")
return nil
}

ociSpecRaw, err := s.Marshal(m.OciSpec)
if err != nil {
log.Warnf("failed to unmarshal OCI spec for %v: %v", m.Name, err)
return nil
}

ociSpec := new(oci.Spec)
err = s.Unmarshal(ociSpecRaw, ociSpec)
if err != nil {
log.Warnf("failed to unmarshal OCI spec: %v", err)
return nil
}

return &CtrDetails{
OciSpec: ociSpec,
}
}

func GetCtrDetailsFromMeasureEvent(m *MeasureEvent, s Serializer) *CtrDetails {

if m == nil || m.CtrData == nil || m.CtrData.OciSpec == nil {
return nil
}

ociSpec := new(oci.Spec)
err := s.Unmarshal(m.CtrData.OciSpec, ociSpec)
if err != nil {
log.Warnf("failed to unmarshal OCI spec: %v", err)
return nil
}

return &CtrDetails{
ConfigSha256: m.CtrData.ConfigSha256,
RootfsSha256: m.CtrData.RootfsSha256,
OciSpec: ociSpec,
}
}
4 changes: 4 additions & 0 deletions cmc/cmc.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type Config struct {
CtrDriver string `json:"ctrDriver,omitempty"`
CtrPcr int `json:"ctrPcr,omitempty"`
CtrLog string `json:"ctrLog"`
ExtCtrLog bool `json:"extendedCtrLog"`
}

type Cmc struct {
Expand All @@ -70,6 +71,7 @@ type Cmc struct {
CtrDriver string
CtrPcr int
CtrLog string
ExtCtrLog bool
}

func GetDrivers() map[string]ar.Driver {
Expand Down Expand Up @@ -98,6 +100,7 @@ func NewCmc(c *Config) (*Cmc, error) {
Serializer: s,
CtrPcr: c.CtrPcr,
CtrLog: c.CtrLog,
ExtCtrLog: c.ExtCtrLog,
CtrDriver: c.CtrDriver,
UseCtr: c.UseCtr,
}
Expand Down Expand Up @@ -141,6 +144,7 @@ func NewCmc(c *Config) (*Cmc, error) {
CtrDriver: c.CtrDriver,
CtrPcr: c.CtrPcr,
CtrLog: c.CtrLog,
ExtCtrLog: c.ExtCtrLog,
}

return cmc, nil
Expand Down
3 changes: 2 additions & 1 deletion cmcd/coap.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,12 @@ func Measure(w mux.ResponseWriter, r *mux.Message) {

log.Debug("Measurer: Recording measurement")
var success bool
err = m.Measure(req.Name, req.ConfigSha256, req.RootfsSha256,
err = m.Measure(&req,
&m.MeasureConfig{
Serializer: Cmc.Serializer,
Pcr: Cmc.CtrPcr,
LogFile: Cmc.CtrLog,
ExtLog: Cmc.ExtCtrLog,
Driver: Cmc.CtrDriver,
})
if err != nil {
Expand Down
25 changes: 16 additions & 9 deletions cmcd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const (
ctrDriverFlag = "ctrdriver"
ctrPcrFlag = "ctrpcr"
ctrLogFlag = "ctrlog"
extCtrLogFlag = "extCtrLog"
)

func getConfig() (*cmc.Config, error) {
Expand Down Expand Up @@ -106,6 +107,8 @@ func getConfig() (*cmc.Config, error) {
"Specifies which driver to use for container measurements")
ctrPcr := flag.Int(ctrPcrFlag, 0, "Container PCR")
ctrLog := flag.String(ctrLogFlag, "", "Container runtime measurements path")
extCtrLog := flag.Bool(extCtrLogFlag, false,
"specifies whether to store extended container logs with OCI runtime specs")
flag.Parse()

// Create default configuration
Expand Down Expand Up @@ -183,6 +186,9 @@ func getConfig() (*cmc.Config, error) {
if internal.FlagPassed(ctrLogFlag) {
c.CtrLog = *ctrLog
}
if internal.FlagPassed(extCtrLogFlag) {
c.ExtCtrLog = *extCtrLog
}

// Configure the logger
l, ok := logLevels[strings.ToLower(c.LogLevel)]
Expand Down Expand Up @@ -243,24 +249,25 @@ func printConfig(c *cmc.Config) {
log.Infof("Running cmcd from working directory %v", wd)

log.Debugf("Using the following configuration:")
log.Debugf("\tCMC Listen Address : %v", c.Addr)
log.Debugf("\tProvisioning Server URL : %v", c.ProvServerAddr)
log.Debugf("\tMetadata Locations : %v", strings.Join(c.Metadata, ","))
log.Debugf("\tCMC listen address : %v", c.Addr)
log.Debugf("\tProvisioning server URL : %v", c.ProvServerAddr)
log.Debugf("\tMetadata locations : %v", strings.Join(c.Metadata, ","))
log.Debugf("\tUse IMA : %v", c.UseIma)
if c.UseIma {
log.Debugf("\tIMA PCR : %v", c.ImaPcr)
}
log.Debugf("\tAPI : %v", c.Api)
log.Debugf("\tNetwork : %v", c.Network)
log.Debugf("\tPolicy Engine : %v", c.PolicyEngine)
log.Debugf("\tKey Config : %v", c.KeyConfig)
log.Debugf("\tLogging Level : %v", c.LogLevel)
log.Debugf("\tPolicy engine : %v", c.PolicyEngine)
log.Debugf("\tKey config : %v", c.KeyConfig)
log.Debugf("\tLogging level : %v", c.LogLevel)
log.Debugf("\tDrivers : %v", strings.Join(c.Drivers, ","))
log.Debugf("\tMeasurement Log : %v", c.MeasurementLog)
log.Debugf("\tMeasurement log : %v", c.MeasurementLog)
log.Debugf("\tMeasure containers : %v", c.UseCtr)
if c.UseCtr {
log.Debugf("\tContainer Driver : %v", c.CtrDriver)
log.Debugf("\tContainer Measurements : %v", c.CtrLog)
log.Debugf("\tContainer driver : %v", c.CtrDriver)
log.Debugf("\tContainer measurements : %v", c.CtrLog)
log.Debugf("\tContainer extended log : %v", c.ExtCtrLog)
log.Debugf("\tContainer PCR : %v", c.CtrPcr)
}
if c.Storage != "" {
Expand Down
11 changes: 10 additions & 1 deletion cmcd/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (

// local modules

cmcapi "github.com/Fraunhofer-AISEC/cmc/api"
"github.com/Fraunhofer-AISEC/cmc/cmc"
"github.com/Fraunhofer-AISEC/cmc/generate"
api "github.com/Fraunhofer-AISEC/cmc/grpcapi"
Expand Down Expand Up @@ -159,8 +160,16 @@ func (s *GrpcServer) Measure(ctx context.Context, in *api.MeasureRequest) (*api.

log.Info("Received Connection Request Type 'Measure Request'")

// TODO better solution? (different but same datatype cmcapi.MeasureRequest and api.MeasureRequest)
req := &cmcapi.MeasureRequest{
Name: in.Name,
ConfigSha256: in.ConfigSha256,
RootfsSha256: in.RootfsSha256,
OciSpec: in.OciSpec,
}

log.Info("Measurer: Recording measurement")
err := m.Measure(in.Name, in.ConfigSha256, in.RootfsSha256,
err := m.Measure(req,
&m.MeasureConfig{
Serializer: s.cmc.Serializer,
Pcr: s.cmc.CtrPcr,
Expand Down
2 changes: 1 addition & 1 deletion cmcd/socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func measure(conn net.Conn, payload []byte, cmc *cmc.Cmc, s ar.Serializer) {

log.Debug("Measurer: recording measurement")
var success bool
err = m.Measure(req.Name, req.ConfigSha256, req.RootfsSha256,
err = m.Measure(req,
&m.MeasureConfig{
Serializer: cmc.Serializer,
Pcr: cmc.CtrPcr,
Expand Down
8 changes: 5 additions & 3 deletions example-setup/update-container-manifest-live
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ input="${data}/metadata-raw"
tmp="${data}/metadata-tmp"
output="${data}/metadata-signed"
runtime="${cmc}/tools/containerd-shim-cmc-v1/containerd-shim-cmc-v1"
runc_config="/etc/runc.json"
runc_config="/etc/runc-measure-conf.json"
target="${target%/}"


Expand Down Expand Up @@ -131,12 +131,14 @@ for ((i = 0; i < num_containers; i++)); do
container=${containers[$i]}
container_name=$(echo "${container}-oci" | sed 's:.*/::' | tr : -)

refval=$(echo "${refvals}" | jq -r ".[$i]")
refval=$(echo "${refvals}" | jq -r ".[$i].referenceValue")
config=$(echo "${refvals}" | jq -r ".[$i].ociSpec")

# App Manifest: Replace existing reference values with new reference values in the App Manifest
# App Manifest: Replace existing reference values and OCI config with new reference values in the App Manifest
json=$(cat "${input}/app.manifest.json")
json=$(echo "${json}" | jq 'del(.referenceValues[])')
json=$(echo "${json}" | jq --argjson ver "[${refval}]" '.referenceValues += $ver')
json=$(echo "${json}" | jq --argjson cfg "${config}" '.ociSpec = $cfg')

# App Manifest: Extract the reference value sha256 and add it to the file name to not overwrite the same
# container with different arguments such as environment variables
Expand Down
Loading
Loading