Skip to content

Commit

Permalink
treewide: change runtime-handler naming scheme
Browse files Browse the repository at this point in the history
The Contrast runtime handlers are now named in the format `contrast-cc--<platform>-<hash>`, where `<hash>` is the hash of the relevant runtime components for platform and `<platform>` is the lowercase variant of the deployed platform.
  • Loading branch information
msanft committed Aug 2, 2024
1 parent dcb904a commit 34854f5
Show file tree
Hide file tree
Showing 28 changed files with 264 additions and 200 deletions.
2 changes: 1 addition & 1 deletion cli/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func runGenerate(cmd *cobra.Command, args []string) error {
}
}

runtimeHandler, err := mnf.RuntimeHandler(flags.referenceValuesPlatform)
runtimeHandler, err := manifest.RuntimeHandler(flags.referenceValuesPlatform)
if err != nil {
return fmt.Errorf("get runtime handler: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"text/tabwriter"

"github.com/edgelesssys/contrast/cli/cmd"
"github.com/edgelesssys/contrast/cli/constants"
"github.com/edgelesssys/contrast/internal/constants"
"github.com/edgelesssys/contrast/internal/manifest"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -43,7 +43,7 @@ func buildVersionString() string {
fmt.Fprintf(versionsWriter, "\t%s\n", image)
}
}
if refValues, err := json.MarshalIndent(manifest.EmbeddedReferenceValues(), "\t", " "); err == nil {
if refValues, err := json.MarshalIndent(manifest.GetEmbeddedReferenceValues(), "\t", " "); err == nil {
fmt.Fprintf(versionsWriter, "embedded reference values:\t%s\n", refValues)
}
fmt.Fprintf(versionsWriter, "genpolicy version:\t%s\n", constants.GenpolicyVersion)
Expand Down
2 changes: 1 addition & 1 deletion cli/telemetry/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"net/url"
"runtime"

"github.com/edgelesssys/contrast/cli/constants"
"github.com/edgelesssys/contrast/internal/constants"
"github.com/spf13/cobra"
)

Expand Down
2 changes: 1 addition & 1 deletion e2e/genpolicy/genpolicy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestGenpolicy(t *testing.T) {

testCases := kuberesource.GenpolicyRegressionTests()

runtimeHandler, err := manifest.DefaultPlatformHandler(platform)
runtimeHandler, err := manifest.RuntimeHandler(platform)
require.NoError(t, err)

for name, deploy := range testCases {
Expand Down
2 changes: 1 addition & 1 deletion e2e/getdents/getdents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestGetDEnts(t *testing.T) {
// TODO(msanft): Make this configurable
platform := platforms.AKSCloudHypervisorSNP

runtimeHandler, err := manifest.DefaultPlatformHandler(platform)
runtimeHandler, err := manifest.RuntimeHandler(platform)
require.NoError(t, err)

resources := kuberesource.GetDEnts()
Expand Down
2 changes: 1 addition & 1 deletion e2e/openssl/openssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func TestOpenSSL(t *testing.T) {
// TODO(msanft): Make this configurable
platform := platforms.AKSCloudHypervisorSNP

runtimeHandler, err := manifest.DefaultPlatformHandler(platform)
runtimeHandler, err := manifest.RuntimeHandler(platform)
require.NoError(t, err)

resources := kuberesource.OpenSSL()
Expand Down
2 changes: 1 addition & 1 deletion e2e/policy/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestPolicy(t *testing.T) {
// TODO(msanft): Make this configurable
platform := platforms.AKSCloudHypervisorSNP

runtimeHandler, err := manifest.DefaultPlatformHandler(platform)
runtimeHandler, err := manifest.RuntimeHandler(platform)
require.NoError(t, err)

resources := kuberesource.OpenSSL()
Expand Down
2 changes: 1 addition & 1 deletion e2e/servicemesh/servicemesh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestIngressEgress(t *testing.T) {
// TODO(msanft): Make this configurable
platform := platforms.AKSCloudHypervisorSNP

runtimeHandler, err := manifest.DefaultPlatformHandler(platform)
runtimeHandler, err := manifest.RuntimeHandler(platform)
require.NoError(t, err)

resources := kuberesource.Emojivoto(kuberesource.ServiceMeshIngressEgress)
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions internal/kuberesource/parts.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (

// ContrastRuntimeClass creates a new RuntimeClassConfig.
func ContrastRuntimeClass(platform platforms.Platform) (*RuntimeClassConfig, error) {
runtimeHandler, err := manifest.DefaultPlatformHandler(platform)
runtimeHandler, err := manifest.RuntimeHandler(platform)
if err != nil {
return nil, fmt.Errorf("getting default runtime handler: %w", err)
}
Expand All @@ -45,7 +45,7 @@ type NodeInstallerConfig struct {
func NodeInstaller(namespace string, platform platforms.Platform) (*NodeInstallerConfig, error) {
name := "contrast-node-installer"

runtimeHandler, err := manifest.DefaultPlatformHandler(platform)
runtimeHandler, err := manifest.RuntimeHandler(platform)
if err != nil {
return nil, fmt.Errorf("getting default runtime handler: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/kuberesource/resourcegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func main() {
log.Fatalf("Error parsing platform: %v", err)
}

runtimeHandler, err = manifest.DefaultPlatformHandler(platform)
runtimeHandler, err = manifest.RuntimeHandler(platform)
if err != nil {
log.Fatalf("Error getting default runtime handler: %v", err)
}
Expand Down
28 changes: 9 additions & 19 deletions internal/manifest/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import (

// Default returns a default manifest with reference values for the given platform.
func Default(platform platforms.Platform) (*Manifest, error) {
refValues := setReferenceValuesIfUninitialized()
embeddedRefValues := GetEmbeddedReferenceValues()
refValues, err := embeddedRefValues.ForPlatform(platform)
if err != nil {
return nil, fmt.Errorf("get reference values for platform %s: %w", platform, err)
}

mnfst := Manifest{}
switch platform {
Expand All @@ -32,29 +36,15 @@ func Default(platform platforms.Platform) (*Manifest, error) {
return &mnfst, nil
}

// DefaultPlatformHandler is a short-hand for getting the default runtime handler for a platform.
func DefaultPlatformHandler(platform platforms.Platform) (string, error) {
mnf, err := Default(platform)
if err != nil {
return "", fmt.Errorf("generating manifest: %w", err)
}
return mnf.RuntimeHandler(platform)
}

// EmbeddedReferenceValues returns the reference values embedded in the binary.
func EmbeddedReferenceValues() ReferenceValues {
return setReferenceValuesIfUninitialized()
}

// EmbeddedReferenceValuesIfUninitialized returns the reference values embedded in the binary.
func setReferenceValuesIfUninitialized() ReferenceValues {
var embeddedReferenceValues *ReferenceValues
// GetEmbeddedReferenceValues returns the reference values embedded in the binary.
func GetEmbeddedReferenceValues() EmbeddedReferenceValues {
var embeddedReferenceValues EmbeddedReferenceValues

if err := json.Unmarshal(EmbeddedReferenceValuesJSON, &embeddedReferenceValues); err != nil {
// As this relies on a constant, predictable value (i.e. the embedded JSON), which -- in a correctly built binary -- should
// unmarshal safely into the [ReferenceValues], it's acceptable to panic here.
panic(fmt.Errorf("failed to unmarshal embedded reference values: %w", err))
}

return *embeddedReferenceValues
return embeddedReferenceValues
}
13 changes: 0 additions & 13 deletions internal/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"encoding/base64"
"fmt"

"github.com/edgelesssys/contrast/internal/platforms"
"github.com/google/go-sev-guest/abi"
"github.com/google/go-sev-guest/kds"
"github.com/google/go-sev-guest/validate"
Expand Down Expand Up @@ -172,15 +171,3 @@ func (m *Manifest) AKSValidateOpts() (*validate.Options, error) {
PermitProvisionalFirmware: true,
}, nil
}

// RuntimeHandler returns the runtime handler for the given platform.
func (m *Manifest) RuntimeHandler(platform platforms.Platform) (string, error) {
switch platform {
case platforms.AKSCloudHypervisorSNP:
return fmt.Sprintf("contrast-cc-%s", m.ReferenceValues.AKS.TrustedMeasurement[:32]), nil
case platforms.K3sQEMUTDX, platforms.RKE2QEMUTDX:
return fmt.Sprintf("contrast-cc-%s", m.ReferenceValues.BareMetalTDX.TrustedMeasurement[:32]), nil
default:
return "", fmt.Errorf("unsupported platform %s", platform)
}
}
51 changes: 50 additions & 1 deletion internal/manifest/referencevalues.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
"encoding/json"
"fmt"
"strconv"
"strings"

"github.com/edgelesssys/contrast/internal/platforms"
)

// EmbeddedReferenceValuesJSON contains the embedded reference values in JSON format.
// At startup, they are unmarshaled into a globally-shared ReferenceValues struct.
//
//go:embed assets/reference-values.json
var EmbeddedReferenceValuesJSON []byte
Expand All @@ -25,6 +27,10 @@ type ReferenceValues struct {
BareMetalTDX *BareMetalTDXReferenceValues `json:"bareMetalTDX,omitempty"`
}

// EmbeddedReferenceValues is a map of runtime handler names to reference values, as
// embedded in the binary.
type EmbeddedReferenceValues map[string]ReferenceValues

// AKSReferenceValues contains reference values for AKS.
type AKSReferenceValues struct {
SNP SNPReferenceValues
Expand Down Expand Up @@ -94,3 +100,46 @@ func (h HexString) String() string {
func (h HexString) Bytes() ([]byte, error) {
return hex.DecodeString(string(h))
}

// ForPlatform returns the reference values for the given platform.
func (e *EmbeddedReferenceValues) ForPlatform(platform platforms.Platform) (*ReferenceValues, error) {
var mapping EmbeddedReferenceValues
if err := json.Unmarshal(EmbeddedReferenceValuesJSON, &mapping); err != nil {
return nil, fmt.Errorf("unmarshal embedded reference values mapping: %w", err)
}

for handler, referenceValues := range mapping {
p, err := platformFromHandler(handler)
if err != nil {
return nil, fmt.Errorf("invalid handler name: %w", err)
}

if p == platform {
return &referenceValues, nil
}
}

return nil, fmt.Errorf("no embedded reference values found for platform: %s", platform)
}

// platformFromHandler extracts the platform from the runtime handler name.
func platformFromHandler(handler string) (platforms.Platform, error) {
rest, found := strings.CutPrefix(handler, "contrast-cc-")
if !found {
return platforms.Unknown, fmt.Errorf("invalid handler name: %s", handler)
}

parts := strings.Split(rest, "-")
if len(parts) != 4 {
return platforms.Unknown, fmt.Errorf("invalid handler name: %s", handler)
}

rawPlatform := fmt.Sprintf("%s-%s-%s", parts[0], parts[1], parts[2])

platform, err := platforms.FromString(rawPlatform)
if err != nil {
return platforms.Unknown, fmt.Errorf("invalid platform in handler name: %w", err)
}

return platform, nil
}
32 changes: 32 additions & 0 deletions internal/manifest/runtimehandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2024 Edgeless Systems GmbH
// SPDX-License-Identifier: AGPL-3.0-only

package manifest

import (
"encoding/json"
"fmt"

"github.com/edgelesssys/contrast/internal/platforms"
)

// RuntimeHandler returns the name of the runtime handler for the given platform.
func RuntimeHandler(platform platforms.Platform) (string, error) {
var mapping EmbeddedReferenceValues
if err := json.Unmarshal(EmbeddedReferenceValuesJSON, &mapping); err != nil {
return "", fmt.Errorf("unmarshal embedded reference values mapping: %w", err)
}

for runtimeHandler := range mapping {
p, err := platformFromHandler(runtimeHandler)
if err != nil {
return "", fmt.Errorf("invalid runtime handler name %s: %w", runtimeHandler, err)
}

if p == platform {
return runtimeHandler, nil
}
}

return "", fmt.Errorf("no runtime handler found for platform %s", platform)
}
24 changes: 24 additions & 0 deletions internal/manifest/runtimehandler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2024 Edgeless Systems GmbH
// SPDX-License-Identifier: AGPL-3.0-only

package manifest

import (
"testing"

"github.com/edgelesssys/contrast/internal/platforms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestRuntimeHandler(t *testing.T) {
require := require.New(t)
assert := assert.New(t)
for _, platform := range platforms.All() {
runtimeHandler, err := RuntimeHandler(platform)
require.NoError(err)
assert.NotEmpty(runtimeHandler)
assert.Less(len(runtimeHandler), 64, "runtime handler name can be 63 characters at most")
assert.Regexp(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`, runtimeHandler, "runtimeHandlerName must be a lowercase RFC 1123 subdomain")
}
}
14 changes: 7 additions & 7 deletions nodeinstaller/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ If desired, you can replace the configuration using a Kubernetes configmap by mo

- `files`: List of files to be installed.
- `files[*].url`: Source of the file's content. Use `http://` or `https://` to download it or `file://` to copy a file from the container image.
- `files[*].path`: Target location of the file on the host filesystem.
- `files[*].path`: Target location of the file on the host filesystem. The `@@runtimeBase@@` placeholder can be used to get a unique per-runtime-handler path.
For example, `@@runtimeBase@@/foo` will resolve to `/opt/edgeless/contrast-cc-<platform>-<runtime-hash>/foo`, where `<platform>` is the platform the node-installer is deployed on,
and `<runtime-hash>` is a hash of all relevant runtime components, so that it's unique per-version.
- `files[*].integrity`: Expected Subresource Integrity (SRI) digest of the file. Only required if URL starts with `http://` or `https://`.
- `runtimeHandlerName`: Name of the container runtime.
- `debugRuntime`: If set to true, enables [serial console access via `vsock`](/dev-docs/aks/serial-console.md). A special, debug-capable IGVM file has to be used for this to work.

Consider the following example:
Expand All @@ -24,25 +25,24 @@ Consider the following example:
"files": [
{
"url": "https://cdn.confidential.cloud/contrast/node-components/2024-03-13/kata-containers.img",
"path": "/opt/edgeless/share/kata-containers.img",
"path": "@@runtimeBase@@/kata-containers.img",
"integrity": "sha256-EdFywKAU+xD0BXmmfbjV4cB6Gqbq9R9AnMWoZFCM3A0="
},
{
"url": "https://cdn.confidential.cloud/contrast/node-components/2024-03-13/kata-containers-igvm.img",
"path": "/opt/edgeless/share/kata-containers-igvm.img",
"path": "@@runtimeBase@@/kata-containers-igvm.img",
"integrity": "sha256-E9Ttx6f9QYwKlQonO/fl1bF2MNBoU4XG3/HHvt9Zv30="
},
{
"url": "https://cdn.confidential.cloud/contrast/node-components/2024-03-13/cloud-hypervisor-cvm",
"path": "/opt/edgeless/bin/cloud-hypervisor-snp",
"path": "@@runtimeBase@@/cloud-hypervisor-snp",
"integrity": "sha256-coTHzd5/QLjlPQfrp9d2TJTIXKNuANTN7aNmpa8PRXo="
},
{
"url": "file:///opt/edgeless/bin/containerd-shim-contrast-cc-v2",
"path": "/opt/edgeless/bin/containerd-shim-contrast-cc-v2",
"path": "@@runtimeBase@@/containerd-shim-contrast-cc-v2",
}
],
"runtimeHandlerName": "contrast-cc",
"debugRuntime": false
}
```
20 changes: 3 additions & 17 deletions nodeinstaller/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ import (
"errors"
"net/url"
"path/filepath"
"regexp"
"strings"
)

// Config is the configuration for the node-installer.
type Config struct {
// Files is a list of files to download.
Files []File `json:"files"`
// RuntimeHandlerName is the name of the runtime handler (containerd runtime) to create.
RuntimeHandlerName string `json:"runtimeHandlerName"`
// DebugRuntime enables the debug mode of the runtime.
// This only works if the igvm file has shell access enabled
// and has no effect on production images.
Expand All @@ -25,19 +23,6 @@ type Config struct {

// Validate validates the configuration.
func (c Config) Validate() error {
if c.RuntimeHandlerName == "" {
return errors.New("runtimeHandlerName is required")
}
if len(c.RuntimeHandlerName) > 63 {
return errors.New("runtimeHandlerName must be 63 characters or fewer")
}
matched, err := regexp.Match(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`, []byte(c.RuntimeHandlerName))
if err != nil {
return err
}
if !matched {
return errors.New("runtimeHandlerName must be a lowercase RFC 1123 subdomain")
}
for _, file := range c.Files {
if err := file.Validate(); err != nil {
return err
Expand Down Expand Up @@ -79,7 +64,8 @@ func (f File) Validate() error {
if f.Path == "" {
return errors.New("path is required")
}
if !filepath.IsAbs(f.Path) {
effectivePath := strings.ReplaceAll(f.Path, "@@runtimeBase@@", "")
if !filepath.IsAbs(effectivePath) {
return errors.New("path must be absolute")
}
if f.Integrity == "" {
Expand Down
Loading

0 comments on commit 34854f5

Please sign in to comment.