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

Feat/runtime/peer pods e2e #963

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
4 changes: 4 additions & 0 deletions cli/cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/edgelesssys/contrast/cli/telemetry"
"github.com/edgelesssys/contrast/internal/atls"
"github.com/edgelesssys/contrast/internal/attestation/certcache"
atlsinsecure "github.com/edgelesssys/contrast/internal/attestation/insecure"
"github.com/edgelesssys/contrast/internal/attestation/snp"
"github.com/edgelesssys/contrast/internal/attestation/tdx"
"github.com/edgelesssys/contrast/internal/fsstore"
Expand Down Expand Up @@ -116,5 +117,8 @@ func validatorsFromManifest(m *manifest.Manifest, log *slog.Logger, hostData []b
validators = append(validators, tdx.NewValidator(&tdx.StaticValidateOptsGenerator{Opts: opt}, logger.NewWithAttrs(logger.NewNamed(log, "validator"), map[string]string{"tee-type": "tdx"})))
}

// TODO(@3u13r): Don't add the insecure validator for all manifests.
validators = append(validators, atlsinsecure.NewValidator(log))

return validators, nil
}
18 changes: 13 additions & 5 deletions cli/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,16 @@ func findGenerateTargets(args []string, logger *slog.Logger) ([]string, error) {
return nil, fmt.Errorf("no .yml/.yaml files found")
}

paths = filterNonCoCoRuntime("contrast-cc", paths, logger)
if len(paths) == 0 {
return nil, fmt.Errorf("no .yml/.yaml files with 'contrast-cc' runtime found")
contrastPaths := filterNonCoCoRuntime("contrast-cc", paths, logger)
if len(contrastPaths) != 0 {
return contrastPaths, nil
}
peerPaths := filterNonCoCoRuntime("kata-remote", paths, logger)
if len(peerPaths) != 0 {
return peerPaths, nil
}

return paths, nil
return nil, fmt.Errorf("no .yml/.yaml files with 'contrast-cc' or 'kata-remote' runtime found")
}

func filterNonCoCoRuntime(runtimeClassNamePrefix string, paths []string, logger *slog.Logger) []string {
Expand All @@ -243,7 +247,11 @@ func filterNonCoCoRuntime(runtimeClassNamePrefix string, paths []string, logger
}

func generatePolicies(ctx context.Context, flags *generateFlags, yamlPaths []string, logger *slog.Logger) error {
cfg := genpolicy.NewConfig(flags.referenceValuesPlatform)
cfg, err := genpolicy.NewConfig(flags.referenceValuesPlatform)
if err != nil {
return fmt.Errorf("getting genpolicy config: %w", err)
}

if err := createFileWithDefault(flags.settingsPath, 0o644, func() ([]byte, error) { return cfg.Settings, nil }); err != nil {
return fmt.Errorf("creating default policy file: %w", err)
}
Expand Down
11 changes: 6 additions & 5 deletions cli/genpolicy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package genpolicy

import (
_ "embed"
"fmt"

"github.com/edgelesssys/contrast/internal/platforms"
)
Expand Down Expand Up @@ -35,21 +36,21 @@ type Config struct {
}

// NewConfig selects the appropriate genpolicy configuration for the target platform.
func NewConfig(platform platforms.Platform) *Config {
func NewConfig(platform platforms.Platform) (*Config, error) {
switch platform {
case platforms.AKSCloudHypervisorSNP:
return &Config{
Rules: aksRules,
Settings: aksSettings,
Bin: aksGenpolicyBin,
}
case platforms.K3sQEMUSNP, platforms.K3sQEMUTDX, platforms.RKE2QEMUTDX:
}, nil
case platforms.K3sQEMUSNP, platforms.K3sQEMUTDX, platforms.RKE2QEMUTDX, platforms.AKSPEERSNP:
return &Config{
Rules: kataRules,
Settings: kataSettings,
Bin: kataGenpolicyBin,
}
}, nil
default:
return nil
return nil, fmt.Errorf("unsupported platform %s", platform)
}
}
1 change: 1 addition & 0 deletions cli/genpolicy/genpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func New(rulesPath, settingsPath, cachePath string, bin []byte) (*Runner, error)
func (r *Runner) Run(ctx context.Context, yamlPath string, logger *slog.Logger) error {
args := []string{
"--runtime-class-names=contrast-cc",
"--runtime-class-names=kata-remote",
"--rego-rules-path=" + r.rulesPath,
"--json-settings-path=" + r.settingsPath,
"--layers-cache-file-path=" + r.cachePath,
Expand Down
13 changes: 13 additions & 0 deletions coordinator/internal/authority/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/edgelesssys/contrast/internal/atls"
"github.com/edgelesssys/contrast/internal/attestation"
"github.com/edgelesssys/contrast/internal/attestation/certcache"
atlsinsecure "github.com/edgelesssys/contrast/internal/attestation/insecure"
"github.com/edgelesssys/contrast/internal/attestation/snp"
"github.com/edgelesssys/contrast/internal/attestation/tdx"
"github.com/edgelesssys/contrast/internal/logger"
Expand Down Expand Up @@ -66,8 +67,11 @@ func (a *Authority) Credentials(reg *prometheus.Registry, issuer atls.Issuer) (*
//
// If successful, the state will be passed to gRPC as [AuthInfo].
func (c *Credentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
c.logger.Debug("ServerHandshake started")

state, err := c.getState()
if err != nil {
c.logger.Error("getting state", "err", err)
return nil, nil, fmt.Errorf("getting state: %w", err)
}

Expand All @@ -79,6 +83,7 @@ func (c *Credentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.A

opts, err := state.Manifest.SNPValidateOpts(c.kdsGetter)
if err != nil {
c.logger.Error("generating SNP validation options", "err", err)
return nil, nil, fmt.Errorf("generating SNP validation options: %w", err)
}

Expand All @@ -91,20 +96,26 @@ func (c *Credentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.A

tdxOpts, err := state.Manifest.TDXValidateOpts()
if err != nil {
c.logger.Error("generating TDX validation options", "err", err)
return nil, nil, fmt.Errorf("generating TDX validation options: %w", err)
}
for _, opt := range tdxOpts {
validators = append(validators, tdx.NewValidatorWithReportSetter(&tdx.StaticValidateOptsGenerator{Opts: opt},
logger.NewWithAttrs(logger.NewNamed(c.logger, "validator"), map[string]string{"tee-type": "tdx"}), &authInfo))
}

// TODO(@3u13r): Don't add the insecure validator for all manifests.
validators = append(validators, atlsinsecure.NewValidatorWithReportSetter(c.logger, &authInfo))

serverCfg, err := atls.CreateAttestationServerTLSConfig(c.issuer, validators, c.attestationFailuresCounter)
if err != nil {
c.logger.Error("creating server TLS config", "err", err)
return nil, nil, err
}

conn, info, err := credentials.NewTLS(serverCfg).ServerHandshake(rawConn)
if err != nil {
c.logger.Error("credentials.NewTLS.ServerHandshake", "err", err)
return nil, nil, err
}
tlsInfo, ok := info.(credentials.TLSInfo)
Expand All @@ -114,6 +125,8 @@ func (c *Credentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.A
c.logger.Error("credentials.NewTLS returned unexpected AuthInfo", "obj", info)
}

c.logger.Debug("ServerHandshake completed", "peer", tlsInfo.State.PeerCertificates[0].Subject.CommonName)

return conn, authInfo, nil
}

Expand Down
18 changes: 14 additions & 4 deletions coordinator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/edgelesssys/contrast/coordinator/history"
"github.com/edgelesssys/contrast/coordinator/internal/authority"
"github.com/edgelesssys/contrast/internal/atls"
atlsinsecure "github.com/edgelesssys/contrast/internal/attestation/insecure"
"github.com/edgelesssys/contrast/internal/grpc/atlscredentials"
"github.com/edgelesssys/contrast/internal/logger"
"github.com/edgelesssys/contrast/internal/meshapi"
Expand Down Expand Up @@ -56,7 +57,7 @@ func run() (retErr error) {
logger.Info("Coordinator started")

if err := setupMount(context.Background(), logger); err != nil {
return fmt.Errorf("setting up mount: %w", err)
logger.Error("Error setting up mount, Contrast recovery is disabled", "err", err)
}

metricsPort := os.Getenv(metricsPortEnvVar)
Expand Down Expand Up @@ -154,9 +155,18 @@ func newServerMetrics(reg *prometheus.Registry) *grpcprometheus.ServerMetrics {
}

func newGRPCServer(serverMetrics *grpcprometheus.ServerMetrics, log *slog.Logger) (*grpc.Server, error) {
issuer, err := atls.PlatformIssuer(log)
if err != nil {
return nil, fmt.Errorf("creating issuer: %w", err)
_, insecure := os.LookupEnv("COORDINATOR_INSECURE")
insecure = true

var issuer atls.Issuer
var err error
if !insecure {
issuer, err = atls.PlatformIssuer(log)
if err != nil {
return nil, fmt.Errorf("creating issuer: %w", err)
}
} else {
issuer = atlsinsecure.NewIssuer(log)
}

credentials := atlscredentials.New(issuer, atls.NoValidators, atls.NoMetrics)
Expand Down
18 changes: 15 additions & 3 deletions coordinator/meshapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package main

import (
"bytes"
"context"
"crypto/x509"
"fmt"
Expand All @@ -14,6 +15,7 @@ import (
"github.com/edgelesssys/contrast/coordinator/internal/authority"
"github.com/edgelesssys/contrast/coordinator/internal/seedengine"
"github.com/edgelesssys/contrast/internal/atls"
atlsinsecure "github.com/edgelesssys/contrast/internal/attestation/insecure"
"github.com/edgelesssys/contrast/internal/manifest"
"github.com/edgelesssys/contrast/internal/meshapi"
grpcprometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
Expand Down Expand Up @@ -108,10 +110,20 @@ func (i *meshAPIServer) NewMeshCert(ctx context.Context, _ *meshapi.NewMeshCertR
}

hostData := manifest.NewHexString(report.HostData())
entry, ok := state.Manifest.Policies[hostData]
if !ok {
return nil, status.Errorf(codes.PermissionDenied, "policy hash %s not found in manifest", hostData)
var entry manifest.PolicyEntry
// TODO(@3u13r): Once we get the policy hash via the vTPM use that to fetch the policy entry
if bytes.Equal(report.HostData(), []byte(atlsinsecure.MagicHostData)) {
for _, v := range state.Manifest.Policies {
entry = v
break
}
} else {
entry, ok = state.Manifest.Policies[hostData]
if !ok {
return nil, status.Errorf(codes.PermissionDenied, "policy hash %s not found in manifest", hostData)
}
}

dnsNames := entry.SANs

peerPubKey, err := x509.ParsePKIXPublicKey(peerPubKeyBytes)
Expand Down
12 changes: 10 additions & 2 deletions e2e/internal/contrasttest/contrasttest.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ func (ct *ContrastTest) Init(t *testing.T, resources []any) {
require.NoError(err)
require.NoError(os.WriteFile(path.Join(ct.WorkDir, "resources.yml"), buf, 0o644))

ct.installRuntime(t)
if ct.Platform == platforms.AKSPEERSNP {
t.Log("Skipping runtime installation for AKS-PEER-SNP")
} else {
ct.installRuntime(t)
}
}

// Generate runs the contrast generate command.
Expand Down Expand Up @@ -196,7 +200,7 @@ func (ct *ContrastTest) patchReferenceValues(t *testing.T, platform platforms.Pl
SNPVersion: toPtr(manifest.SVN(255)),
MicrocodeVersion: toPtr(manifest.SVN(255)),
}
case platforms.K3sQEMUSNP:
case platforms.K3sQEMUSNP, platforms.AKSPEERSNP:
// The generate command doesn't fill in all required fields when
// generating a manifest for baremetal SNP. Do that now.
for i, snp := range m.ReferenceValues.SNP {
Expand All @@ -214,6 +218,8 @@ func (ct *ContrastTest) patchReferenceValues(t *testing.T, platform platforms.Pl
tdx.MrSeam = manifest.HexString("1cc6a17ab799e9a693fac7536be61c12ee1e0fabada82d0c999e08ccee2aa86de77b0870f558c570e7ffe55d6d47fa04")
m.ReferenceValues.TDX[i] = tdx
}
default:
require.NoError(t, fmt.Errorf("unsupported platform %s", platform))
}

manifestBytes, err = json.Marshal(m)
Expand Down Expand Up @@ -367,6 +373,8 @@ func (ct *ContrastTest) FactorPlatformTimeout(timeout time.Duration) time.Durati
return timeout
case platforms.K3sQEMUSNP, platforms.K3sQEMUTDX, platforms.RKE2QEMUTDX:
return 2 * timeout
case platforms.AKSPEERSNP:
return 3 * timeout
default:
return timeout
}
Expand Down
6 changes: 5 additions & 1 deletion e2e/openssl/openssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func TestOpenSSL(t *testing.T) {
require.NoError(t, err)

resources := kuberesource.OpenSSL()
coordinator := kuberesource.CoordinatorBundle()
coordinator := kuberesource.CoordinatorBundleWith(true)

resources = append(resources, coordinator...)

Expand Down Expand Up @@ -194,6 +194,10 @@ func TestOpenSSL(t *testing.T) {
}

t.Run("coordinator recovery", func(t *testing.T) {
if platform == platforms.AKSPEERSNP {
t.Skip("coordinator recovery test is not supported on AKSPEERSNP")
}

require := require.New(t)

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) // Already long timeout, not using ct.FactorPlatformTimeout.
Expand Down
16 changes: 13 additions & 3 deletions initializer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"time"

"github.com/edgelesssys/contrast/internal/atls"
atlsinsecure "github.com/edgelesssys/contrast/internal/attestation/insecure"
"github.com/edgelesssys/contrast/internal/grpc/dialer"
"github.com/edgelesssys/contrast/internal/logger"
"github.com/edgelesssys/contrast/internal/meshapi"
Expand Down Expand Up @@ -48,16 +49,25 @@ func run() (retErr error) {
return errors.New("COORDINATOR_HOST not set")
}

_, insecure := os.LookupEnv("COORDINATOR_INSECURE")
insecure = true

ctx := context.Background()

privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return fmt.Errorf("generating key: %w", err)
}

issuer, err := atls.PlatformIssuer(log)
if err != nil {
return fmt.Errorf("creating issuer: %w", err)
var issuer atls.Issuer
if !insecure {
issuer, err = atls.PlatformIssuer(log)
if err != nil {
return fmt.Errorf("creating issuer: %w", err)
}
} else {
log.Warn("Running in insecure mode")
issuer = atlsinsecure.NewIssuer(log)
}

requestCert := func() (*meshapi.NewMeshCertResponse, error) {
Expand Down
4 changes: 2 additions & 2 deletions internal/atls/atls.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import (
"math/big"
"time"

"github.com/edgelesssys/contrast/internal/attestation"
"github.com/edgelesssys/contrast/internal/crypto"
"github.com/edgelesssys/contrast/internal/oid"
"github.com/prometheus/client_golang/prometheus"
)

Expand Down Expand Up @@ -227,7 +227,7 @@ func verifyEmbeddedReport(validators []Validator, cert *x509.Certificate, peerPu
for _, ex := range cert.Extensions {
// Optimization: Skip the extension early before heading into the m*n complexity of the validator check
// if the extension is not an attestation document.
if !attestation.IsAttestationDocumentExtension(ex.Id) {
if !oid.IsAttestationDocumentExtension(ex.Id) {
continue
}

Expand Down
45 changes: 45 additions & 0 deletions internal/attestation/insecure/issuer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2024 Edgeless Systems GmbH
// SPDX-License-Identifier: AGPL-3.0-only

package atlsinsecure

import (
"context"
"encoding/asn1"
"log/slog"

"github.com/edgelesssys/contrast/internal/attestation/reportdata"
"github.com/edgelesssys/contrast/internal/oid"
)

// Issuer issues attestation statements.
type Issuer struct {
logger *slog.Logger
}

// NewIssuer returns a new Issuer.
func NewIssuer(log *slog.Logger) *Issuer {
return &Issuer{
logger: log,
}
}

// OID returns the OID of the issuer.
func (i *Issuer) OID() asn1.ObjectIdentifier {
return oid.RawInsecureReport
}

// Issue the attestation document.
func (i *Issuer) Issue(_ context.Context, ownPublicKey []byte, nonce []byte) (res []byte, err error) {
i.logger.Info("Issue called")
defer func() {
if err != nil {
i.logger.Error("Failed to issue attestation statement", "err", err)
}
}()

reportData := reportdata.Construct(ownPublicKey, nonce)

i.logger.Info("Successfully issued attestation statement")
return reportData[:], nil
}
Loading