Skip to content

Commit

Permalink
Merge pull request #5977 from oasisprotocol/peternose/trivial/elf-met…
Browse files Browse the repository at this point in the history
…adata

go/runtime/bundle/component: Add ELF metadata
  • Loading branch information
peternose authored Dec 17, 2024
2 parents 76552be + c483e20 commit fed855c
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 134 deletions.
Empty file added .changelog/5976.trivial.md
Empty file.
8 changes: 5 additions & 3 deletions go/oasis-test-runner/oasis/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,11 @@ func (rt *Runtime) toRuntimeBundle(deploymentIndex int) (*bundle.Bundle, error)
_ = bnd.Add(elfBin, bundle.NewBytesData(binBuf))

comp := &bundle.Component{
Kind: compCfg.Kind,
Version: compCfg.Version,
Executable: elfBin,
Kind: compCfg.Kind,
Version: compCfg.Version,
ELF: &bundle.ELFMetadata{
Executable: elfBin,
},
}

if rt.teeHardware == node.TEEHardwareIntelSGX {
Expand Down
27 changes: 21 additions & 6 deletions go/runtime/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,22 @@ func (bnd *Bundle) Validate() error {
}
var needFiles []bundleFile
for id, comp := range bnd.Manifest.GetAvailableComponents() {
needFiles = append(needFiles, bundleFile{
descr: fmt.Sprintf("%s: ELF executable", id),
fn: comp.Executable,
optional: true,
})
if elf := comp.ELF; elf != nil {
needFiles = append(needFiles,
[]bundleFile{
{
descr: fmt.Sprintf("%s: ELF executable", id),
fn: elf.Executable,
},
}...,
)
} else {
needFiles = append(needFiles, bundleFile{
descr: fmt.Sprintf("%s: ELF executable", id),
fn: comp.Executable,
optional: true,
})
}
if sgx := comp.SGX; sgx != nil {
needFiles = append(needFiles,
[]bundleFile{
Expand Down Expand Up @@ -494,7 +505,11 @@ func (bnd *Bundle) WriteExploded(dataDir string) (string, error) {
}

for id, comp := range bnd.Manifest.GetAvailableComponents() {
if comp.Executable != "" {
if comp.ELF != nil {
if err := os.Chmod(bnd.ExplodedPath(dataDir, comp.ELF.Executable), 0o700); err != nil {
return "", fmt.Errorf("runtime/bundle: failed to fixup executable permissions for '%s': %w", id, err)
}
} else if comp.Executable != "" {
if err := os.Chmod(bnd.ExplodedPath(dataDir, comp.Executable), 0o700); err != nil {
return "", fmt.Errorf("runtime/bundle: failed to fixup executable permissions for '%s': %w", id, err)
}
Expand Down
20 changes: 12 additions & 8 deletions go/runtime/bundle/bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ func TestBundle(t *testing.T) {
}(),
Components: []*Component{
{
Kind: component.RONL,
Executable: "runtime.bin",
Kind: component.RONL,
ELF: &ELFMetadata{
Executable: "runtime.bin",
},
SGX: &SGXMetadata{
Executable: "runtime.sgx",
},
Expand All @@ -54,7 +56,7 @@ func TestBundle(t *testing.T) {
require.False(t, manifest.IsDetached(), "manifest with RONL component should not be detached")

t.Run("Add_Write", func(t *testing.T) {
err := bundle.Add(manifest.Components[0].Executable, NewBytesData(randBuffer(3252)))
err := bundle.Add(manifest.Components[0].ELF.Executable, NewBytesData(randBuffer(3252)))
require.NoError(t, err, "bundle.Add(elf)")
err = bundle.Add(manifest.Components[0].SGX.Executable, NewBytesData(randBuffer(6541)))
require.NoError(t, err, "bundle.Add(sgx)")
Expand Down Expand Up @@ -84,15 +86,15 @@ func TestBundle(t *testing.T) {

t.Run("Open_WithManifestHash", func(t *testing.T) {
var manifestHash hash.Hash
err := manifestHash.UnmarshalHex("905e9866eccb967e8991698273f41d20c616cab4f9e9332a2a12d9d3a1c8a486")
err := manifestHash.UnmarshalHex("2faad6101e24f0034a82b99aee10f4de909a00b1b2c125f7d6fb97d65dc7984b")
require.NoError(t, err, "UnmarshalHex")

_, err = Open(bundleFn, WithManifestHash(manifestHash))
require.NoError(t, err, "Open_WithManifestHash")

_, err = Open(bundleFn, WithManifestHash(hash.Hash{}))
require.Error(t, err, "Open_WithManifestHash")
require.ErrorContains(t, err, "invalid manifest (got: 905e9866eccb967e8991698273f41d20c616cab4f9e9332a2a12d9d3a1c8a486, expected: 0000000000000000000000000000000000000000000000000000000000000000)")
require.ErrorContains(t, err, "invalid manifest (got: 2faad6101e24f0034a82b99aee10f4de909a00b1b2c125f7d6fb97d65dc7984b, expected: 0000000000000000000000000000000000000000000000000000000000000000)")
})

t.Run("ResetManifest", func(t *testing.T) {
Expand Down Expand Up @@ -137,8 +139,10 @@ func TestDetachedBundle(t *testing.T) {
Components: []*Component{
// No RONL component in the manifest.
{
Kind: component.ROFL,
Executable: "runtime.bin",
Kind: component.ROFL,
ELF: &ELFMetadata{
Executable: "runtime.bin",
},
SGX: &SGXMetadata{
Executable: "runtime.sgx",
},
Expand All @@ -152,7 +156,7 @@ func TestDetachedBundle(t *testing.T) {
require.True(t, manifest.IsDetached(), "manifest without RONL component should be detached")

t.Run("Add_Write", func(t *testing.T) {
err := bundle.Add(manifest.Components[0].Executable, NewBytesData(randBuffer(2231)))
err := bundle.Add(manifest.Components[0].ELF.Executable, NewBytesData(randBuffer(2231)))
require.NoError(t, err, "bundle.Add(elf)")
err = bundle.Add(manifest.Components[0].SGX.Executable, NewBytesData(randBuffer(7627)))
require.NoError(t, err, "bundle.Add(sgx)")
Expand Down
132 changes: 130 additions & 2 deletions go/runtime/bundle/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"path/filepath"

"github.com/oasisprotocol/oasis-core/go/common"
"github.com/oasisprotocol/oasis-core/go/common/sgx"
"github.com/oasisprotocol/oasis-core/go/common/version"
"github.com/oasisprotocol/oasis-core/go/runtime/bundle/component"
)
Expand Down Expand Up @@ -40,8 +41,12 @@ type Component struct {
Version version.Version

// Executable is the name of the runtime ELF executable file if any.
// NOTE: This may go away in the future, use `ELF` instead.
Executable string `json:"executable,omitempty"`

// ELF is the ELF specific manifest metadata if any.
ELF *ELFMetadata `json:"elf,omitempty"`

// SGX is the SGX specific manifest metadata if any.
SGX *SGXMetadata `json:"sgx,omitempty"`

Expand Down Expand Up @@ -78,6 +83,12 @@ func (c *Component) Validate() error {
) {
return fmt.Errorf("each component can only include metadata for a single TEE")
}
if c.ELF != nil {
err := c.ELF.Validate()
if err != nil {
return fmt.Errorf("elf: %w", err)
}
}
if c.SGX != nil {
err := c.SGX.Validate()
if err != nil {
Expand All @@ -96,7 +107,7 @@ func (c *Component) Validate() error {
if c.Name != "" {
return fmt.Errorf("RONL component must have an empty name")
}
if c.Executable == "" {
if c.Executable == "" && c.ELF == nil {
return fmt.Errorf("RONL component must define an executable")
}
if c.Disabled {
Expand All @@ -123,7 +134,7 @@ func (c *Component) IsNetworkAllowed() bool {

// IsTEERequired returns true iff the component only provides TEE executables.
func (c *Component) IsTEERequired() bool {
return c.Executable == "" && c.TEEKind() != component.TEEKindNone
return c.Executable == "" && c.ELF == nil && c.TEEKind() != component.TEEKindNone
}

// TEEKind returns the kind of TEE supported by the component.
Expand All @@ -137,3 +148,120 @@ func (c *Component) TEEKind() component.TEEKind {
return component.TEEKindNone
}
}

// ELFMetadata is the ELF specific manifest metadata.
type ELFMetadata struct {
// Executable is the name of the ELF executable file.
Executable string `json:"executable"`
}

// Validate validates the ELF metadata structure for well-formedness.
func (e *ELFMetadata) Validate() error {
if e.Executable == "" {
return fmt.Errorf("executable must be set")
}
return nil
}

// SGXMetadata is the SGX specific manifest metadata.
type SGXMetadata struct {
// Executable is the name of the SGX enclave executable file.
Executable string `json:"executable"`

// Signature is the name of the SGX enclave signature file.
Signature string `json:"signature"`
}

// Validate validates the SGX metadata structure for well-formedness.
func (s *SGXMetadata) Validate() error {
if s.Executable == "" {
return fmt.Errorf("executable must be set")
}
return nil
}

// TDXMetadata is the TDX specific manifest metadata.
//
// Note that changes to these fields may change the TD measurements.
type TDXMetadata struct {
// Firmware is the name of the virtual firmware file. It should rarely change and multiple
// components may use the same firmware.
Firmware string `json:"firmware"`
// Kernel is the name of the kernel image file. It should rarely change and multiple components
// may use the same kernel.
Kernel string `json:"kernel,omitempty"`
// InitRD is the name of the initial RAM disk image file. It should rarely change and multiple
// components may use the same initrd.
InitRD string `json:"initrd,omitempty"`
// ExtraKernelOptions are the extra kernel options to pass to the kernel after any of the
// default options. Note that kernel options affect TD measurements.
ExtraKernelOptions []string `json:"extra_kernel_options,omitempty"`

// Stage2Image is the name of the stage 2 VM image file.
Stage2Image string `json:"stage2_image,omitempty"`

// Resources are the requested VM resources.
Resources TDXResources `json:"resources"`
}

// Validate validates the TDX metadata structure for well-formedness.
func (t *TDXMetadata) Validate() error {
if t.Firmware == "" {
return fmt.Errorf("firmware must be set")
}
if !t.HasKernel() && t.HasStage2() {
return fmt.Errorf("kernel must be set if stage 2 image is set")
}
if !t.HasKernel() && t.HasInitRD() {
return fmt.Errorf("kernel must be set if initrd image is set")
}
if err := t.Resources.Validate(); err != nil {
return err
}
return nil
}

// HasKernel returns true iff the TDX metadata indicates there is a kernel present.
func (t *TDXMetadata) HasKernel() bool {
return t.Kernel != ""
}

// HasInitRD returns true iff the TDX metadata indicates there is an initial RAM disk image present.
func (t *TDXMetadata) HasInitRD() bool {
return t.InitRD != ""
}

// HasStage2 returns true iff the TDX metadata indicates there is a stage 2 image present.
func (t *TDXMetadata) HasStage2() bool {
return t.Stage2Image != ""
}

// TDXResources are the requested VM resources for TDX VMs.
//
// Note that changes to these fields may change the TD measurements.
type TDXResources struct {
// Memory is the requested VM memory amount in megabytes.
Memory uint64 `json:"memory"`
// CPUCount is the requested number of vCPUs.
CPUCount uint8 `json:"cpus"`
}

// Validate validates the VM resources.
func (r *TDXResources) Validate() error {
if r.Memory < 16 {
return fmt.Errorf("memory limit must be at least 16M")
}
if r.CPUCount < 1 {
return fmt.Errorf("vCPU count must be at least 1")
}
return nil
}

// Identity is the cryptographic identity of a component.
type Identity struct {
// Hypervisor is the optional hypervisor this identity is for.
Hypervisor string `json:"hypervisor,omitempty"`

// Enclave is the enclave identity.
Enclave sgx.EnclaveIdentity `json:"enclave"`
}
Loading

0 comments on commit fed855c

Please sign in to comment.