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

sdk: move verify code into sdk #1030

Merged
merged 13 commits into from
Dec 4, 2024
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
11 changes: 11 additions & 0 deletions sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 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.
derpsteb marked this conversation as resolved.
Show resolved Hide resolved

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

# Building

To build the SDK please add the build tag `contrast_unstable_api` to the importing module's files and build commands.
This tag will be removed once the SDK is officially removed.
derpsteb marked this conversation as resolved.
Show resolved Hide resolved
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