Skip to content

Commit

Permalink
sdk: move verify code into sdk (#1030)
Browse files Browse the repository at this point in the history
For now the SDK is hidden behind a build 
tag and only for internal use.
Because, contrary to the module's version,
the API might receive breaking changes in
a minor version - until officially released.
  • Loading branch information
derpsteb authored Dec 4, 2024
1 parent 1d5e7c8 commit c75797e
Show file tree
Hide file tree
Showing 12 changed files with 259 additions and 80 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ run:
modules-download-mode: readonly
build-tags:
- e2e
- contrast_unstable_api

output:
formats:
Expand Down
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"gopls": {
"formatting.gofumpt": true,
},
"go.buildTags": "e2e",
"go.buildTags": "e2e contrast_unstable_api",
"go.lintTool": "golangci-lint",
"go.lintFlags": [
"--fast",
Expand Down
46 changes: 0 additions & 46 deletions cli/cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,11 @@ package cmd
import (
"context"
_ "embed"
"fmt"
"log/slog"
"os"
"path/filepath"
"time"

"github.com/edgelesssys/contrast/cli/telemetry"
"github.com/edgelesssys/contrast/internal/atls"
"github.com/edgelesssys/contrast/internal/attestation/certcache"
"github.com/edgelesssys/contrast/internal/attestation/snp"
"github.com/edgelesssys/contrast/internal/attestation/tdx"
"github.com/edgelesssys/contrast/internal/fsstore"
"github.com/edgelesssys/contrast/internal/logger"
"github.com/edgelesssys/contrast/internal/manifest"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -81,40 +72,3 @@ func withTelemetry(runFunc func(*cobra.Command, []string) error) func(*cobra.Com
return cmdErr
}
}

// validatorsFromManifest returns a list of validators corresponding to the reference values in the given manifest.
func validatorsFromManifest(m *manifest.Manifest, log *slog.Logger, hostData []byte) ([]atls.Validator, error) {
kdsDir, err := cachedir("kds")
if err != nil {
return nil, fmt.Errorf("getting cache dir: %w", err)
}
log.Debug("Using KDS cache dir", "dir", kdsDir)
kdsCache := fsstore.New(kdsDir, log.WithGroup("kds-cache"))
kdsGetter := certcache.NewCachedHTTPSGetter(kdsCache, certcache.NeverGCTicker, log.WithGroup("kds-getter"))

var validators []atls.Validator

opts, err := m.SNPValidateOpts(kdsGetter)
if err != nil {
return nil, fmt.Errorf("getting SNP validate options: %w", err)
}
for _, opt := range opts {
opt.ValidateOpts.HostData = hostData
validators = append(validators, snp.NewValidator(opt.VerifyOpts, opt.ValidateOpts,
logger.NewWithAttrs(logger.NewNamed(log, "validator"), map[string]string{"tee-type": "snp"}),
))
}

tdxOpts, err := m.TDXValidateOpts()
if err != nil {
return nil, fmt.Errorf("generating TDX validation options: %w", err)
}
var mrConfigID [48]byte
copy(mrConfigID[:], hostData)
for _, opt := range tdxOpts {
opt.TdQuoteBodyOptions.MrConfigID = mrConfigID[:]
validators = append(validators, tdx.NewValidator(&tdx.StaticValidateOptsGenerator{Opts: opt}, logger.NewWithAttrs(logger.NewNamed(log, "validator"), map[string]string{"tee-type": "tdx"})))
}

return validators, nil
}
9 changes: 8 additions & 1 deletion cli/cmd/recover.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/edgelesssys/contrast/internal/grpc/dialer"
"github.com/edgelesssys/contrast/internal/manifest"
"github.com/edgelesssys/contrast/internal/userapi"
"github.com/edgelesssys/contrast/sdk"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -73,7 +74,13 @@ func runRecover(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("decrypting seed: %w", err)
}

validators, err := validatorsFromManifest(&m, log, flags.policy)
kdsDir, err := cachedir("kds")
if err != nil {
return fmt.Errorf("getting cache dir: %w", err)
}
log.Debug("Using KDS cache dir", "dir", kdsDir)

validators, err := sdk.ValidatorsFromManifest(kdsDir, &m, log, flags.policy)
if err != nil {
return fmt.Errorf("getting validators: %w", err)
}
Expand Down
9 changes: 8 additions & 1 deletion cli/cmd/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/edgelesssys/contrast/internal/retry"
"github.com/edgelesssys/contrast/internal/spinner"
"github.com/edgelesssys/contrast/internal/userapi"
"github.com/edgelesssys/contrast/sdk"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -98,7 +99,13 @@ func runSet(cmd *cobra.Command, args []string) error {
return fmt.Errorf("checking policies match manifest: %w", err)
}

validators, err := validatorsFromManifest(&m, log, flags.policy)
kdsDir, err := cachedir("kds")
if err != nil {
return fmt.Errorf("getting cache dir: %w", err)
}
log.Debug("Using KDS cache dir", "dir", kdsDir)

validators, err := sdk.ValidatorsFromManifest(kdsDir, &m, log, flags.policy)
if err != nil {
return fmt.Errorf("getting validators: %w", err)
}
Expand Down
38 changes: 9 additions & 29 deletions cli/cmd/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,13 @@
package cmd

import (
"bytes"
"crypto/sha256"
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"

"github.com/edgelesssys/contrast/internal/atls"
"github.com/edgelesssys/contrast/internal/grpc/dialer"
"github.com/edgelesssys/contrast/internal/manifest"
"github.com/edgelesssys/contrast/internal/userapi"
"github.com/edgelesssys/contrast/sdk"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -60,33 +55,19 @@ func runVerify(cmd *cobra.Command, _ []string) error {
if err != nil {
return fmt.Errorf("failed to read manifest file: %w", err)
}
var m manifest.Manifest
if err := json.Unmarshal(manifestBytes, &m); err != nil {
return fmt.Errorf("failed to unmarshal manifest: %w", err)
}
if err := m.Validate(); err != nil {
return fmt.Errorf("validating manifest: %w", err)
}

validators, err := validatorsFromManifest(&m, log, flags.policy)
kdsDir, err := cachedir("kds")
if err != nil {
return fmt.Errorf("getting validators: %w", err)
return fmt.Errorf("getting cache dir: %w", err)
}
dialer := dialer.New(atls.NoIssuer, validators, atls.NoMetrics, &net.Dialer{})
log.Debug("Using KDS cache dir", "dir", kdsDir)

log.Debug("Dialing coordinator", "endpoint", flags.coordinator)
conn, err := dialer.Dial(cmd.Context(), flags.coordinator)
sdkClient := sdk.NewWithSlog(log)
resp, err := sdkClient.GetCoordinatorState(cmd.Context(), kdsDir, manifestBytes, flags.coordinator, flags.policy)
if err != nil {
return fmt.Errorf("Error: failed to dial coordinator: %w", err)
return fmt.Errorf("getting manifests: %w", err)
}
defer conn.Close()

log.Debug("Getting manifest")
client := userapi.NewUserAPIClient(conn)
resp, err := client.GetManifests(cmd.Context(), &userapi.GetManifestsRequest{})
if err != nil {
return fmt.Errorf("failed to get manifest: %w", err)
}
log.Debug("Got response")

fmt.Fprintln(cmd.OutOrStdout(), "✔️ Successfully verified Coordinator CVM based on reference values from manifest")
Expand All @@ -109,9 +90,8 @@ func runVerify(cmd *cobra.Command, _ []string) error {

fmt.Fprintf(cmd.OutOrStdout(), "✔️ Wrote Coordinator configuration and keys to %s\n", filepath.Join(flags.workspaceDir, verifyDir))

currentManifest := resp.Manifests[len(resp.Manifests)-1]
if !bytes.Equal(currentManifest, manifestBytes) {
return fmt.Errorf("manifest active at Coordinator does not match expected manifest")
if err := sdkClient.Verify(manifestBytes, resp.Manifests); err != nil {
return fmt.Errorf("failed to verify Coordinator manifest: %w", err)
}

fmt.Fprintln(cmd.OutOrStdout(), "✔️ Manifest active at Coordinator matches expected manifest")
Expand Down
9 changes: 7 additions & 2 deletions packages/by-name/contrast/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ let
;
pname = "${contrast.pname}-e2e";

tags = [ "e2e" ];
tags = [
"e2e"
"contrast_unstable_api"
];

subPackages = [
"e2e/genpolicy"
Expand Down Expand Up @@ -201,13 +204,15 @@ buildGoModule rec {
"-X github.com/edgelesssys/contrast/internal/constants.KataGenpolicyVersion=${kata.genpolicy.version}"
];

tags = [ "contrast_unstable_api" ];

preCheck = ''
export CGO_ENABLED=1
'';

checkPhase = ''
runHook preCheck
go test -race ./...
go test -tags=contrast_unstable_api -race ./...
runHook postCheck
'';

Expand Down
10 changes: 10 additions & 0 deletions sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Contrast SDK

**Caution:** This SDK is still under active development and not fit for external use yet.
Please expect breaking changes with new minor versions.

The SDK allows writing programs that interact with a Contrast deployment like the CLI does, without relying on the CLI.

# Building

If you decide to use the unstable API and accept the risk of breakage, you need to set the Go build tag `contrast_unstable_api`.
53 changes: 53 additions & 0 deletions sdk/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2024 Edgeless Systems GmbH
// SPDX-License-Identifier: AGPL-3.0-only

//go:build contrast_unstable_api

package sdk

import (
"fmt"
"log/slog"

"github.com/edgelesssys/contrast/internal/atls"
"github.com/edgelesssys/contrast/internal/attestation/certcache"
"github.com/edgelesssys/contrast/internal/attestation/snp"
"github.com/edgelesssys/contrast/internal/attestation/tdx"
"github.com/edgelesssys/contrast/internal/fsstore"
"github.com/edgelesssys/contrast/internal/logger"
"github.com/edgelesssys/contrast/internal/manifest"
)

// ValidatorsFromManifest returns a list of validators corresponding to the reference values in the given manifest.
// Originally an unexported function in the contrast CLI.
// Can be made unexported again, if we decide to move all userapi calls from the CLI to the SDK.
func ValidatorsFromManifest(kdsDir string, m *manifest.Manifest, log *slog.Logger, coordinatorPolicyChecksum []byte) ([]atls.Validator, error) {
kdsCache := fsstore.New(kdsDir, log.WithGroup("kds-cache"))
kdsGetter := certcache.NewCachedHTTPSGetter(kdsCache, certcache.NeverGCTicker, log.WithGroup("kds-getter"))

var validators []atls.Validator

opts, err := m.SNPValidateOpts(kdsGetter)
if err != nil {
return nil, fmt.Errorf("getting SNP validate options: %w", err)
}
for _, opt := range opts {
opt.ValidateOpts.HostData = coordinatorPolicyChecksum
validators = append(validators, snp.NewValidator(opt.VerifyOpts, opt.ValidateOpts,
logger.NewWithAttrs(logger.NewNamed(log, "validator"), map[string]string{"tee-type": "snp"}),
))
}

tdxOpts, err := m.TDXValidateOpts()
if err != nil {
return nil, fmt.Errorf("generating TDX validation options: %w", err)
}
var mrConfigID [48]byte
copy(mrConfigID[:], coordinatorPolicyChecksum)
for _, opt := range tdxOpts {
opt.TdQuoteBodyOptions.MrConfigID = mrConfigID[:]
validators = append(validators, tdx.NewValidator(&tdx.StaticValidateOptsGenerator{Opts: opt}, logger.NewWithAttrs(logger.NewNamed(log, "validator"), map[string]string{"tee-type": "tdx"})))
}

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

//go:build contrast_unstable_api

package sdk
Loading

0 comments on commit c75797e

Please sign in to comment.