diff --git a/cli/cmd/common.go b/cli/cmd/common.go index 86881a4d40..6ab1d2b758 100644 --- a/cli/cmd/common.go +++ b/cli/cmd/common.go @@ -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" @@ -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 } diff --git a/cli/cmd/generate.go b/cli/cmd/generate.go index 17ba8083cb..f1deb8f4d2 100644 --- a/cli/cmd/generate.go +++ b/cli/cmd/generate.go @@ -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 { @@ -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) } diff --git a/cli/genpolicy/config.go b/cli/genpolicy/config.go index 19a5c07611..125448af2a 100644 --- a/cli/genpolicy/config.go +++ b/cli/genpolicy/config.go @@ -5,6 +5,7 @@ package genpolicy import ( _ "embed" + "fmt" "github.com/edgelesssys/contrast/internal/platforms" ) @@ -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) } } diff --git a/cli/genpolicy/genpolicy.go b/cli/genpolicy/genpolicy.go index 8fd6cbc477..aabbb59be6 100644 --- a/cli/genpolicy/genpolicy.go +++ b/cli/genpolicy/genpolicy.go @@ -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, diff --git a/coordinator/internal/authority/credentials.go b/coordinator/internal/authority/credentials.go index f250e3d375..22047dfe66 100644 --- a/coordinator/internal/authority/credentials.go +++ b/coordinator/internal/authority/credentials.go @@ -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" @@ -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) } @@ -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) } @@ -91,6 +96,7 @@ 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 { @@ -98,13 +104,18 @@ func (c *Credentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.A 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) @@ -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 } diff --git a/coordinator/main.go b/coordinator/main.go index 799b95bc75..15d803003a 100644 --- a/coordinator/main.go +++ b/coordinator/main.go @@ -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" @@ -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) @@ -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) diff --git a/coordinator/meshapi.go b/coordinator/meshapi.go index 50a0ad838b..c62d5a6799 100644 --- a/coordinator/meshapi.go +++ b/coordinator/meshapi.go @@ -4,6 +4,7 @@ package main import ( + "bytes" "context" "crypto/x509" "fmt" @@ -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" @@ -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) diff --git a/e2e/internal/contrasttest/contrasttest.go b/e2e/internal/contrasttest/contrasttest.go index 2718f58fc0..c429360afa 100644 --- a/e2e/internal/contrasttest/contrasttest.go +++ b/e2e/internal/contrasttest/contrasttest.go @@ -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. @@ -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 { @@ -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) @@ -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 } diff --git a/e2e/openssl/openssl_test.go b/e2e/openssl/openssl_test.go index a69728b010..bd7e5e31a0 100644 --- a/e2e/openssl/openssl_test.go +++ b/e2e/openssl/openssl_test.go @@ -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...) @@ -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. diff --git a/initializer/main.go b/initializer/main.go index 0c782c32ff..d910d1a8c2 100644 --- a/initializer/main.go +++ b/initializer/main.go @@ -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" @@ -48,6 +49,9 @@ 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) @@ -55,9 +59,15 @@ func run() (retErr error) { 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) { diff --git a/internal/atls/atls.go b/internal/atls/atls.go index 7196138a4e..ed70e852d1 100644 --- a/internal/atls/atls.go +++ b/internal/atls/atls.go @@ -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" ) @@ -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 } diff --git a/internal/attestation/insecure/issuer.go b/internal/attestation/insecure/issuer.go new file mode 100644 index 0000000000..e3479cc448 --- /dev/null +++ b/internal/attestation/insecure/issuer.go @@ -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 +} diff --git a/internal/attestation/insecure/validator.go b/internal/attestation/insecure/validator.go new file mode 100644 index 0000000000..4ce72b0310 --- /dev/null +++ b/internal/attestation/insecure/validator.go @@ -0,0 +1,70 @@ +// Copyright 2024 Edgeless Systems GmbH +// SPDX-License-Identifier: AGPL-3.0-only + +package atlsinsecure + +import ( + "crypto/x509/pkix" + "encoding/asn1" + "encoding/hex" + "log/slog" + + "github.com/edgelesssys/contrast/internal/attestation" + "github.com/edgelesssys/contrast/internal/oid" +) + +const MagicHostData = "insecure" + +// Validator validates attestation statements. +type Validator struct { + reportSetter attestation.ReportSetter + logger *slog.Logger +} + +// NewValidator returns a new Validator. +func NewValidator(log *slog.Logger) *Validator { + return &Validator{ + logger: log, + } +} + +// NewValidatorWithReportSetter returns a new Validator with a report setter. +func NewValidatorWithReportSetter(log *slog.Logger, reportSetter attestation.ReportSetter) *Validator { + v := NewValidator(log) + v.reportSetter = reportSetter + return v +} + +// OID returns the OID of the validator. +func (v *Validator) OID() asn1.ObjectIdentifier { + return oid.RawInsecureReport +} + +// Validate a TDX attestation. +func (v *Validator) Validate(_ []byte, nonce []byte, _ []byte) error { + v.logger.Info("Validate called", "nonce", hex.EncodeToString(nonce)) + + // Parse the attestation document. + + if v.reportSetter != nil { + // We don't know what the policy hash was that this podVM was started with, + // since it is not passed to SNP/TDX when the VM is started. + report := insecureReport{hostData: []byte(MagicHostData)} + v.reportSetter.SetReport(report) + } + + v.logger.Info("Validate finished successfully") + return nil +} + +type insecureReport struct { + hostData []byte +} + +func (i insecureReport) HostData() []byte { + return i.hostData +} + +func (i insecureReport) ClaimsToCertExtension() ([]pkix.Extension, error) { + return []pkix.Extension{}, nil +} diff --git a/internal/attestation/oid.go b/internal/attestation/oid.go deleted file mode 100644 index 97b436f32a..0000000000 --- a/internal/attestation/oid.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2024 Edgeless Systems GmbH -// SPDX-License-Identifier: AGPL-3.0-only - -package attestation - -import ( - "encoding/asn1" - - oids "github.com/edgelesssys/contrast/internal/oid" -) - -// IsAttestationDocumentExtension checks whether the given OID corresponds to an attestation document extension -// supported by Contrast (i.e. TDX or SNP). -func IsAttestationDocumentExtension(oid asn1.ObjectIdentifier) bool { - return oid.Equal(oids.RawTDXReport) || oid.Equal(oids.RawSNPReport) -} diff --git a/internal/kuberesource/mutators.go b/internal/kuberesource/mutators.go index 63ad4a1236..d27612e5bf 100644 --- a/internal/kuberesource/mutators.go +++ b/internal/kuberesource/mutators.go @@ -36,7 +36,7 @@ func AddInitializer( if meta.Annotations[skipInitializerAnnotationKey] == "true" { return meta, spec } - if spec.RuntimeClassName == nil || !strings.HasPrefix(*spec.RuntimeClassName, "contrast-cc") { + if spec.RuntimeClassName == nil || (!strings.HasPrefix(*spec.RuntimeClassName, "contrast-cc") && !strings.HasPrefix(*spec.RuntimeClassName, "kata-remote")) { return meta, spec } diff --git a/internal/kuberesource/parts.go b/internal/kuberesource/parts.go index 056aadb958..ac337f0b29 100644 --- a/internal/kuberesource/parts.go +++ b/internal/kuberesource/parts.go @@ -136,6 +136,9 @@ func NodeInstaller(namespace string, platform platforms.Platform) (*NodeInstalle nodeInstallerImageURL = "ghcr.io/edgelesssys/contrast/node-installer-kata:latest" snapshotter = nydusSnapshotter snapshotterVolumes = nydusSnapshotterVolumes + case platforms.AKSPEERSNP: + // Node installer for SNP peer pods is currently not implemented. Wait for https://github.com/edgelesssys/contrast/pull/959. + nodeInstallerImageURL = "ghcr.io/edgelesssys/contrast/node-installer-microsoft:latest" default: return nil, fmt.Errorf("unsupported platform %q", platform) } @@ -282,7 +285,7 @@ type CoordinatorConfig struct { } // Coordinator constructs a new CoordinatorConfig. -func Coordinator(namespace string) *CoordinatorConfig { +func Coordinator(namespace string, withoutState bool) *CoordinatorConfig { c := StatefulSet("coordinator", namespace). WithSpec(StatefulSetSpec(). WithReplicas(1). @@ -341,6 +344,50 @@ func Coordinator(namespace string) *CoordinatorConfig { ), ) + if withoutState { + c = StatefulSet("coordinator", namespace). + WithSpec(StatefulSetSpec(). + WithReplicas(1). + WithServiceName("coordinator"). + WithSelector(LabelSelector(). + WithMatchLabels(map[string]string{"app.kubernetes.io/name": "coordinator"}), + ). + WithTemplate(PodTemplateSpec(). + WithLabels(map[string]string{"app.kubernetes.io/name": "coordinator"}). + WithAnnotations(map[string]string{"contrast.edgeless.systems/pod-role": "coordinator"}). + WithSpec(PodSpec(). + WithContainers( + Container(). + WithName("coordinator"). + WithImage("ghcr.io/edgelesssys/contrast/coordinator:latest"). + WithSecurityContext(SecurityContext(). + WithCapabilities(applycorev1.Capabilities(). + WithAdd("SYS_ADMIN"), + ), + ). + WithPorts( + ContainerPort(). + WithName("userapi"). + WithContainerPort(1313), + ContainerPort(). + WithName("meshapi"). + WithContainerPort(7777), + ). + WithReadinessProbe(Probe(). + WithInitialDelaySeconds(1). + WithPeriodSeconds(5). + WithTCPSocket(TCPSocketAction(). + WithPort(intstr.FromInt(1313))), + ). + WithResources(ResourceRequirements(). + WithMemoryLimitAndRequest(100), + ), + ), + ), + ), + ) + } + return &CoordinatorConfig{c} } diff --git a/internal/kuberesource/parts_test.go b/internal/kuberesource/parts_test.go index 7793b422a5..7f68a75520 100644 --- a/internal/kuberesource/parts_test.go +++ b/internal/kuberesource/parts_test.go @@ -24,7 +24,7 @@ func TestNewPortForwarder(t *testing.T) { func TestCoordinator(t *testing.T) { require := require.New(t) - b, err := EncodeResources(Coordinator("default")) + b, err := EncodeResources(Coordinator("default", false)) require.NoError(err) t.Log("\n" + string(b)) } diff --git a/internal/kuberesource/sets.go b/internal/kuberesource/sets.go index 4c9010779a..98f3a5e848 100644 --- a/internal/kuberesource/sets.go +++ b/internal/kuberesource/sets.go @@ -17,7 +17,21 @@ import ( // CoordinatorBundle returns the Coordinator and a matching Service. func CoordinatorBundle() []any { - coordinatorSfSets := Coordinator("").StatefulSetApplyConfiguration + coordinatorSfSets := Coordinator("", false).StatefulSetApplyConfiguration + coordinatorService := ServiceForStatefulSet(coordinatorSfSets). + WithAnnotations(map[string]string{exposeServiceAnnotation: "true"}) + + resources := []any{ + coordinatorSfSets, + coordinatorService, + } + + return resources +} + +// CoordinatorBundleWith returns the Coordinator and a matching Service. +func CoordinatorBundleWith(withoutState bool) []any { + coordinatorSfSets := Coordinator("", withoutState).StatefulSetApplyConfiguration coordinatorService := ServiceForStatefulSet(coordinatorSfSets). WithAnnotations(map[string]string{exposeServiceAnnotation: "true"}) diff --git a/internal/manifest/runtimehandler.go b/internal/manifest/runtimehandler.go index de8134ac89..89eec08ab2 100644 --- a/internal/manifest/runtimehandler.go +++ b/internal/manifest/runtimehandler.go @@ -17,6 +17,10 @@ func RuntimeHandler(platform platforms.Platform) (string, error) { return "", fmt.Errorf("unmarshal embedded reference values mapping: %w", err) } + if platform == platforms.AKSPEERSNP { + return "kata-remote", nil + } + for runtimeHandler := range mapping { p, err := platformFromHandler(runtimeHandler) if err != nil { diff --git a/internal/oid/oid.go b/internal/oid/oid.go index f42f151651..2eb5d01bb8 100644 --- a/internal/oid/oid.go +++ b/internal/oid/oid.go @@ -12,3 +12,13 @@ var RawSNPReport = asn1.ObjectIdentifier{1, 3, 9901, 2, 1} // RawTDXReport is the root OID for the raw TDX report extensions // used by the aTLS issuer and validator. var RawTDXReport = asn1.ObjectIdentifier{1, 3, 9901, 2, 2} + +// RawInsecureReport is the root OID for the raw insecure report extensions +// used by the aTLS issuer and validator. +var RawInsecureReport = asn1.ObjectIdentifier{1, 3, 9901, 2, 3} + +// IsAttestationDocumentExtension checks whether the given OID corresponds to an attestation document extension +// supported by Contrast (i.e. TDX or SNP). +func IsAttestationDocumentExtension(oid asn1.ObjectIdentifier) bool { + return oid.Equal(RawTDXReport) || oid.Equal(RawSNPReport) || oid.Equal(RawInsecureReport) +} diff --git a/internal/platforms/platforms.go b/internal/platforms/platforms.go index 92966870c0..57ad7fc946 100644 --- a/internal/platforms/platforms.go +++ b/internal/platforms/platforms.go @@ -24,6 +24,8 @@ const ( K3sQEMUSNP // RKE2QEMUTDX represents a deployment with QEMU on bare-metal TDX RKE2. RKE2QEMUTDX + // AKSPEERSNP represents a deployment with PeerPod on SEV-SNP AKS. + AKSPEERSNP ) // All returns a list of all available platforms. @@ -51,6 +53,8 @@ func (p Platform) String() string { return "K3s-QEMU-SNP" case RKE2QEMUTDX: return "RKE2-QEMU-TDX" + case AKSPEERSNP: + return "AKS-PEER-SNP" default: return "Unknown" } @@ -67,6 +71,8 @@ func FromString(s string) (Platform, error) { return K3sQEMUSNP, nil case "rke2-qemu-tdx": return RKE2QEMUTDX, nil + case "aks-peer-snp": + return AKSPEERSNP, nil default: return Unknown, fmt.Errorf("unknown platform: %s", s) } diff --git a/packages/by-name/contrast/package.nix b/packages/by-name/contrast/package.nix index 1326394eaa..a2925b5040 100644 --- a/packages/by-name/contrast/package.nix +++ b/packages/by-name/contrast/package.nix @@ -53,6 +53,8 @@ let k3s-qemu-tdx-handler = runtimeHandler "k3s-qemu-tdx" kata.contrast-node-installer-image.runtimeHash; rke2-qemu-tdx-handler = runtimeHandler "rke2-qemu-tdx" kata.contrast-node-installer-image.runtimeHash; k3s-qemu-snp-handler = runtimeHandler "k3s-qemu-snp" kata.contrast-node-installer-image.runtimeHash; + # We currently don't have our own node installer. Wait for https://github.com/edgelesssys/contrast/pull/959. + aks-peer-snp-handler = runtimeHandler "aks-peer-snp" kata.contrast-node-installer-image.runtimeHash; aksRefVals = { snp = [ @@ -129,6 +131,9 @@ let "${k3s-qemu-tdx-handler}" = tdxRefVals; "${rke2-qemu-tdx-handler}" = tdxRefVals; "${k3s-qemu-snp-handler}" = snpRefVals; + # TODO(@3u13r): We don't currently have an implemented attestation story for SNP peer pods. + # Use the snpRefVals as a placeholder for now. + "${aks-peer-snp-handler}" = snpRefVals; } ); diff --git a/packages/by-name/kata/kata-runtime/0003-runtime-agent-verify-the-agent-policy-hash.patch b/packages/by-name/kata/kata-runtime/0003-runtime-agent-verify-the-agent-policy-hash.patch index 14246534e7..95e45342d2 100644 --- a/packages/by-name/kata/kata-runtime/0003-runtime-agent-verify-the-agent-policy-hash.patch +++ b/packages/by-name/kata/kata-runtime/0003-runtime-agent-verify-the-agent-policy-hash.patch @@ -291,7 +291,7 @@ index ccac317d0..2f1da9ecd 100644 /// Replace the Policy in regorus. pub async fn set_policy(&mut self, policy: &str) -> Result<()> { -+ verify_policy_digest(policy)?; ++ // verify_policy_digest(policy)?; self.engine = Self::new_engine(); self.engine .add_policy("agent_policy".to_string(), policy.to_string())?; diff --git a/packages/by-name/kata/kata-runtime/0008-genpolicy-settings-bump-OCI-version.patch b/packages/by-name/kata/kata-runtime/0008-genpolicy-settings-bump-OCI-version.patch index d382648f88..cbb5513145 100644 --- a/packages/by-name/kata/kata-runtime/0008-genpolicy-settings-bump-OCI-version.patch +++ b/packages/by-name/kata/kata-runtime/0008-genpolicy-settings-bump-OCI-version.patch @@ -17,7 +17,7 @@ index e50d5e545..fcafa46cc 100644 "kata_config": { "confidential_guest": false, - "oci_version": "1.1.0" -+ "oci_version": "1.2.0" ++ "oci_version": "1.1.0" }, "cluster_config": { "default_namespace": "default", diff --git a/packages/by-name/kata/kata-runtime/0015-genpolicy_netns_hack.patch b/packages/by-name/kata/kata-runtime/0015-genpolicy_netns_hack.patch new file mode 100644 index 0000000000..4382eb2f93 --- /dev/null +++ b/packages/by-name/kata/kata-runtime/0015-genpolicy_netns_hack.patch @@ -0,0 +1,15 @@ +diff --git a/src/tools/genpolicy/src/policy.rs b/src/tools/genpolicy/src/policy.rs +index 9402e87ed..1b62be7f9 100644 +--- a/src/tools/genpolicy/src/policy.rs ++++ b/src/tools/genpolicy/src/policy.rs +@@ -982,5 +982,10 @@ pub fn get_kata_namespaces( + Path: "".to_string(), + }); + ++ namespaces.push(KataLinuxNamespace { ++ Type: "network".to_string(), ++ Path: "/run/netns/podns".to_string(), ++ }); ++ + namespaces + } diff --git a/packages/by-name/kata/kata-runtime/package.nix b/packages/by-name/kata/kata-runtime/package.nix index 73868bc915..c450443c5b 100644 --- a/packages/by-name/kata/kata-runtime/package.nix +++ b/packages/by-name/kata/kata-runtime/package.nix @@ -90,6 +90,8 @@ buildGoModule rec { # The patch is not sufficient for upstream, because it requires the extraRootFs content from # our Nix packaging. ./0014-tools-don-t-clean-build-root-when-generating-rootfs.patch + + ./0015-genpolicy_netns_hack.patch ]; };