├── .github └── workflows │ ├── checks.yml │ └── release.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── attested-get │ └── main.go ├── dummy-server │ └── main.go ├── proxy-client │ └── main.go └── proxy-server │ └── main.go ├── common ├── logging.go └── vars.go ├── go.mod ├── go.sum ├── internal ├── README.md ├── api │ ├── attestationconfigapi │ │ ├── BUILD.bazel │ │ ├── attestationconfigapi.go │ │ ├── cli │ │ │ ├── BUILD.bazel │ │ │ ├── client │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── client.go │ │ │ │ ├── client_test.go │ │ │ │ ├── reporter.go │ │ │ │ └── reporter_test.go │ │ │ ├── compare.go │ │ │ ├── delete.go │ │ │ ├── e2e │ │ │ │ └── test.sh.in │ │ │ ├── main.go │ │ │ ├── upload.go │ │ │ └── validargs.go │ │ ├── fetcher.go │ │ ├── fetcher_test.go │ │ ├── version.go │ │ └── version_test.go │ ├── client │ │ ├── BUILD.bazel │ │ └── client.go │ ├── fetcher │ │ ├── BUILD.bazel │ │ └── fetcher.go │ └── versionsapi │ │ ├── BUILD.bazel │ │ ├── apiconstants.go │ │ ├── cli │ │ ├── BUILD.bazel │ │ ├── add.go │ │ ├── latest.go │ │ ├── list.go │ │ ├── main.go │ │ └── rm.go │ │ ├── client.go │ │ ├── cliinfo.go │ │ ├── cliinfo_test.go │ │ ├── fetcher.go │ │ ├── fetcher_test.go │ │ ├── imageinfo.go │ │ ├── imageinfo_test.go │ │ ├── latest.go │ │ ├── latest_test.go │ │ ├── list.go │ │ ├── list_test.go │ │ ├── version.go │ │ ├── version_test.go │ │ └── versionsapi.go ├── atls │ ├── BUILD.bazel │ ├── README.md │ ├── atls.go │ └── atls_test.go ├── attestation │ ├── BUILD.bazel │ ├── attestation.go │ ├── attestation_test.go │ ├── aws │ │ ├── BUILD.bazel │ │ ├── aws.go │ │ ├── nitrotpm │ │ │ ├── BUILD.bazel │ │ │ ├── issuer.go │ │ │ ├── issuer_test.go │ │ │ ├── nitrotpm.go │ │ │ ├── validator.go │ │ │ └── validator_test.go │ │ └── snp │ │ │ ├── BUILD.bazel │ │ │ ├── errors.go │ │ │ ├── issuer.go │ │ │ ├── issuer_test.go │ │ │ ├── snp.go │ │ │ ├── testdata │ │ │ ├── BUILD.bazel │ │ │ ├── certchain.pem │ │ │ ├── report.txt │ │ │ ├── testdata.go │ │ │ └── vlek.pem │ │ │ ├── validator.go │ │ │ └── validator_test.go │ ├── azure │ │ ├── BUILD.bazel │ │ ├── azure.go │ │ ├── azure_test.go │ │ ├── snp │ │ │ ├── BUILD.bazel │ │ │ ├── imds.go │ │ │ ├── issuer.go │ │ │ ├── issuer_test.go │ │ │ ├── maa.go │ │ │ ├── snp.go │ │ │ ├── validator.go │ │ │ └── validator_test.go │ │ ├── tdx │ │ │ ├── BUILD.bazel │ │ │ ├── issuer.go │ │ │ ├── issuer_test.go │ │ │ ├── tdx.go │ │ │ ├── testdata │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── hclreport.bin │ │ │ │ └── testdata.go │ │ │ └── validator.go │ │ └── trustedlaunch │ │ │ ├── BUILD.bazel │ │ │ ├── issuer.go │ │ │ ├── trustedlaunch.go │ │ │ ├── trustedlaunch_test.go │ │ │ └── validator.go │ ├── choose │ │ ├── BUILD.bazel │ │ ├── choose.go │ │ └── choose_test.go │ ├── gcp │ │ ├── BUILD.bazel │ │ ├── es │ │ │ ├── BUILD.bazel │ │ │ ├── es.go │ │ │ ├── issuer.go │ │ │ ├── issuer_test.go │ │ │ ├── validator.go │ │ │ └── validator_test.go │ │ ├── gcp.go │ │ ├── metadata.go │ │ ├── restclient.go │ │ └── snp │ │ │ ├── BUILD.bazel │ │ │ ├── issuer.go │ │ │ ├── snp.go │ │ │ └── validator.go │ ├── idkeydigest │ │ ├── BUILD.bazel │ │ ├── enforcement_string.go │ │ ├── idkeydigest.go │ │ └── idkeydigest_test.go │ ├── initialize │ │ ├── BUILD.bazel │ │ ├── initialize.go │ │ └── initialize_test.go │ ├── measurements │ │ ├── BUILD.bazel │ │ ├── fetchmeasurements.go │ │ ├── fetchmeasurements_test.go │ │ ├── measurement-generator │ │ │ ├── BUILD.bazel │ │ │ ├── generate.go │ │ │ └── generate_test.go │ │ ├── measurements.go │ │ ├── measurements_enterprise.go │ │ ├── measurements_oss.go │ │ ├── measurements_test.go │ │ └── overrides.go │ ├── qemu │ │ ├── BUILD.bazel │ │ ├── issuer.go │ │ ├── qemu.go │ │ └── validator.go │ ├── simulator │ │ ├── BUILD.bazel │ │ ├── simulator.go │ │ └── simulator_disabled.go │ ├── snp │ │ ├── BUILD.bazel │ │ ├── snp.go │ │ ├── snp_test.go │ │ └── testdata │ │ │ ├── BUILD.bazel │ │ │ ├── attestation.bin │ │ │ ├── certchain.pem │ │ │ ├── runtimedata.bin │ │ │ ├── testdata.go │ │ │ ├── vcek.cert │ │ │ ├── vcek.pem │ │ │ ├── vlek.pem │ │ │ └── vlekcertchain.pem │ ├── tdx │ │ ├── BUILD.bazel │ │ ├── issuer.go │ │ ├── tdx.go │ │ └── validator.go │ ├── variant │ │ ├── BUILD.bazel │ │ └── variant.go │ └── vtpm │ │ ├── BUILD.bazel │ │ ├── attestation.go │ │ ├── attestation_test.go │ │ ├── vtpm.go │ │ └── vtpm_test.go ├── cloud │ ├── BUILD.bazel │ ├── aws │ │ ├── BUILD.bazel │ │ ├── aws.go │ │ └── aws_test.go │ ├── azure │ │ ├── BUILD.bazel │ │ ├── azure.go │ │ ├── azure_test.go │ │ ├── imds.go │ │ ├── imds_test.go │ │ ├── interface.go │ │ ├── iptables_cross.go │ │ └── iptables_linux.go │ ├── azureshared │ │ ├── BUILD.bazel │ │ ├── appcredentials.go │ │ ├── appcredentials_test.go │ │ ├── authmethod_string.go │ │ ├── azureshared.go │ │ ├── metadata.go │ │ └── metadata_test.go │ ├── cloud.go │ ├── cloudprovider │ │ ├── BUILD.bazel │ │ ├── cloudprovider.go │ │ ├── cloudprovider_test.go │ │ └── provider_string.go │ ├── gcp │ │ ├── BUILD.bazel │ │ ├── gcp.go │ │ ├── gcp_test.go │ │ ├── interface.go │ │ └── wrappers.go │ ├── gcpshared │ │ ├── BUILD.bazel │ │ ├── gcpshared.go │ │ ├── providerid.go │ │ ├── providerid_test.go │ │ ├── serviceaccountkey.go │ │ └── serviceaccountkey_test.go │ ├── metadata │ │ ├── BUILD.bazel │ │ └── metadata.go │ ├── openstack │ │ ├── BUILD.bazel │ │ ├── accountkey.go │ │ ├── accountkey_test.go │ │ ├── api.go │ │ ├── api_test.go │ │ ├── clouds │ │ │ ├── BUILD.bazel │ │ │ ├── LICENSE │ │ │ ├── clouds.go │ │ │ └── read.go │ │ ├── imds.go │ │ ├── imds_test.go │ │ ├── openstack.go │ │ ├── openstack_test.go │ │ ├── plumbing.go │ │ ├── plumbing_test.go │ │ └── wrappers.go │ └── qemu │ │ ├── BUILD.bazel │ │ └── qemu.go ├── compatibility │ ├── BUILD.bazel │ ├── compatibility.go │ └── compatibility_test.go ├── config │ ├── BUILD.bazel │ ├── attestation.go │ ├── attestation_test.go │ ├── attestationversion.go │ ├── attestationversion_test.go │ ├── aws.go │ ├── azure.go │ ├── config.go │ ├── config_doc.go │ ├── config_test.go │ ├── disktypes │ │ ├── BUILD.bazel │ │ ├── aws.go │ │ ├── azure.go │ │ └── gcp.go │ ├── gcp.go │ ├── image_enterprise.go │ ├── image_oss.go │ ├── imageversion │ │ ├── BUILD.bazel │ │ ├── imageversion.go │ │ └── placeholder.go │ ├── instancetypes │ │ ├── BUILD.bazel │ │ ├── aws.go │ │ ├── azure.go │ │ ├── gcp.go │ │ └── stackit.go │ ├── migration │ │ ├── BUILD.bazel │ │ └── migration.go │ ├── testdata │ │ ├── configAWSV2.yaml │ │ ├── configAzureV2MultipleIDKeyDigest.yaml │ │ ├── configAzureV2SingleIDKeyDigest.yaml │ │ └── configGCPV2.yaml │ ├── validation.go │ └── validation_test.go ├── constants │ ├── BUILD.bazel │ ├── constants.go │ ├── enterprise.go │ └── oss.go ├── containerimage │ ├── BUILD.bazel │ └── containerimage.go ├── crypto │ ├── BUILD.bazel │ ├── crypto.go │ ├── crypto_test.go │ └── testvector │ │ ├── BUILD.bazel │ │ └── testvector.go ├── cryptsetup │ ├── BUILD.bazel │ ├── cryptsetup.go │ ├── cryptsetup_cgo.go │ └── cryptsetup_cross.go ├── encoding │ ├── BUILD.bazel │ ├── encoding.go │ └── encoding_test.go ├── file │ ├── BUILD.bazel │ ├── file.go │ └── file_test.go ├── grpc │ ├── atlscredentials │ │ ├── BUILD.bazel │ │ ├── atlscredentials.go │ │ └── atlscredentials_test.go │ ├── dialer │ │ ├── BUILD.bazel │ │ ├── dialer.go │ │ └── dialer_test.go │ ├── grpclog │ │ ├── BUILD.bazel │ │ ├── grpclog.go │ │ └── grpclog_test.go │ ├── retry │ │ ├── BUILD.bazel │ │ ├── retry.go │ │ └── retry_test.go │ └── testdialer │ │ ├── BUILD.bazel │ │ └── testdialer.go ├── imagefetcher │ ├── BUILD.bazel │ ├── imagefetcher.go │ ├── imagefetcher_test.go │ ├── raw.go │ └── raw_test.go ├── installer │ ├── BUILD.bazel │ ├── installer.go │ └── installer_test.go ├── kms │ ├── README.md │ ├── config │ │ ├── BUILD.bazel │ │ └── config.go │ ├── kms │ │ ├── BUILD.bazel │ │ ├── aws │ │ │ ├── BUILD.bazel │ │ │ └── aws.go │ │ ├── azure │ │ │ ├── BUILD.bazel │ │ │ └── azure.go │ │ ├── cluster │ │ │ ├── BUILD.bazel │ │ │ ├── cluster.go │ │ │ └── cluster_test.go │ │ ├── gcp │ │ │ ├── BUILD.bazel │ │ │ └── gcp.go │ │ ├── internal │ │ │ ├── BUILD.bazel │ │ │ ├── internal.go │ │ │ └── internal_test.go │ │ └── kms.go │ ├── setup │ │ ├── BUILD.bazel │ │ ├── setup.go │ │ └── setup_test.go │ ├── storage │ │ ├── BUILD.bazel │ │ ├── awss3 │ │ │ ├── BUILD.bazel │ │ │ ├── awss3.go │ │ │ └── awss3_test.go │ │ ├── azureblob │ │ │ ├── BUILD.bazel │ │ │ ├── azureblob.go │ │ │ └── azureblob_test.go │ │ ├── gcs │ │ │ ├── BUILD.bazel │ │ │ ├── gcs.go │ │ │ └── gcs_test.go │ │ ├── memfs │ │ │ ├── BUILD.bazel │ │ │ ├── memfs.go │ │ │ └── memfs_test.go │ │ └── storage.go │ ├── test │ │ ├── BUILD.bazel │ │ ├── aws_test.go │ │ ├── azure_test.go │ │ ├── gcp_test.go │ │ └── integration_test.go │ └── uri │ │ ├── BUILD.bazel │ │ ├── uri.go │ │ └── uri_test.go ├── kubernetes │ ├── BUILD.bazel │ ├── configmaps.go │ ├── configmaps_test.go │ ├── kubectl │ │ ├── BUILD.bazel │ │ ├── kubectl.go │ │ └── kubectl_test.go │ ├── kubernetes.go │ ├── marshal.go │ ├── marshal_test.go │ ├── secrets.go │ └── secrets_test.go ├── license │ ├── BUILD.bazel │ ├── checker_enterprise.go │ ├── checker_enterprise_test.go │ ├── checker_oss.go │ ├── file.go │ ├── file_test.go │ ├── integration │ │ ├── BUILD.bazel │ │ └── license_integration_test.go │ └── license.go ├── logger │ ├── BUILD.bazel │ ├── cmdline.go │ ├── grpclogger.go │ ├── levelhandler.go │ └── log.go ├── maa │ ├── BUILD.bazel │ ├── maa.go │ ├── patch.go │ └── patch_test.go ├── mpimage │ ├── BUILD.bazel │ ├── mpimage.go │ ├── uri.go │ └── uri_test.go ├── nodestate │ ├── BUILD.bazel │ ├── nodestate.go │ └── nodestate_test.go ├── osimage │ ├── BUILD.bazel │ ├── archive │ │ ├── BUILD.bazel │ │ └── archive.go │ ├── imageinfo │ │ ├── BUILD.bazel │ │ └── imageinfo.go │ ├── measurementsuploader │ │ ├── BUILD.bazel │ │ └── measurementsuploader.go │ ├── nop │ │ ├── BUILD.bazel │ │ └── nop.go │ ├── osimage.go │ ├── secureboot │ │ ├── BUILD.bazel │ │ ├── secureboot.go │ │ ├── secureboot_test.go │ │ └── zlibdict.go │ └── uplosi │ │ ├── BUILD.bazel │ │ ├── uplosi.conf.in │ │ └── uplosiupload.go ├── retry │ ├── BUILD.bazel │ ├── retry.go │ └── retry_test.go ├── role │ ├── BUILD.bazel │ ├── role.go │ ├── role_string.go │ └── role_test.go ├── semver │ ├── BUILD.bazel │ ├── semver.go │ └── semver_test.go ├── sigstore │ ├── BUILD.bazel │ ├── keyselect │ │ ├── BUILD.bazel │ │ └── keyselect.go │ ├── rekor.go │ ├── rekor_integration_test.go │ ├── rekor_test.go │ ├── sign.go │ ├── sign_test.go │ ├── sigstore.go │ ├── verify.go │ └── verify_test.go ├── staticupload │ ├── BUILD.bazel │ ├── delete.go │ ├── get.go │ ├── staticupload.go │ ├── staticupload_test.go │ └── upload.go ├── validation │ ├── BUILD.bazel │ ├── constraints.go │ ├── constraints_test.go │ ├── errors.go │ ├── errors_test.go │ ├── validation.go │ └── validation_test.go ├── verify │ ├── BUILD.bazel │ ├── verify.go │ └── verify_test.go └── versions │ ├── BUILD.bazel │ ├── components │ ├── BUILD.bazel │ ├── components.go │ ├── components.pb.go │ ├── components.proto │ └── components_test.go │ ├── hash-generator │ ├── BUILD.bazel │ ├── generate.go │ └── generate_test.go │ ├── versions.go │ └── versions_test.go ├── measurements.json ├── multimeasurements ├── multimeasurements.go └── multimeasurements_test.go ├── proxy-server.dockerfile ├── proxy ├── atls_config.go ├── mutli_validator.go ├── proxy.go └── proxy_test.go ├── staticcheck.conf └── tdx ├── issuer.go ├── remote_quote_provider.go └── validator.go /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | name: Checks 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | test: 11 | name: Test 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Set up Go 15 | uses: actions/setup-go@v3 16 | with: 17 | go-version: ^1.23 18 | id: go 19 | 20 | - name: Check out code into the Go module directory 21 | uses: actions/checkout@v2 22 | 23 | - name: Run unit tests and generate the coverage report 24 | run: make test-race 25 | 26 | lint: 27 | name: Lint 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Set up Go 31 | uses: actions/setup-go@v3 32 | with: 33 | go-version: ^1.23 34 | id: go 35 | 36 | - name: Check out code into the Go module directory 37 | uses: actions/checkout@v2 38 | 39 | - name: Install gofumpt 40 | run: go install mvdan.cc/gofumpt@v0.4.0 41 | 42 | - name: Install staticcheck 43 | run: go install honnef.co/go/tools/cmd/staticcheck@2024.1.1 44 | 45 | - name: Install golangci-lint 46 | run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.61.0 47 | 48 | - name: Lint 49 | run: make lint 50 | 51 | - name: Ensure go mod tidy runs without changes 52 | run: | 53 | go mod tidy 54 | git update-index -q --really-refresh 55 | git diff-index HEAD 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /constellation/ 2 | /cvm-reverse-proxy 3 | /measurements.json 4 | /build/ 5 | /quotes/ 6 | /builder-cert.pem -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.24rc2-bullseye@sha256:236da40764c1bcf469fcaf6ca225ca881c3f06cbd1934e392d6e4af3484f6cac AS builder 2 | 3 | ARG BINARY=proxy-client 4 | WORKDIR /app 5 | COPY ./ /app 6 | RUN make build-${BINARY} 7 | 8 | FROM gcr.io/distroless/cc-debian12:nonroot-6755e21ccd99ddead6edc8106ba03888cbeed41a 9 | ARG BINARY 10 | COPY --from=builder /app/build/${BINARY} /app 11 | ENTRYPOINT [ "/app" ] 12 | -------------------------------------------------------------------------------- /cmd/dummy-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | func getRoot(w http.ResponseWriter, r *http.Request) { 10 | // Print all headers 11 | log.Println("Received request with headers:") 12 | for name, values := range r.Header { 13 | for _, value := range values { 14 | log.Printf(" %s: %s", name, value) 15 | } 16 | } 17 | 18 | _, _ = io.WriteString(w, "checkcheck\n") 19 | } 20 | 21 | func main() { 22 | listenAddr := ":8085" 23 | http.HandleFunc("/", getRoot) 24 | 25 | log.Printf("Starting dummy server on %s", listenAddr) 26 | log.Fatal(http.ListenAndServe(listenAddr, nil)) 27 | } 28 | -------------------------------------------------------------------------------- /common/logging.go: -------------------------------------------------------------------------------- 1 | // Package common contains shared utilities 2 | package common 3 | 4 | import ( 5 | "log/slog" 6 | "os" 7 | ) 8 | 9 | type LoggingOpts struct { 10 | Debug bool 11 | JSON bool 12 | Service string 13 | Version string 14 | } 15 | 16 | func SetupLogger(opts *LoggingOpts) (log *slog.Logger) { 17 | logLevel := slog.LevelInfo 18 | if opts.Debug { 19 | logLevel = slog.LevelDebug 20 | } 21 | 22 | if opts.JSON { 23 | log = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: logLevel})) 24 | } else { 25 | log = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: logLevel})) 26 | } 27 | 28 | if opts.Service != "" { 29 | log = log.With("service", opts.Service) 30 | } 31 | 32 | if opts.Version != "" { 33 | log = log.With("version", opts.Version) 34 | } 35 | 36 | return log 37 | } 38 | -------------------------------------------------------------------------------- /common/vars.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | var Version = "dev" 4 | -------------------------------------------------------------------------------- /internal/README.md: -------------------------------------------------------------------------------- 1 | Files in this directory are copied from https://github.com/edgelesssys/constellation -------------------------------------------------------------------------------- /internal/api/attestationconfigapi/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "attestationconfigapi", 6 | srcs = [ 7 | "attestationconfigapi.go", 8 | "fetcher.go", 9 | "version.go", 10 | ], 11 | importpath = "cvm-reverse-proxy/internal/api/attestationconfigapi", 12 | visibility = ["//:__subpackages__"], 13 | deps = [ 14 | "//internal/api/fetcher", 15 | "//internal/attestation/variant", 16 | "//internal/constants", 17 | "//internal/sigstore", 18 | ], 19 | ) 20 | 21 | go_test( 22 | name = "attestationconfigapi_test", 23 | srcs = [ 24 | "fetcher_test.go", 25 | "version_test.go", 26 | ], 27 | embed = [":attestationconfigapi"], 28 | deps = [ 29 | "//internal/attestation/variant", 30 | "//internal/constants", 31 | "@com_github_stretchr_testify//assert", 32 | "@com_github_stretchr_testify//require", 33 | ], 34 | ) 35 | -------------------------------------------------------------------------------- /internal/api/attestationconfigapi/attestationconfigapi.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | # AttestationConfig API 9 | 10 | The AttestationConfig API provides values for the attestation key in the Constellation config. 11 | 12 | This package defines API types that represents objects of the AttestationConfig API. 13 | The types provide helper methods for validation and commonly used operations on the 14 | information contained in the objects. Especially the paths used for the API are defined 15 | in these helper methods. 16 | 17 | Regarding the decision to implement new types over using the existing types from internal/config: 18 | AttestationCfg objects for AttestationCfg API need to hold some version information (for sorting, recognizing latest). 19 | Thus, existing config types (AWSNitroTPM, AzureSEVSNP, ...) can not be extended to implement apiObject interface. 20 | Instead, we need a separate type that wraps _all_ attestation types. In the codebase this is done using the AttestationCfg interface. 21 | The new type AttestationCfgGet needs to be located inside internal/config in order to implement UnmarshalJSON. 22 | */ 23 | package attestationconfigapi 24 | -------------------------------------------------------------------------------- /internal/api/attestationconfigapi/cli/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 2 | load("//bazel/sh:def.bzl", "sh_template") 3 | 4 | go_binary( 5 | name = "cli", 6 | embed = [":cli_lib"], 7 | visibility = ["//visibility:public"], 8 | ) 9 | 10 | go_library( 11 | name = "cli_lib", 12 | srcs = [ 13 | "compare.go", 14 | "delete.go", 15 | "main.go", 16 | "upload.go", 17 | "validargs.go", 18 | ], 19 | importpath = "cvm-reverse-proxy/internal/api/attestationconfigapi/cli", 20 | visibility = ["//visibility:private"], 21 | deps = [ 22 | "//internal/api/attestationconfigapi", 23 | "//internal/api/attestationconfigapi/cli/client", 24 | "//internal/api/fetcher", 25 | "//internal/attestation/variant", 26 | "//internal/constants", 27 | "//internal/file", 28 | "//internal/logger", 29 | "//internal/staticupload", 30 | "//internal/verify", 31 | "@com_github_aws_aws_sdk_go_v2//aws", 32 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 33 | "@com_github_aws_aws_sdk_go_v2_service_s3//types", 34 | "@com_github_google_go_tdx_guest//proto/tdx", 35 | "@com_github_spf13_afero//:afero", 36 | "@com_github_spf13_cobra//:cobra", 37 | ], 38 | ) 39 | 40 | sh_template( 41 | name = "cli_e2e_test", 42 | data = [":cli"], 43 | substitutions = { 44 | "@@CONFIGAPI_CLI@@": "$(rootpath :cli)", 45 | }, 46 | template = "e2e/test.sh.in", 47 | ) 48 | -------------------------------------------------------------------------------- /internal/api/attestationconfigapi/cli/client/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "client", 6 | srcs = [ 7 | "client.go", 8 | "reporter.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/api/attestationconfigapi/cli/client", 11 | visibility = ["//:__subpackages__"], 12 | deps = [ 13 | "//internal/api/attestationconfigapi", 14 | "//internal/api/client", 15 | "//internal/attestation/variant", 16 | "//internal/sigstore", 17 | "//internal/staticupload", 18 | "@com_github_aws_aws_sdk_go//aws", 19 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 20 | ], 21 | ) 22 | 23 | go_test( 24 | name = "client_test", 25 | srcs = [ 26 | "client_test.go", 27 | "reporter_test.go", 28 | ], 29 | embed = [":client"], 30 | deps = [ 31 | "//internal/api/attestationconfigapi", 32 | "@com_github_stretchr_testify//assert", 33 | ], 34 | ) 35 | -------------------------------------------------------------------------------- /internal/api/attestationconfigapi/cli/client/client_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | package client 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/flashbots/cvm-reverse-proxy/internal/api/attestationconfigapi" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestDeleteAzureSEVSNPVersions(t *testing.T) { 17 | sut := Client{ 18 | bucketID: "bucket", 19 | } 20 | versions := attestationconfigapi.List{List: []string{"2023-01-01.json", "2021-01-01.json", "2019-01-01.json"}} 21 | 22 | ops, err := sut.deleteVersion(versions, "2021-01-01") 23 | 24 | assert := assert.New(t) 25 | assert.NoError(err) 26 | assert.Contains(ops, deleteCmd{ 27 | apiObject: attestationconfigapi.Entry{ 28 | Version: "2021-01-01.json", 29 | }, 30 | }) 31 | 32 | assert.Contains(ops, putCmd{ 33 | apiObject: attestationconfigapi.List{List: []string{"2023-01-01.json", "2019-01-01.json"}}, 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /internal/api/attestationconfigapi/cli/validargs.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package main 8 | 9 | import ( 10 | "errors" 11 | "fmt" 12 | "strings" 13 | 14 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/variant" 15 | 16 | "github.com/spf13/cobra" 17 | ) 18 | 19 | func arg0isAttestationVariant() cobra.PositionalArgs { 20 | return func(_ *cobra.Command, args []string) error { 21 | attestationVariant, err := variant.FromString(args[0]) 22 | if err != nil { 23 | return errors.New("argument 0 isn't a valid attestation variant") 24 | } 25 | switch attestationVariant { 26 | case variant.AWSSEVSNP{}, variant.AzureSEVSNP{}, variant.AzureTDX{}, variant.GCPSEVSNP{}: 27 | return nil 28 | default: 29 | return errors.New("argument 0 isn't a supported attestation variant") 30 | } 31 | } 32 | } 33 | 34 | func isValidKind(arg int) cobra.PositionalArgs { 35 | return func(_ *cobra.Command, args []string) error { 36 | if kind := kindFromString(args[arg]); kind == unknown { 37 | return fmt.Errorf("argument %s isn't a valid kind: must be one of [%q, %q]", args[arg], attestationReport, guestFirmware) 38 | } 39 | return nil 40 | } 41 | } 42 | 43 | // objectKind encodes the available actions. 44 | type objectKind string 45 | 46 | const ( 47 | // unknown is the default objectKind and does nothing. 48 | unknown objectKind = "unknown-kind" 49 | attestationReport objectKind = "attestation-report" 50 | guestFirmware objectKind = "guest-firmware" 51 | ) 52 | 53 | func kindFromString(s string) objectKind { 54 | lower := strings.ToLower(s) 55 | switch objectKind(lower) { 56 | case attestationReport, guestFirmware: 57 | return objectKind(lower) 58 | default: 59 | return unknown 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /internal/api/client/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "client", 5 | srcs = ["client.go"], 6 | importpath = "cvm-reverse-proxy/internal/api/client", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/sigstore", 10 | "//internal/staticupload", 11 | "@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager", 12 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 13 | "@com_github_aws_aws_sdk_go_v2_service_s3//types", 14 | ], 15 | ) 16 | -------------------------------------------------------------------------------- /internal/api/fetcher/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "fetcher", 5 | srcs = ["fetcher.go"], 6 | importpath = "cvm-reverse-proxy/internal/api/fetcher", 7 | visibility = ["//:__subpackages__"], 8 | deps = ["//internal/sigstore"], 9 | ) 10 | -------------------------------------------------------------------------------- /internal/api/versionsapi/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "versionsapi", 6 | srcs = [ 7 | "apiconstants.go", 8 | "client.go", 9 | "cliinfo.go", 10 | "fetcher.go", 11 | "imageinfo.go", 12 | "latest.go", 13 | "list.go", 14 | "version.go", 15 | "versionsapi.go", 16 | ], 17 | importpath = "cvm-reverse-proxy/internal/api/versionsapi", 18 | visibility = ["//:__subpackages__"], 19 | deps = [ 20 | "//internal/api/client", 21 | "//internal/api/fetcher", 22 | "//internal/constants", 23 | "@org_golang_x_mod//semver", 24 | ], 25 | ) 26 | 27 | go_test( 28 | name = "versionsapi_test", 29 | srcs = [ 30 | "cliinfo_test.go", 31 | "fetcher_test.go", 32 | "imageinfo_test.go", 33 | "latest_test.go", 34 | "list_test.go", 35 | "version_test.go", 36 | ], 37 | embed = [":versionsapi"], 38 | deps = [ 39 | "//internal/cloud/cloudprovider", 40 | "//internal/constants", 41 | "@com_github_stretchr_testify//assert", 42 | "@com_github_stretchr_testify//require", 43 | "@org_uber_go_goleak//:goleak", 44 | ], 45 | ) 46 | -------------------------------------------------------------------------------- /internal/api/versionsapi/apiconstants.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package versionsapi 8 | 9 | var ( 10 | // APIV1 is the v1 API version. 11 | APIV1 = apiVersion{slug: "v1"} 12 | // APIV2 is the v2 API version. 13 | APIV2 = apiVersion{slug: "v2"} 14 | ) 15 | 16 | type apiVersion struct { 17 | slug string 18 | } 19 | 20 | func (v apiVersion) String() string { 21 | return v.slug 22 | } 23 | -------------------------------------------------------------------------------- /internal/api/versionsapi/cli/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 2 | 3 | go_library( 4 | name = "cli_lib", 5 | srcs = [ 6 | "add.go", 7 | "latest.go", 8 | "list.go", 9 | "main.go", 10 | "rm.go", 11 | ], 12 | importpath = "cvm-reverse-proxy/internal/api/versionsapi/cli", 13 | visibility = ["//visibility:private"], 14 | deps = [ 15 | "//internal/api/client", 16 | "//internal/api/versionsapi", 17 | "//internal/constants", 18 | "//internal/logger", 19 | "@com_github_aws_aws_sdk_go_v2_config//:config", 20 | "@com_github_aws_aws_sdk_go_v2_service_ec2//:ec2", 21 | "@com_github_aws_smithy_go//:smithy-go", 22 | "@com_github_azure_azure_sdk_for_go_sdk_azcore//runtime", 23 | "@com_github_azure_azure_sdk_for_go_sdk_azidentity//:azidentity", 24 | "@com_github_azure_azure_sdk_for_go_sdk_resourcemanager_compute_armcompute_v6//:armcompute", 25 | "@com_github_googleapis_gax_go_v2//:gax-go", 26 | "@com_github_spf13_cobra//:cobra", 27 | "@com_google_cloud_go_compute//apiv1", 28 | "@com_google_cloud_go_compute//apiv1/computepb", 29 | "@org_golang_x_mod//semver", 30 | ], 31 | ) 32 | 33 | go_binary( 34 | name = "cli", 35 | embed = [":cli_lib"], 36 | visibility = ["//:__subpackages__"], 37 | ) 38 | -------------------------------------------------------------------------------- /internal/api/versionsapi/fetcher.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package versionsapi 8 | 9 | import ( 10 | "context" 11 | 12 | "github.com/flashbots/cvm-reverse-proxy/internal/api/fetcher" 13 | "github.com/flashbots/cvm-reverse-proxy/internal/constants" 14 | ) 15 | 16 | // Fetcher fetches version API resources without authentication. 17 | type Fetcher struct { 18 | fetcher.HTTPClient 19 | cdnURL string 20 | } 21 | 22 | // NewFetcher returns a new Fetcher. 23 | func NewFetcher() *Fetcher { 24 | return &Fetcher{fetcher.NewHTTPClient(), constants.CDNRepositoryURL} 25 | } 26 | 27 | // FetchVersionList fetches the given version list from the versions API. 28 | func (f *Fetcher) FetchVersionList(ctx context.Context, list List) (List, error) { 29 | return fetcher.Fetch(ctx, f.HTTPClient, f.cdnURL, list) 30 | } 31 | 32 | // FetchVersionLatest fetches the latest version from the versions API. 33 | func (f *Fetcher) FetchVersionLatest(ctx context.Context, latest Latest) (Latest, error) { 34 | return fetcher.Fetch(ctx, f.HTTPClient, f.cdnURL, latest) 35 | } 36 | 37 | // FetchImageInfo fetches the given image info from the versions API. 38 | func (f *Fetcher) FetchImageInfo(ctx context.Context, imageInfo ImageInfo) (ImageInfo, error) { 39 | return fetcher.Fetch(ctx, f.HTTPClient, f.cdnURL, imageInfo) 40 | } 41 | 42 | // FetchCLIInfo fetches the given cli info from the versions API. 43 | func (f *Fetcher) FetchCLIInfo(ctx context.Context, cliInfo CLIInfo) (CLIInfo, error) { 44 | return fetcher.Fetch(ctx, f.HTTPClient, f.cdnURL, cliInfo) 45 | } 46 | -------------------------------------------------------------------------------- /internal/api/versionsapi/versionsapi.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | # Versions API 9 | 10 | The Versions API provides information about versions of Constellation components. 11 | 12 | This package defines API types that represents objects of the versions API. 13 | The types provide helper methods for validation and commonly used operations on the 14 | information contained in the objects. Especially the paths used for the API are defined 15 | in these helper methods. 16 | 17 | The package also provides helper functions that can be used in context of the versions API, 18 | e.g. to validate versions. 19 | */ 20 | package versionsapi 21 | -------------------------------------------------------------------------------- /internal/atls/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "atls", 6 | srcs = ["atls.go"], 7 | importpath = "cvm-reverse-proxy/internal/atls", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/attestation/variant", 11 | "//internal/crypto", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "atls_test", 17 | srcs = ["atls_test.go"], 18 | embed = [":atls"], 19 | deps = [ 20 | "//internal/attestation/variant", 21 | "@com_github_stretchr_testify//assert", 22 | "@com_github_stretchr_testify//require", 23 | "@org_uber_go_goleak//:goleak", 24 | ], 25 | ) 26 | -------------------------------------------------------------------------------- /internal/attestation/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "attestation", 6 | srcs = ["attestation.go"], 7 | importpath = "cvm-reverse-proxy/internal/attestation", 8 | visibility = ["//:__subpackages__"], 9 | deps = ["//internal/crypto"], 10 | ) 11 | 12 | go_test( 13 | name = "attestation_test", 14 | srcs = ["attestation_test.go"], 15 | embed = [":attestation"], 16 | deps = [ 17 | "//internal/crypto/testvector", 18 | "@com_github_stretchr_testify//assert", 19 | "@com_github_stretchr_testify//require", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /internal/attestation/aws/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "aws", 5 | srcs = ["aws.go"], 6 | importpath = "cvm-reverse-proxy/internal/attestation/aws", 7 | visibility = ["//:__subpackages__"], 8 | ) 9 | -------------------------------------------------------------------------------- /internal/attestation/aws/aws.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | # Amazon Web Services attestation 9 | 10 | Constellation supports multiple attestation technologies on AWS. 11 | 12 | - SEV - Secure Nested Paging (SEV-SNP) 13 | 14 | TPM attestation verified using an SEV-SNP attestation statement. 15 | The TPM runs outside the confidential context. 16 | The initial firmware measurement included in the SNP report can be calculated idependently. 17 | The source code of the firmware is publicly available. 18 | 19 | - NitroTPM 20 | 21 | No confidential computing. Attestation via a TPM 2.0 compliant vTPM. 22 | */ 23 | package aws 24 | -------------------------------------------------------------------------------- /internal/attestation/aws/nitrotpm/nitrotpm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | # NitroTPM Attestation. 9 | 10 | Uses NitroTPM to enable a TPM based measured boot Constellation deployment. 11 | The origin of the attesation statement can not be verified. 12 | 13 | # Issuer 14 | 15 | The TPM attestation is signed by the NitroTPM's RSA attestation key. 16 | Additionally to the TPM attestation, we attach a node's [instance identity document] to the attestation document. 17 | 18 | # Validator 19 | 20 | Currently, the NitroTPM provides no endorsement certificate for its attestation key, nor does AWS offer an alternative way of verifying it. 21 | For now we have to blindly trust the key. 22 | 23 | Additionally to verifying the TPM attestation, we also check the instance identity document for consistency. 24 | 25 | [instance identity document]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html 26 | 27 | [NitroTPM]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nitrotpm.html 28 | */ 29 | package nitrotpm 30 | -------------------------------------------------------------------------------- /internal/attestation/aws/snp/errors.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package snp 8 | 9 | import "fmt" 10 | 11 | // decodeError is used to signal an error during decoding of a public key. 12 | // It only wrapps an error. 13 | type decodeError struct { 14 | inner error 15 | } 16 | 17 | // newDecodeError an error in a DecodeError. 18 | func newDecodeError(err error) *decodeError { 19 | return &decodeError{inner: err} 20 | } 21 | 22 | func (e *decodeError) Error() string { 23 | return fmt.Sprintf("decoding public key: %v", e.inner) 24 | } 25 | 26 | func (e *decodeError) Unwrap() error { 27 | return e.inner 28 | } 29 | 30 | // validationError is used to signal an invalid SNP report. 31 | // It only wrapps an error. 32 | // Used during testing to error conditions more precisely. 33 | type validationError struct { 34 | inner error 35 | } 36 | 37 | // newValidationError wraps an error in a ValidationError. 38 | func newValidationError(err error) *validationError { 39 | return &validationError{inner: err} 40 | } 41 | 42 | func (e *validationError) Error() string { 43 | return e.inner.Error() 44 | } 45 | 46 | func (e *validationError) Unwrap() error { 47 | return e.inner 48 | } 49 | -------------------------------------------------------------------------------- /internal/attestation/aws/snp/issuer_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package snp 8 | 9 | import ( 10 | "os" 11 | "testing" 12 | 13 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/simulator" 14 | 15 | tpmclient "github.com/google/go-tpm-tools/client" 16 | "github.com/stretchr/testify/assert" 17 | "github.com/stretchr/testify/require" 18 | ) 19 | 20 | func TestGetAttestationKey(t *testing.T) { 21 | cgo := os.Getenv("CGO_ENABLED") 22 | if cgo == "0" { 23 | t.Skip("skipping test because CGO is disabled and tpm simulator requires it") 24 | } 25 | 26 | require := require.New(t) 27 | assert := assert.New(t) 28 | 29 | tpm, err := simulator.OpenSimulatedTPM() 30 | require.NoError(err) 31 | defer tpm.Close() 32 | 33 | // create the attestation key in RSA format 34 | tpmAk, err := tpmclient.AttestationKeyRSA(tpm) 35 | assert.NoError(err) 36 | assert.NotNil(tpmAk) 37 | 38 | // get the cached, already created key 39 | getAk, err := getAttestationKey(tpm) 40 | assert.NoError(err) 41 | assert.NotNil(getAk) 42 | 43 | // if everything worked fine, tpmAk and getAk are the same key 44 | assert.Equal(tpmAk, getAk) 45 | } 46 | -------------------------------------------------------------------------------- /internal/attestation/aws/snp/snp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | --------- WARNING! --------- 9 | 10 | THIS PACKAGE DOES CURRENTLY NOT IMPLEMENT ANY SNP ATTESTATION. 11 | It exists to implement required interfaces while implementing other parts of the AWS SNP attestation variant within Constellation. 12 | 13 | ---------------------------- 14 | 15 | # SNP 16 | 17 | Attestation based on TPMs and AMD SEV-SNP. 18 | The TPM is used to generate runtime measurements and sign them with an attestation key. 19 | The TPM currently runs outside the confidential context. This is a limitation imposed by the AWS implementation. 20 | 21 | # Issuer 22 | 23 | Generates a TPM attestation using an attestation key saved inside the TPM. 24 | Additionally loads the SEV-SNP attestation report and AMD VLEK certificate chain, and adds them to the attestation document. 25 | The SNP report includes a measurement of the initial firmware inside the CVM, which can be precalculated independently for verification. 26 | The report also includes the attestation key. 27 | 28 | # Validator 29 | 30 | Verifies the SNP report by verifying the VLEK certificate chain and the report's signature. 31 | This estabilishes trust in the attestation key and the CVM's initial firmware. 32 | However, since the TPM is outside the confidential context, it has to be trusted without verification. 33 | Thus, the hypervisor is still included in the trusted computing base. 34 | 35 | # Glossary 36 | 37 | This section explains abbreviations used in SNP implementation. 38 | 39 | - Attestation Key (AK) 40 | 41 | - AMD Root Key (ARK) 42 | 43 | - AMD Signing Key (ASK) 44 | 45 | - Versioned Chip Endorsement Key (VCEK) 46 | 47 | - Versioned Loaded Endorsement Key (VLEK) 48 | For more information see [SNP WhitePaper] 49 | 50 | [SNP WhitePaper]: https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf 51 | */ 52 | package snp 53 | -------------------------------------------------------------------------------- /internal/attestation/aws/snp/testdata/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "testdata", 5 | srcs = ["testdata.go"], 6 | embedsrcs = [ 7 | "certchain.pem", 8 | "vlek.pem", 9 | "report.txt", 10 | ], 11 | importpath = "cvm-reverse-proxy/internal/attestation/aws/snp/testdata", 12 | visibility = ["//:__subpackages__"], 13 | ) 14 | -------------------------------------------------------------------------------- /internal/attestation/aws/snp/testdata/report.txt: -------------------------------------------------------------------------------- 1 | AgAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAADAAAAAAAW1QMAAAAAAAAABAAAAAAAAAADJjVhPI4zH6KeCWNxkQ/mofaTg92gLJRhQApwtm2Ho9pd2GMAJSK+Q6/DTywjOYm9bkAeNR0Q18yADW9d/PAZ8amfwHRwvrA7EzD0SJUgr48CiBh/+2YccfNcqwm+oZzZYtTy0J9aFsPDi32mvtJEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACKGpDGe5jVOEMlqVJrJXkLPExDcn4NNEkTnWyr1bpRyf//////////////////////////////////////////AwAAAAAAFdEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAFdMUNwEAEjcBAAMAAAAAABXTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdtzdhdRVgTlqcv/jK6JowumHvC2VvXiZ9Zpf150hAG4Y4Zc/ypAlPoORo0uIcxPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATMdpGsLZn99jRGCI3eaEGkcNMvDnJVcVSviZtDTMpNjydn/F1cE2AKOFqCZQ4HnZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 2 | -------------------------------------------------------------------------------- /internal/attestation/aws/snp/testdata/testdata.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package testdata contains testing data for an attestation process. 8 | package testdata 9 | 10 | import _ "embed" 11 | 12 | // SNPReport holds a valid VLEK-signed SNP report. 13 | // 14 | //go:embed report.txt 15 | var SNPReport string 16 | 17 | // AKDigest holds the AK digest embedded in SNPReport.REPORT_DATA. 18 | const AKDigest = "032635613c8e331fa29e096371910fe6a1f69383dda02c9461400a70b66d87a3da5dd863002522be43afc34f2c233989bd6e401e351d10d7cc800d6f5dfcf019" 19 | 20 | // VLEK for SNPReport. 21 | // 22 | //go:embed vlek.pem 23 | var VLEK []byte 24 | 25 | // CertChain is a valid certificate chain for the VLEK certificate. Queried from AMD KDS. 26 | // 27 | //go:embed certchain.pem 28 | var CertChain []byte 29 | -------------------------------------------------------------------------------- /internal/attestation/aws/snp/testdata/vlek.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFLTCCAtygAwIBAgIBADBGBgkqhkiG9w0BAQowOaAPMA0GCWCGSAFlAwQCAgUA 3 | oRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATCjAwIBATCBgDEUMBIG 4 | A1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVTMRQwEgYDVQQHDAtTYW50YSBD 5 | bGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFkdmFuY2VkIE1pY3JvIERldmlj 6 | ZXMxFzAVBgNVBAMMDlNFVi1WTEVLLU1pbGFuMB4XDTI0MDUwNTIxNDUyNloXDTI1 7 | MDUwNTIxNDUyNlowejEUMBIGA1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVT 8 | MRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFk 9 | dmFuY2VkIE1pY3JvIERldmljZXMxETAPBgNVBAMMCFNFVi1WTEVLMHYwEAYHKoZI 10 | zj0CAQYFK4EEACIDYgAEHCsA6v0QwdgijkHV1KnV+1wMqjVaITbdleQV40cnL6ZT 11 | Pq3IsXeFGI9tq2a2EoDksTTqeo5a1ZDq2BiNA2cue0PlZhHkv2MK1cNPMDGAOddc 12 | k7VNaqrRLUo84kn6tRXpo4HyMIHvMBAGCSsGAQQBnHgBAQQDAgEAMBQGCSsGAQQB 13 | nHgBAgQHFgVNaWxhbjARBgorBgEEAZx4AQMBBAMCAQMwEQYKKwYBBAGceAEDAgQD 14 | AgEAMBEGCisGAQQBnHgBAwQEAwIBADARBgorBgEEAZx4AQMFBAMCAQAwEQYKKwYB 15 | BAGceAEDBgQDAgEAMBEGCisGAQQBnHgBAwcEAwIBADARBgorBgEEAZx4AQMDBAMC 16 | ARUwEgYKKwYBBAGceAEDCAQEAgIA0TAsBgkrBgEEAZx4AQUEHxYdQ049Y2MtZXUt 17 | d2VzdC0xLmFtYXpvbmF3cy5jb20wRgYJKoZIhvcNAQEKMDmgDzANBglghkgBZQME 18 | AgIFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIFAKIDAgEwowMCAQEDggIB 19 | ACeJ78s9Nrdz+WtvsNAecT7+ztE8jpxLZdgacsPtf3xU/JfcQHhVUuy/Lp5rIQ7B 20 | h1HalTrmuY7goRO1kTp/lobXyntWkit0d5nR6iNjzp/uHr8+qEym2WbYX1Jesang 21 | BQX06XxXTmEphrHElTrp8BovYIsPejdY2nNUYV6fhrdTXEh+qLDGQmwjK12FG+hu 22 | 4AS+rev2V7H9uE1XKXsM4TTqvI1hT3E2ocN4KjfUBi7yL/BF97kXfdqZH48pPD4y 23 | i7TbZ7S89UikrAv0ZtgGyXY8yR094YVjfbnUvyYTyh4fgV8a8Mxsb4yhPoOOxkUI 24 | 8tNBhM4LkTPkR/4+Y2Dg6maglZJ5Hb2WWWNkd0CZchZC80T7HIgHztINMnHULiYi 25 | sNRtKeUAqUNtwy0d2YehX+v9HzueTfKtvxIy2oBfT1LCykvTQTibE3aCvFMkEiw8 26 | 4CunpWfPAoZEzzJUTxLQ6PkdE4MVRTTuuOAVHTrtkIUOB6tlkgMzijqAdwzTDdIj 27 | NGQxTm0Vd2h+zvZl2HnSCi6PMoZml5RwZHiZXKRC90bPn0Vk1XlYW1wMEFHTWQqo 28 | tFH44eWyGIoTwSqcqATR/HklCoUP0wMe2sSsMemJMPwAXWW4fZxmee72OR4p6c+w 29 | TGzR0J5WFdJ0g2Ix+NobBydNaJnQz4H5Y+/gZFUCRrWh 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /internal/attestation/azure/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "azure", 6 | srcs = ["azure.go"], 7 | importpath = "cvm-reverse-proxy/internal/attestation/azure", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "@com_github_google_go_tpm//legacy/tpm2", 11 | "@com_github_google_go_tpm_tools//client", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "azure_test", 17 | srcs = ["azure_test.go"], 18 | embed = [":azure"], 19 | deps = [ 20 | "//internal/attestation/simulator", 21 | "//internal/attestation/snp", 22 | "@com_github_google_go_tpm//legacy/tpm2", 23 | "@com_github_google_go_tpm_tools//client", 24 | "@com_github_stretchr_testify//assert", 25 | "@com_github_stretchr_testify//require", 26 | ], 27 | ) 28 | -------------------------------------------------------------------------------- /internal/attestation/azure/snp/imds.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package snp 8 | 9 | import ( 10 | "context" 11 | "fmt" 12 | 13 | "github.com/flashbots/cvm-reverse-proxy/internal/cloud/azure" 14 | ) 15 | 16 | const tagMAAURL = "constellation-maa-url" 17 | 18 | type imdsClient struct { 19 | imdsClient *azure.IMDSClient 20 | } 21 | 22 | func newIMDSClient() *imdsClient { 23 | return &imdsClient{ 24 | imdsClient: azure.NewIMDSClient(), 25 | } 26 | } 27 | 28 | func (c *imdsClient) getMAAURL(ctx context.Context) (string, error) { 29 | tags, err := c.imdsClient.Tags(ctx) 30 | if err != nil { 31 | return "", fmt.Errorf("getting tags: %w", err) 32 | } 33 | 34 | return tags[tagMAAURL], nil 35 | } 36 | -------------------------------------------------------------------------------- /internal/attestation/azure/snp/snp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | # SNP 9 | 10 | Attestation based on TPM and SEV-SNP attestation. 11 | The TPM is used to generate runtime measurements and signed by an attestation key that can be verified using the SEV-SNP attestation report. 12 | 13 | # Issuer 14 | 15 | Generates a TPM attestation using an attestation key saved in the TPM. 16 | Additionally loads the SEV-SNP attestation report and AMD VCEK certificate chain, and adds them to the attestation document. 17 | 18 | # Validator 19 | 20 | Verifies the attestation key used by first verifying the VCEK certificate chain and the SNP attestation report. 21 | 22 | # Glossary 23 | 24 | This section explains abbreviations used in SNP implementation. 25 | 26 | - Attestation Key (AK) 27 | 28 | - AMD Root Key (ARK) 29 | 30 | - AMD Signing Key (ASK) 31 | 32 | - Versioned Chip Endorsement Key (VCEK) 33 | 34 | For more information see [SNP WhitePaper] 35 | 36 | - Host (Hardware?) Compatibility Layer (HCL) 37 | 38 | No public information. Azure compute API has a field `isHostCompatibilityLayerVm`, with only a [single sentence of documentation]. 39 | 40 | [SNP WhitePaper]: https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf 41 | [single sentence of documentation]: https://learn.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=windows 42 | */ 43 | package snp 44 | -------------------------------------------------------------------------------- /internal/attestation/azure/tdx/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "tdx", 6 | srcs = [ 7 | "issuer.go", 8 | "tdx.go", 9 | "validator.go", 10 | ], 11 | importpath = "cvm-reverse-proxy/internal/attestation/azure/tdx", 12 | visibility = ["//:__subpackages__"], 13 | deps = [ 14 | "//internal/attestation", 15 | "//internal/attestation/azure", 16 | "//internal/attestation/variant", 17 | "//internal/attestation/vtpm", 18 | "//internal/config", 19 | "@com_github_google_go_tdx_guest//abi", 20 | "@com_github_google_go_tdx_guest//proto/tdx", 21 | "@com_github_google_go_tdx_guest//validate", 22 | "@com_github_google_go_tdx_guest//verify", 23 | "@com_github_google_go_tdx_guest//verify/trust", 24 | "@com_github_google_go_tpm//legacy/tpm2", 25 | "@com_github_google_go_tpm_tools//proto/attest", 26 | ], 27 | ) 28 | 29 | go_test( 30 | name = "tdx_test", 31 | srcs = ["issuer_test.go"], 32 | embed = [":tdx"], 33 | deps = [ 34 | "//internal/attestation/azure/tdx/testdata", 35 | "@com_github_stretchr_testify//assert", 36 | "@com_github_stretchr_testify//require", 37 | ], 38 | ) 39 | -------------------------------------------------------------------------------- /internal/attestation/azure/tdx/tdx.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | package tdx implements attestation for TDX on Azure. 9 | 10 | Quotes are generated using an Azure provided vTPM and the IMDS API. 11 | They are verified using the go-tdx-guest library. 12 | 13 | More specifically: 14 | - The vTPM is used to collected a TPM attestation and a Hardware Compatibility Layer (HCL) report. 15 | - The HCL report is sent to the IMDS API to generate a TDX quote. 16 | - The quote is verified using the go-tdx-guest library. 17 | - The quote's report data can be used to verify the TPM's attestation key. 18 | - The attestation key can be used to verify the TPM attestation. 19 | */ 20 | package tdx 21 | 22 | // InstanceInfo wraps the TDX report with additional Azure specific runtime data. 23 | type InstanceInfo struct { 24 | AttestationReport []byte 25 | RuntimeData []byte 26 | } 27 | -------------------------------------------------------------------------------- /internal/attestation/azure/tdx/testdata/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "testdata", 5 | srcs = ["testdata.go"], 6 | embedsrcs = ["hclreport.bin"], 7 | importpath = "cvm-reverse-proxy/internal/attestation/azure/tdx/testdata", 8 | visibility = ["//:__subpackages__"], 9 | ) 10 | -------------------------------------------------------------------------------- /internal/attestation/azure/tdx/testdata/hclreport.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flashbots/cvm-reverse-proxy/4e175a4f663ef7d679b3900c41396de360fdbc74/internal/attestation/azure/tdx/testdata/hclreport.bin -------------------------------------------------------------------------------- /internal/attestation/azure/tdx/testdata/testdata.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package testdata contains testing data for an attestation process. 8 | package testdata 9 | 10 | import _ "embed" 11 | 12 | // HCLReport is an example HCL report from an Azure TDX VM. 13 | // 14 | //go:embed hclreport.bin 15 | var HCLReport []byte 16 | -------------------------------------------------------------------------------- /internal/attestation/azure/trustedlaunch/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "trustedlaunch", 6 | srcs = [ 7 | "issuer.go", 8 | "trustedlaunch.go", 9 | "validator.go", 10 | ], 11 | importpath = "cvm-reverse-proxy/internal/attestation/azure/trustedlaunch", 12 | visibility = ["//:__subpackages__"], 13 | deps = [ 14 | "//internal/attestation", 15 | "//internal/attestation/variant", 16 | "//internal/attestation/vtpm", 17 | "//internal/config", 18 | "//internal/crypto", 19 | "@com_github_google_go_tpm//legacy/tpm2", 20 | "@com_github_google_go_tpm_tools//client", 21 | "@com_github_google_go_tpm_tools//proto/attest", 22 | ], 23 | ) 24 | 25 | go_test( 26 | name = "trustedlaunch_test", 27 | srcs = ["trustedlaunch_test.go"], 28 | embed = [":trustedlaunch"], 29 | # keep 30 | gotags = select({ 31 | "//bazel/settings:tpm_simulator_enabled": [], 32 | "//conditions:default": ["disable_tpm_simulator"], 33 | }), 34 | deps = [ 35 | "//internal/attestation/measurements", 36 | "//internal/attestation/simulator", 37 | "//internal/attestation/vtpm", 38 | "//internal/config", 39 | "//internal/crypto", 40 | "//internal/logger", 41 | "@com_github_google_go_tpm//legacy/tpm2", 42 | "@com_github_google_go_tpm_tools//client", 43 | "@com_github_google_go_tpm_tools//proto/attest", 44 | "@com_github_stretchr_testify//assert", 45 | "@com_github_stretchr_testify//require", 46 | ], 47 | ) 48 | -------------------------------------------------------------------------------- /internal/attestation/azure/trustedlaunch/trustedlaunch.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | # Trusted Launch 9 | 10 | Use Azure's trusted launch vTPM to enable a TPM based measure boot Constellation. 11 | 12 | # Issuer 13 | 14 | Generates a TPM attestation using an attestation key saved in the TPM. 15 | Additionally an endorsement certificate of the key, and corresponding CA certificate chain are added to the attestation document. 16 | 17 | # Validator 18 | 19 | Verifies the TPM attestation statement using the public key of the endorsement certificate. 20 | The certificate is verified by first verifying its CA certificate chain. 21 | */ 22 | package trustedlaunch 23 | -------------------------------------------------------------------------------- /internal/attestation/choose/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "choose", 6 | srcs = ["choose.go"], 7 | importpath = "cvm-reverse-proxy/internal/attestation/choose", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/atls", 11 | "//internal/attestation", 12 | "//internal/attestation/aws/nitrotpm", 13 | "//internal/attestation/aws/snp", 14 | "//internal/attestation/azure/snp", 15 | "//internal/attestation/azure/tdx", 16 | "//internal/attestation/azure/trustedlaunch", 17 | "//internal/attestation/gcp/es", 18 | "//internal/attestation/gcp/snp", 19 | "//internal/attestation/qemu", 20 | "//internal/attestation/tdx", 21 | "//internal/attestation/variant", 22 | "//internal/config", 23 | ], 24 | ) 25 | 26 | go_test( 27 | name = "choose_test", 28 | srcs = ["choose_test.go"], 29 | embed = [":choose"], 30 | deps = [ 31 | "//internal/attestation/measurements", 32 | "//internal/attestation/variant", 33 | "//internal/config", 34 | "@com_github_stretchr_testify//assert", 35 | "@com_github_stretchr_testify//require", 36 | ], 37 | ) 38 | -------------------------------------------------------------------------------- /internal/attestation/gcp/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "gcp", 5 | srcs = [ 6 | "gcp.go", 7 | "metadata.go", 8 | "restclient.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/attestation/gcp", 11 | visibility = ["//:__subpackages__"], 12 | deps = [ 13 | "//internal/attestation/snp", 14 | "//internal/attestation/variant", 15 | "//internal/attestation/vtpm", 16 | "@com_github_google_go_tpm_tools//proto/attest", 17 | "@com_github_googleapis_gax_go_v2//:gax-go", 18 | "@com_google_cloud_go_compute//apiv1", 19 | "@com_google_cloud_go_compute//apiv1/computepb", 20 | "@com_google_cloud_go_compute_metadata//:metadata", 21 | "@org_golang_google_api//option", 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /internal/attestation/gcp/es/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "es", 6 | srcs = [ 7 | "es.go", 8 | "issuer.go", 9 | "validator.go", 10 | ], 11 | importpath = "cvm-reverse-proxy/internal/attestation/gcp/es", 12 | visibility = ["//:__subpackages__"], 13 | deps = [ 14 | "//internal/attestation", 15 | "//internal/attestation/gcp", 16 | "//internal/attestation/variant", 17 | "//internal/attestation/vtpm", 18 | "//internal/config", 19 | "@com_github_google_go_tpm_tools//client", 20 | "@com_github_google_go_tpm_tools//proto/attest", 21 | ], 22 | ) 23 | 24 | go_test( 25 | name = "es_test", 26 | srcs = [ 27 | "issuer_test.go", 28 | "validator_test.go", 29 | ], 30 | embed = [":es"], 31 | deps = [ 32 | "//internal/attestation/gcp", 33 | "//internal/attestation/variant", 34 | "//internal/attestation/vtpm", 35 | "@com_github_google_go_tpm_tools//proto/attest", 36 | "@com_github_googleapis_gax_go_v2//:gax-go", 37 | "@com_github_stretchr_testify//assert", 38 | "@com_github_stretchr_testify//require", 39 | "@com_google_cloud_go_compute//apiv1/computepb", 40 | "@org_golang_google_api//option", 41 | "@org_golang_google_protobuf//proto", 42 | ], 43 | ) 44 | -------------------------------------------------------------------------------- /internal/attestation/gcp/es/issuer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package es 8 | 9 | import ( 10 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation" 11 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/gcp" 12 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/variant" 13 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/vtpm" 14 | 15 | tpmclient "github.com/google/go-tpm-tools/client" 16 | ) 17 | 18 | // Issuer for GCP confidential VM attestation. 19 | type Issuer struct { 20 | variant.GCPSEVES 21 | *vtpm.Issuer 22 | } 23 | 24 | // NewIssuer initializes a new GCP Issuer. 25 | func NewIssuer(log attestation.Logger) *Issuer { 26 | return &Issuer{ 27 | Issuer: vtpm.NewIssuer( 28 | vtpm.OpenVTPM, 29 | tpmclient.GceAttestationKeyRSA, 30 | gcp.GCEInstanceInfo(gcp.MetadataClient{}), 31 | log, 32 | ), 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /internal/attestation/gcp/es/validator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package es 8 | 9 | import ( 10 | "fmt" 11 | 12 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation" 13 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/gcp" 14 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/variant" 15 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/vtpm" 16 | "github.com/flashbots/cvm-reverse-proxy/internal/config" 17 | 18 | "github.com/google/go-tpm-tools/proto/attest" 19 | ) 20 | 21 | const minimumGceVersion = 1 22 | 23 | // Validator for GCP confidential VM attestation. 24 | type Validator struct { 25 | variant.GCPSEVES 26 | *vtpm.Validator 27 | } 28 | 29 | // NewValidator initializes a new GCP validator with the provided PCR values specified in the config. 30 | func NewValidator(cfg *config.GCPSEVES, log attestation.Logger) (*Validator, error) { 31 | getTrustedKey, err := gcp.TrustedKeyGetter(variant.GCPSEVES{}, gcp.NewRESTClient) 32 | if err != nil { 33 | return nil, fmt.Errorf("create trusted key getter: %v", err) 34 | } 35 | 36 | return &Validator{ 37 | Validator: vtpm.NewValidator( 38 | cfg.Measurements, 39 | getTrustedKey, 40 | validateCVM, 41 | log, 42 | ), 43 | }, nil 44 | } 45 | 46 | // validateCVM checks that the machine state represents a GCE AMD-SEV VM. 47 | func validateCVM(_ vtpm.AttestationDocument, state *attest.MachineState) error { 48 | gceVersion := state.Platform.GetGceVersion() 49 | if gceVersion < minimumGceVersion { 50 | return fmt.Errorf("outdated GCE version: %v (require >= %v)", gceVersion, minimumGceVersion) 51 | } 52 | 53 | tech := state.Platform.Technology 54 | wantTech := attest.GCEConfidentialTechnology_AMD_SEV 55 | if tech != wantTech { 56 | return fmt.Errorf("unexpected confidential technology: %v (expected: %v)", tech, wantTech) 57 | } 58 | 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /internal/attestation/gcp/gcp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | # Google Cloud Platform attestation 9 | */ 10 | package gcp 11 | -------------------------------------------------------------------------------- /internal/attestation/gcp/snp/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "snp", 5 | srcs = [ 6 | "issuer.go", 7 | "snp.go", 8 | "validator.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/attestation/gcp/snp", 11 | visibility = ["//:__subpackages__"], 12 | deps = [ 13 | "//internal/attestation", 14 | "//internal/attestation/gcp", 15 | "//internal/attestation/snp", 16 | "//internal/attestation/variant", 17 | "//internal/attestation/vtpm", 18 | "//internal/config", 19 | "@com_github_google_go_sev_guest//abi", 20 | "@com_github_google_go_sev_guest//kds", 21 | "@com_github_google_go_sev_guest//proto/sevsnp", 22 | "@com_github_google_go_sev_guest//validate", 23 | "@com_github_google_go_sev_guest//verify", 24 | "@com_github_google_go_sev_guest//verify/trust", 25 | "@com_github_google_go_tpm_tools//client", 26 | "@com_github_google_go_tpm_tools//proto/attest", 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /internal/attestation/idkeydigest/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "idkeydigest", 6 | srcs = [ 7 | "enforcement_string.go", 8 | "idkeydigest.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/attestation/idkeydigest", 11 | visibility = ["//:__subpackages__"], 12 | ) 13 | 14 | go_test( 15 | name = "idkeydigest_test", 16 | srcs = ["idkeydigest_test.go"], 17 | embed = [":idkeydigest"], 18 | deps = [ 19 | "@com_github_stretchr_testify//assert", 20 | "@com_github_stretchr_testify//require", 21 | "@in_gopkg_yaml_v3//:yaml_v3", 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /internal/attestation/idkeydigest/enforcement_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Enforcement"; DO NOT EDIT. 2 | 3 | package idkeydigest 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[Unknown-0] 12 | _ = x[Equal-1] 13 | _ = x[MAAFallback-2] 14 | _ = x[WarnOnly-3] 15 | } 16 | 17 | const _Enforcement_name = "UnknownEqualMAAFallbackWarnOnly" 18 | 19 | var _Enforcement_index = [...]uint8{0, 7, 12, 23, 31} 20 | 21 | func (i Enforcement) String() string { 22 | if i >= Enforcement(len(_Enforcement_index)-1) { 23 | return "Enforcement(" + strconv.FormatInt(int64(i), 10) + ")" 24 | } 25 | return _Enforcement_name[_Enforcement_index[i]:_Enforcement_index[i+1]] 26 | } 27 | -------------------------------------------------------------------------------- /internal/attestation/initialize/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "initialize", 6 | srcs = ["initialize.go"], 7 | importpath = "cvm-reverse-proxy/internal/attestation/initialize", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/attestation/measurements", 11 | "//internal/attestation/tdx", 12 | "@com_github_edgelesssys_go_tdx_qpl//tdx", 13 | "@com_github_google_go_tpm//legacy/tpm2", 14 | ], 15 | ) 16 | 17 | go_test( 18 | name = "initialize_test", 19 | srcs = ["initialize_test.go"], 20 | embed = [":initialize"], 21 | deps = [ 22 | "//internal/attestation/measurements", 23 | "//internal/attestation/simulator", 24 | "@com_github_google_go_tpm//legacy/tpm2", 25 | "@com_github_google_go_tpm_tools//client", 26 | "@com_github_stretchr_testify//assert", 27 | "@com_github_stretchr_testify//require", 28 | ], 29 | ) 30 | -------------------------------------------------------------------------------- /internal/attestation/measurements/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "measurements", 6 | srcs = [ 7 | "fetchmeasurements.go", 8 | "measurements.go", 9 | # keep 10 | "measurements_enterprise.go", 11 | # keep 12 | "measurements_oss.go", 13 | "overrides.go", 14 | ], 15 | importpath = "cvm-reverse-proxy/internal/attestation/measurements", 16 | visibility = ["//:__subpackages__"], 17 | deps = [ 18 | "//internal/api/versionsapi", 19 | "//internal/attestation/variant", 20 | "//internal/cloud/cloudprovider", 21 | "//internal/sigstore", 22 | "//internal/sigstore/keyselect", 23 | "@com_github_google_go_tpm//tpmutil", 24 | "@com_github_siderolabs_talos_pkg_machinery//config/encoder", 25 | "@in_gopkg_yaml_v3//:yaml_v3", 26 | ], 27 | ) 28 | 29 | go_test( 30 | name = "measurements_test", 31 | srcs = [ 32 | "fetchmeasurements_test.go", 33 | "measurements_test.go", 34 | ], 35 | embed = [":measurements"], 36 | deps = [ 37 | "//internal/api/versionsapi", 38 | "//internal/attestation/variant", 39 | "//internal/cloud/cloudprovider", 40 | "//internal/sigstore", 41 | "@com_github_siderolabs_talos_pkg_machinery//config/encoder", 42 | "@com_github_stretchr_testify//assert", 43 | "@com_github_stretchr_testify//require", 44 | "@in_gopkg_yaml_v3//:yaml_v3", 45 | ], 46 | ) 47 | -------------------------------------------------------------------------------- /internal/attestation/measurements/measurement-generator/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "measurement-generator_lib", 6 | srcs = ["generate.go"], 7 | importpath = "cvm-reverse-proxy/internal/attestation/measurements/measurement-generator", 8 | visibility = ["//visibility:private"], 9 | deps = [ 10 | "//internal/api/versionsapi", 11 | "//internal/attestation/measurements", 12 | "//internal/attestation/variant", 13 | "//internal/cloud/cloudprovider", 14 | "//internal/constants", 15 | "//internal/sigstore", 16 | "//internal/sigstore/keyselect", 17 | "@org_golang_x_tools//go/ast/astutil", 18 | ], 19 | ) 20 | 21 | go_binary( 22 | name = "measurement-generator", 23 | embed = [":measurement-generator_lib"], 24 | # keep 25 | gotags = ["enterprise"], 26 | visibility = ["//:__subpackages__"], 27 | ) 28 | 29 | go_test( 30 | name = "measurement-generator_test", 31 | srcs = ["generate_test.go"], 32 | embed = [":measurement-generator_lib"], 33 | deps = ["@org_uber_go_goleak//:goleak"], 34 | ) 35 | -------------------------------------------------------------------------------- /internal/attestation/measurements/measurement-generator/generate_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package main 8 | 9 | import ( 10 | "testing" 11 | 12 | "go.uber.org/goleak" 13 | ) 14 | 15 | func TestMain(m *testing.M) { 16 | goleak.VerifyTestMain(m, goleak.IgnoreAnyFunction("github.com/bazelbuild/rules_go/go/tools/bzltestutil.RegisterTimeoutHandler.func1")) 17 | } 18 | -------------------------------------------------------------------------------- /internal/attestation/qemu/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "qemu", 5 | srcs = [ 6 | "issuer.go", 7 | "qemu.go", 8 | "validator.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/attestation/qemu", 11 | visibility = ["//:__subpackages__"], 12 | deps = [ 13 | "//internal/attestation", 14 | "//internal/attestation/variant", 15 | "//internal/attestation/vtpm", 16 | "//internal/config", 17 | "@com_github_google_go_tpm//legacy/tpm2", 18 | "@com_github_google_go_tpm_tools//client", 19 | "@com_github_google_go_tpm_tools//proto/attest", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /internal/attestation/qemu/issuer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package qemu 8 | 9 | import ( 10 | "context" 11 | "io" 12 | 13 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation" 14 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/variant" 15 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/vtpm" 16 | 17 | tpmclient "github.com/google/go-tpm-tools/client" 18 | ) 19 | 20 | // Issuer for qemu TPM attestation. 21 | type Issuer struct { 22 | variant.QEMUVTPM 23 | *vtpm.Issuer 24 | } 25 | 26 | // NewIssuer initializes a new QEMU Issuer. 27 | func NewIssuer(log attestation.Logger) *Issuer { 28 | return &Issuer{ 29 | Issuer: vtpm.NewIssuer( 30 | vtpm.OpenVTPM, 31 | tpmclient.AttestationKeyRSA, 32 | func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) { return nil, nil }, 33 | log, 34 | ), 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /internal/attestation/qemu/qemu.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | # QEMU attestation 9 | 10 | Basic TPM attestation for QEMU platforms. 11 | This is used for miniConstellation, or QEMU testing clusters. 12 | */ 13 | package qemu 14 | -------------------------------------------------------------------------------- /internal/attestation/qemu/validator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package qemu 8 | 9 | import ( 10 | "context" 11 | "crypto" 12 | 13 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation" 14 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/variant" 15 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/vtpm" 16 | "github.com/flashbots/cvm-reverse-proxy/internal/config" 17 | 18 | "github.com/google/go-tpm-tools/proto/attest" 19 | "github.com/google/go-tpm/legacy/tpm2" 20 | ) 21 | 22 | // Validator for QEMU VM attestation. 23 | type Validator struct { 24 | variant.QEMUVTPM 25 | *vtpm.Validator 26 | } 27 | 28 | // NewValidator initializes a new QEMU validator with the provided PCR values. 29 | func NewValidator(cfg *config.QEMUVTPM, log attestation.Logger) *Validator { 30 | return &Validator{ 31 | Validator: vtpm.NewValidator( 32 | cfg.Measurements, 33 | unconditionalTrust, 34 | func(vtpm.AttestationDocument, *attest.MachineState) error { return nil }, 35 | log, 36 | ), 37 | } 38 | } 39 | 40 | // unconditionalTrust returns the given public key as the trusted attestation key. 41 | func unconditionalTrust(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) { 42 | pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return pubArea.Key() 47 | } 48 | -------------------------------------------------------------------------------- /internal/attestation/simulator/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | # keep 4 | go_library( 5 | name = "simulator", 6 | srcs = [ 7 | "simulator.go", 8 | "simulator_disabled.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/attestation/simulator", 11 | visibility = ["//:__subpackages__"], 12 | deps = ["@com_github_google_go_tpm_tools//simulator"], 13 | ) 14 | -------------------------------------------------------------------------------- /internal/attestation/simulator/simulator.go: -------------------------------------------------------------------------------- 1 | //go:build !disable_tpm_simulator 2 | 3 | /* 4 | Copyright (c) Edgeless Systems GmbH 5 | 6 | SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | // TPM2 simulator used for unit tests. 10 | package simulator 11 | 12 | import ( 13 | "io" 14 | 15 | "github.com/google/go-tpm-tools/simulator" 16 | ) 17 | 18 | // OpenSimulatedTPM returns a simulated TPM device. 19 | func OpenSimulatedTPM() (io.ReadWriteCloser, error) { 20 | return simulator.Get() 21 | } 22 | 23 | // NewSimulatedTPMOpenFunc returns a TPMOpenFunc that opens a simulated TPM. 24 | func NewSimulatedTPMOpenFunc() (func() (io.ReadWriteCloser, error), io.Closer) { 25 | tpm, err := OpenSimulatedTPM() 26 | if err != nil { 27 | panic(err) 28 | } 29 | return func() (io.ReadWriteCloser, error) { 30 | return &simulatedTPM{tpm}, nil 31 | }, tpm 32 | } 33 | 34 | type simulatedTPM struct { 35 | openSimulatedTPM io.ReadWriteCloser 36 | } 37 | 38 | func (t *simulatedTPM) Read(p []byte) (int, error) { 39 | return t.openSimulatedTPM.Read(p) 40 | } 41 | 42 | func (t *simulatedTPM) Write(p []byte) (int, error) { 43 | return t.openSimulatedTPM.Write(p) 44 | } 45 | 46 | func (t *simulatedTPM) Close() error { 47 | // never close the underlying simulated TPM to allow calling the TPMOpenFunc again 48 | return nil 49 | } 50 | 51 | func (*simulatedTPM) EventLog() ([]byte, error) { 52 | return nil, nil 53 | } 54 | -------------------------------------------------------------------------------- /internal/attestation/simulator/simulator_disabled.go: -------------------------------------------------------------------------------- 1 | //go:build disable_tpm_simulator 2 | 3 | /* 4 | Copyright (c) Edgeless Systems GmbH 5 | 6 | SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package simulator 10 | 11 | import ( 12 | "io" 13 | ) 14 | 15 | // OpenSimulatedTPM returns a simulated TPM device. 16 | func OpenSimulatedTPM() (io.ReadWriteCloser, error) { 17 | panic("simulator not enabled") 18 | } 19 | 20 | // NewSimulatedTPMOpenFunc returns a TPMOpenFunc that opens a simulated TPM. 21 | func NewSimulatedTPMOpenFunc() (func() (io.ReadWriteCloser, error), io.Closer) { 22 | panic("simulator not enabled") 23 | } 24 | -------------------------------------------------------------------------------- /internal/attestation/snp/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "snp", 6 | srcs = ["snp.go"], 7 | importpath = "cvm-reverse-proxy/internal/attestation/snp", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/attestation", 11 | "@com_github_google_go_sev_guest//abi", 12 | "@com_github_google_go_sev_guest//client", 13 | "@com_github_google_go_sev_guest//kds", 14 | "@com_github_google_go_sev_guest//proto/sevsnp", 15 | "@com_github_google_go_sev_guest//verify/trust", 16 | "@com_github_google_go_tpm_tools//proto/attest", 17 | ], 18 | ) 19 | 20 | go_test( 21 | name = "snp_test", 22 | srcs = ["snp_test.go"], 23 | embed = [":snp"], 24 | deps = [ 25 | "//internal/attestation/snp/testdata", 26 | "//internal/config", 27 | "//internal/logger", 28 | "@com_github_google_go_sev_guest//kds", 29 | "@com_github_google_go_sev_guest//verify/trust", 30 | "@com_github_stretchr_testify//assert", 31 | "@com_github_stretchr_testify//require", 32 | ], 33 | ) 34 | -------------------------------------------------------------------------------- /internal/attestation/snp/testdata/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "testdata", 5 | srcs = ["testdata.go"], 6 | embedsrcs = [ 7 | "attestation.bin", 8 | "vcek.cert", 9 | "certchain.pem", 10 | "runtimedata.bin", 11 | "vcek.pem", 12 | "vlek.pem", 13 | "vlekcertchain.pem", 14 | ], 15 | importpath = "cvm-reverse-proxy/internal/attestation/snp/testdata", 16 | visibility = ["//:__subpackages__"], 17 | ) 18 | -------------------------------------------------------------------------------- /internal/attestation/snp/testdata/attestation.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flashbots/cvm-reverse-proxy/4e175a4f663ef7d679b3900c41396de360fdbc74/internal/attestation/snp/testdata/attestation.bin -------------------------------------------------------------------------------- /internal/attestation/snp/testdata/runtimedata.bin: -------------------------------------------------------------------------------- 1 | {"keys":[{"kid":"HCLAkPub","key_ops":["encrypt"],"kty":"RSA","e":"AQAB","n":"tyFqvAAAf2GFelkW7VcRhJnA2YvY6LjBzeUN2vaMZnZth_tFnWMkK5AXtusyCNel3uip3VGZzTaz5X2tGVjGrs-MVHcap9QdwqUXVW3g9OQ_tEbixcx7-xblUJQkGEQfnbbSdnPI2lvLzOs1Z_0vjeDAxvSQrmags6nY-cJAWH-pgDVJyHtpsUSs_QBWklazDso5WHmnMt9s9MuilWXox0RSyXnUeaQyhY1ju7RTScRnVXuNy67z_EJnwM92drwFb8AUd0SJ_9ohvEYl4aZRDECGo0VrjcSHU-JGJeuWC5VhDBR5EOoCVBBgqe9e83v_lJxI3WLe2ovSIZIIzAmbSQ"}],"vm-configuration":{"console-enabled":true,"current-time":1661455391,"secure-boot":false,"tpm-enabled":true,"vmUniqueId":"B6C98C3B-4EC7-4DA6-BD2F-7D98D20D7B75"}} -------------------------------------------------------------------------------- /internal/attestation/snp/testdata/vcek.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flashbots/cvm-reverse-proxy/4e175a4f663ef7d679b3900c41396de360fdbc74/internal/attestation/snp/testdata/vcek.cert -------------------------------------------------------------------------------- /internal/attestation/snp/testdata/vcek.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFTDCCAvugAwIBAgIBADBGBgkqhkiG9w0BAQowOaAPMA0GCWCGSAFlAwQCAgUA 3 | oRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATCjAwIBATB7MRQwEgYD 4 | VQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDASBgNVBAcMC1NhbnRhIENs 5 | YXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5jZWQgTWljcm8gRGV2aWNl 6 | czESMBAGA1UEAwwJU0VWLU1pbGFuMB4XDTIzMDgzMDEyMTUyNFoXDTMwMDgzMDEy 7 | MTUyNFowejEUMBIGA1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVTMRQwEgYD 8 | VQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFkdmFuY2Vk 9 | IE1pY3JvIERldmljZXMxETAPBgNVBAMMCFNFVi1WQ0VLMHYwEAYHKoZIzj0CAQYF 10 | K4EEACIDYgAEhPX8Cl9uA7PxqNGzeqamJNYJLx/VFE/s3+8qOWtaztKNcn1PaAI4 11 | ndE+yaVfMHsiA8CLTylumpWXcVBHPYV9kPEVrtozhvrrT5Oii9OpZPYHJ7/WPVmM 12 | J3K8/Iz3AshTo4IBFjCCARIwEAYJKwYBBAGceAEBBAMCAQAwFwYJKwYBBAGceAEC 13 | BAoWCE1pbGFuLUIwMBEGCisGAQQBnHgBAwEEAwIBAjARBgorBgEEAZx4AQMCBAMC 14 | AQAwEQYKKwYBBAGceAEDBAQDAgEAMBEGCisGAQQBnHgBAwUEAwIBADARBgorBgEE 15 | AZx4AQMGBAMCAQAwEQYKKwYBBAGceAEDBwQDAgEAMBEGCisGAQQBnHgBAwMEAwIB 16 | BjARBgorBgEEAZx4AQMIBAMCAV0wTQYJKwYBBAGceAEEBECeRKrvAs/Kb926ymac 17 | bP0p4auNl+vJOYVxKKy7E7h0DfMUNtNOhuX4rgzf6zoOGF20beysF2zHfXYcIqG5 18 | 3PJbMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0B 19 | AQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBA4ICAQBoVGgDdFV9gWPHaEOBrHzd 20 | WVYyuuMBH340DDSXbCGlPR6rhgja0qALmkUPG50REQGvoPsikAskwqhzRG2XEDO2 21 | b6+fRPIq3DjEbz/8V89IiYiOZI/ycFACi3EEVECAWbzjXSfiOio1NfbniXP6tWzW 22 | D/8xpd/8N8166bHpgNgMl9pX4i0I9vaTl3qH+jBuSMZ5Q4heTHLB+v4V7q+H6SZo 23 | 7htqpaI3keLEhQL/pCP72udMPAzU+/5W/x/t/LD6SbQcQQoHbWDU6kgTDuXabDxl 24 | A4JoEZfatr+/TO6jKQcGtqOLKT8JFGcigUlBi/TBVP+Xs8E4CWYGZZiTpYoLwNAu 25 | yuKOP9VVFViSCqPvzpNs2G+e0zXg2w3te7oMw/l0bD8iQCAS8rR0+r+8pZL4e010 26 | KLZ3yEfA0moXef66k5xyf4y37ZIP189wz6qJ+YXqOujDmeTomCU0SnZXlri6GhbF 27 | 19rp2z5/lsZG+W27CRxvzTB3hk+ukZr35vCqNq4Rs+c7/hYcYzzyZ4ysATwdglNF 28 | WddfVw5Qunlu6Ngxr84ifz3HrnUx9bR5DzmFbztrb7IbkZhq7GjImwJULub1viyg 29 | YFa7X3p8b1WllienSEfvbadobbS9HeuLUrWyh0kZjQnz+0Q1UB1/zlzokeQmAYCf 30 | 8H3kABPv6hqrFftRNbargQ== 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /internal/attestation/snp/testdata/vlek.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFLDCCAtugAwIBAgIBADBGBgkqhkiG9w0BAQowOaAPMA0GCWCGSAFlAwQCAgUA 3 | oRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATCjAwIBATCBgDEUMBIG 4 | A1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVTMRQwEgYDVQQHDAtTYW50YSBD 5 | bGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFkdmFuY2VkIE1pY3JvIERldmlj 6 | ZXMxFzAVBgNVBAMMDlNFVi1WTEVLLU1pbGFuMB4XDTIzMDcxOTA4MjkyOFoXDTI0 7 | MDcxOTA4MjkyOFowejEUMBIGA1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVT 8 | MRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFk 9 | dmFuY2VkIE1pY3JvIERldmljZXMxETAPBgNVBAMMCFNFVi1WTEVLMHYwEAYHKoZI 10 | zj0CAQYFK4EEACIDYgAEXFl4NHpiQCuZXIrehIEk/5XNIdMvo24wyaezN+0FouYB 11 | 9Z23nL523gpJUlT+mvb5ZMybh5tO1nBGFMOKwzP9dnSBwTs0qn57Ts9OTpW57EAo 12 | Mx4SI7g1yz/mt4e6hma4o4HxMIHuMBAGCSsGAQQBnHgBAQQDAgEAMBQGCSsGAQQB 13 | nHgBAgQHFgVNaWxhbjARBgorBgEEAZx4AQMBBAMCAQMwEQYKKwYBBAGceAEDAgQD 14 | AgEAMBEGCisGAQQBnHgBAwQEAwIBADARBgorBgEEAZx4AQMFBAMCAQAwEQYKKwYB 15 | BAGceAEDBgQDAgEAMBEGCisGAQQBnHgBAwcEAwIBADARBgorBgEEAZx4AQMDBAMC 16 | AQowEQYKKwYBBAGceAEDCAQDAgFzMCwGCSsGAQQBnHgBBQQfFh1DTj1jYy11cy1l 17 | YXN0LTIuYW1hem9uYXdzLmNvbTBGBgkqhkiG9w0BAQowOaAPMA0GCWCGSAFlAwQC 18 | AgUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATCjAwIBAQOCAgEA 19 | E2CR10QkVTofcjmQbuu787J+H+OjzQLPIi/dUbP/LvZdYi/eWglYQPRbYxhxnIi1 20 | PB9R9c7LLhbNRhroog+TzrxyKLibEAW3rwn2iygPnsIemyL89wqtPNqEKNjhBXsb 21 | s/0bmf0rNJ3lugssCAzrIStkx8at0K/099BEs4FuUM5u97HVy+jqLdRa2XOHMgGa 22 | K7sNdR4swuLhfts9gOOX8ntJ+XkxtUx2mz449fXn8KN70mKa2YShhNd2JWJmv1jW 23 | K0I1UxVVwIOHBn/W8fQL5a061oRQQaW5+wPRTys0iEMmLU7+plC8LNWeEq93TfFY 24 | eUZ9EzinZ5S7z+c8J1FVWYNHGJauWj4lkjf+XGUZqXwTCPzou6tYJqqwWQEUUxXC 25 | M3QKgbkIGWg4WKHIAXGChbM86JLY0W6VueOHyu4S1Z4i81IcDp4cs83WxYWfCpKH 26 | Fq3Si2BhzZ0YGgK25JCkomh5Yf7dlsByyuQssf3TCqNmOfSFOTLvxfwTvLD5Omlm 27 | O1mPI0YaoZya4WcPxbpWS+2Em23/5inQvT+ZhvMNkljD2NVbhLVGP1v4YR+T2zaC 28 | 0qJ4YYJ2ERQTnEUlKnlF9bm6PwZSRHupK6ecsGjH+Bz5hBPbT09nEpJf0bWkzVSA 29 | AY8POFt3zBJiqONQuOlBpXzqKRKvFYQVEaX2EXQ+W6s= 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /internal/attestation/tdx/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "tdx", 5 | srcs = [ 6 | "issuer.go", 7 | "tdx.go", 8 | "validator.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/attestation/tdx", 11 | visibility = ["//:__subpackages__"], 12 | deps = [ 13 | "//internal/attestation", 14 | "//internal/attestation/measurements", 15 | "//internal/attestation/variant", 16 | "//internal/config", 17 | "@com_github_edgelesssys_go_tdx_qpl//tdx", 18 | "@com_github_edgelesssys_go_tdx_qpl//verification", 19 | "@com_github_edgelesssys_go_tdx_qpl//verification/types", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /internal/attestation/tdx/issuer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package tdx 8 | 9 | import ( 10 | "context" 11 | "encoding/json" 12 | "fmt" 13 | 14 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation" 15 | "github.com/flashbots/cvm-reverse-proxy/internal/attestation/variant" 16 | 17 | "github.com/edgelesssys/go-tdx-qpl/tdx" 18 | ) 19 | 20 | // Issuer is the TDX attestation issuer. 21 | type Issuer struct { 22 | variant.QEMUTDX 23 | 24 | open OpenFunc 25 | log attestation.Logger 26 | } 27 | 28 | // NewIssuer initializes a new TDX Issuer. 29 | func NewIssuer(log attestation.Logger) *Issuer { 30 | if log == nil { 31 | log = attestation.NOPLogger{} 32 | } 33 | return &Issuer{ 34 | open: Open, 35 | log: log, 36 | } 37 | } 38 | 39 | // Issue issues a TDX attestation document. 40 | func (i *Issuer) Issue(_ context.Context, userData []byte, nonce []byte) (attDoc []byte, err error) { 41 | i.log.Info("Issuing attestation statement") 42 | defer func() { 43 | if err != nil { 44 | i.log.Warn(fmt.Sprintf("Failed to issue attestation document: %s", err)) 45 | } 46 | }() 47 | 48 | handle, err := i.open() 49 | if err != nil { 50 | return nil, fmt.Errorf("opening TDX device: %w", err) 51 | } 52 | defer handle.Close() 53 | 54 | quote, err := tdx.GenerateQuote(handle, attestation.MakeExtraData(userData, nonce)) 55 | if err != nil { 56 | return nil, fmt.Errorf("generating quote: %w", err) 57 | } 58 | 59 | rawAttDoc, err := json.Marshal(tdxAttestationDocument{ 60 | RawQuote: quote, 61 | UserData: userData, 62 | }) 63 | if err != nil { 64 | return nil, fmt.Errorf("marshaling attestation document: %w", err) 65 | } 66 | 67 | return rawAttDoc, nil 68 | } 69 | -------------------------------------------------------------------------------- /internal/attestation/variant/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "variant", 5 | srcs = ["variant.go"], 6 | importpath = "cvm-reverse-proxy/internal/attestation/variant", 7 | visibility = ["//:__subpackages__"], 8 | deps = ["//internal/cloud/cloudprovider"], 9 | ) 10 | -------------------------------------------------------------------------------- /internal/attestation/vtpm/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "vtpm", 6 | srcs = [ 7 | "attestation.go", 8 | "vtpm.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/attestation/vtpm", 11 | visibility = ["//:__subpackages__"], 12 | deps = [ 13 | "//internal/attestation", 14 | "//internal/attestation/measurements", 15 | "@com_github_google_go_sev_guest//proto/sevsnp", 16 | "@com_github_google_go_tpm//legacy/tpm2", 17 | "@com_github_google_go_tpm_tools//client", 18 | "@com_github_google_go_tpm_tools//proto/attest", 19 | "@com_github_google_go_tpm_tools//proto/tpm", 20 | "@com_github_google_go_tpm_tools//server", 21 | ], 22 | ) 23 | 24 | go_test( 25 | name = "vtpm_test", 26 | srcs = [ 27 | "attestation_test.go", 28 | "vtpm_test.go", 29 | ], 30 | embed = [":vtpm"], 31 | # keep 32 | gotags = select({ 33 | "//bazel/settings:tpm_simulator_enabled": [], 34 | "//conditions:default": ["disable_tpm_simulator"], 35 | }), 36 | deps = [ 37 | "//internal/attestation/initialize", 38 | "//internal/attestation/measurements", 39 | "//internal/attestation/simulator", 40 | "//internal/logger", 41 | "@com_github_google_go_tpm//legacy/tpm2", 42 | "@com_github_google_go_tpm_tools//client", 43 | "@com_github_google_go_tpm_tools//proto/attest", 44 | "@com_github_google_go_tpm_tools//proto/tpm", 45 | "@com_github_stretchr_testify//assert", 46 | "@com_github_stretchr_testify//require", 47 | "@org_uber_go_goleak//:goleak", 48 | ], 49 | ) 50 | -------------------------------------------------------------------------------- /internal/attestation/vtpm/vtpm_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package vtpm 8 | 9 | import ( 10 | "testing" 11 | 12 | "go.uber.org/goleak" 13 | ) 14 | 15 | func TestMain(m *testing.M) { 16 | goleak.VerifyTestMain(m, goleak.IgnoreAnyFunction("github.com/bazelbuild/rules_go/go/tools/bzltestutil.RegisterTimeoutHandler.func1")) 17 | } 18 | -------------------------------------------------------------------------------- /internal/cloud/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "cloud", 5 | srcs = ["cloud.go"], 6 | importpath = "cvm-reverse-proxy/internal/cloud", 7 | visibility = ["//:__subpackages__"], 8 | ) 9 | -------------------------------------------------------------------------------- /internal/cloud/azure/iptables_cross.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | /* 4 | Copyright (c) Edgeless Systems GmbH 5 | 6 | SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package azure 10 | 11 | import ( 12 | "context" 13 | "log/slog" 14 | ) 15 | 16 | // PrepareControlPlaneNode is only supported on Linux. 17 | func (c *Cloud) PrepareControlPlaneNode(_ context.Context, _ *slog.Logger) error { 18 | panic("azure.*Cloud.PrepareControlPlaneNode is only supported on Linux") 19 | } 20 | -------------------------------------------------------------------------------- /internal/cloud/azureshared/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "azureshared", 6 | srcs = [ 7 | "appcredentials.go", 8 | "authmethod_string.go", 9 | "azureshared.go", 10 | "metadata.go", 11 | ], 12 | importpath = "cvm-reverse-proxy/internal/cloud/azureshared", 13 | visibility = ["//:__subpackages__"], 14 | ) 15 | 16 | go_test( 17 | name = "azureshared_test", 18 | srcs = [ 19 | "appcredentials_test.go", 20 | "metadata_test.go", 21 | ], 22 | embed = [":azureshared"], 23 | deps = [ 24 | "@com_github_stretchr_testify//assert", 25 | "@com_github_stretchr_testify//require", 26 | "@org_uber_go_goleak//:goleak", 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /internal/cloud/azureshared/authmethod_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=AuthMethod -trimprefix=AuthMethod"; DO NOT EDIT. 2 | 3 | package azureshared 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[AuthMethodUnknown-0] 12 | _ = x[AuthMethodServicePrincipal-1] 13 | _ = x[AuthMethodUserAssignedIdentity-2] 14 | } 15 | 16 | const _AuthMethod_name = "UnknownServicePrincipalUserAssignedIdentity" 17 | 18 | var _AuthMethod_index = [...]uint8{0, 7, 23, 43} 19 | 20 | func (i AuthMethod) String() string { 21 | if i >= AuthMethod(len(_AuthMethod_index)-1) { 22 | return "AuthMethod(" + strconv.FormatInt(int64(i), 10) + ")" 23 | } 24 | return _AuthMethod_name[_AuthMethod_index[i]:_AuthMethod_index[i+1]] 25 | } 26 | -------------------------------------------------------------------------------- /internal/cloud/azureshared/azureshared.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | Package gcpshared contains code to parse and define data types 9 | relevant for Microsoft Azure. 10 | 11 | This package is intended to have a minimal size and surface. If you 12 | have Azure related code that is not shared by multiple applications, 13 | or if the code interacts with the GCP API, please keep the code in 14 | the application's internal package or add it to the Azure cloud package. 15 | */ 16 | package azureshared 17 | -------------------------------------------------------------------------------- /internal/cloud/azureshared/metadata.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package azureshared 8 | 9 | import ( 10 | "errors" 11 | "fmt" 12 | "regexp" 13 | ) 14 | 15 | var azureVMSSProviderIDRegexp = regexp.MustCompile(`^azure:///subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachineScaleSets/([^/]+)/virtualMachines/([^/]+)$`) 16 | 17 | // BasicsFromProviderID extracts subscriptionID and resourceGroup from both types of valid azure providerID. 18 | func BasicsFromProviderID(providerID string) (subscriptionID, resourceGroup string, err error) { 19 | subscriptionID, resourceGroup, _, _, err = ScaleSetInformationFromProviderID(providerID) 20 | if err == nil { 21 | return subscriptionID, resourceGroup, nil 22 | } 23 | return "", "", fmt.Errorf("providerID %v is malformatted", providerID) 24 | } 25 | 26 | // ScaleSetInformationFromProviderID splits a provider's id belonging to an azure scaleset into core components. 27 | // A providerID for scale set VMs is build after the following schema: 28 | // - 'azure:///subscriptions//resourceGroups//providers/Microsoft.Compute/virtualMachineScaleSets//virtualMachines/' 29 | func ScaleSetInformationFromProviderID(providerID string) (subscriptionID, resourceGroup, scaleSet, instanceID string, err error) { 30 | matches := azureVMSSProviderIDRegexp.FindStringSubmatch(providerID) 31 | if len(matches) != 5 { 32 | return "", "", "", "", errors.New("error splitting providerID") 33 | } 34 | return matches[1], matches[2], matches[3], matches[4], nil 35 | } 36 | -------------------------------------------------------------------------------- /internal/cloud/cloud.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | # Cloud 9 | 10 | This package provides functions to interact with cloud providers. 11 | This is mainly used to fetch information about the current instance, or other instances of the Constellation cluster. 12 | 13 | Implementation of the cloud provider specific code is done in subpackages named after the CSP. 14 | Code that is commonly used by other packages that do not require actual interaction with the CSP API, 15 | such as CSP URI string parsing or data types, should go in a shared package instead. 16 | 17 | A cloud package should implement the following interface: 18 | 19 | type Cloud interface { 20 | List(ctx context.Context) ([]metadata.InstanceMetadata, error) 21 | Self(ctx context.Context) (metadata.InstanceMetadata, error) 22 | GetLoadBalancerEndpoint(ctx context.Context) (string, error) 23 | InitSecretHash(ctx context.Context) ([]byte, error) 24 | UID(ctx context.Context) (string, error) 25 | } 26 | */ 27 | package cloud 28 | 29 | const ( 30 | // TagRole is the tag/label key used to identify the role of a node. 31 | TagRole = "constellation-role" 32 | // TagUID is the tag/label key used to identify the UID of a cluster. 33 | TagUID = "constellation-uid" 34 | // TagInitSecretHash is the tag/label key used to identify the hash of the init secret. 35 | TagInitSecretHash = "constellation-init-secret-hash" 36 | // TagCustomEndpoint is the tag/label key used to identify the custom endpoint 37 | // or dns name that should be added to tls cert SANs. 38 | TagCustomEndpoint = "constellation-custom-endpoint" 39 | ) 40 | -------------------------------------------------------------------------------- /internal/cloud/cloudprovider/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "cloudprovider", 6 | srcs = [ 7 | "cloudprovider.go", 8 | "provider_string.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/cloud/cloudprovider", 11 | visibility = ["//:__subpackages__"], 12 | ) 13 | 14 | go_test( 15 | name = "cloudprovider_test", 16 | srcs = ["cloudprovider_test.go"], 17 | embed = [":cloudprovider"], 18 | deps = [ 19 | "@com_github_stretchr_testify//assert", 20 | "@in_gopkg_yaml_v3//:yaml_v3", 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /internal/cloud/cloudprovider/provider_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Provider"; DO NOT EDIT. 2 | 3 | package cloudprovider 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[Unknown-0] 12 | _ = x[AWS-1] 13 | _ = x[Azure-2] 14 | _ = x[GCP-3] 15 | _ = x[OpenStack-4] 16 | _ = x[QEMU-5] 17 | } 18 | 19 | const _Provider_name = "UnknownAWSAzureGCPOpenStackQEMU" 20 | 21 | var _Provider_index = [...]uint8{0, 7, 10, 15, 18, 27, 31} 22 | 23 | func (i Provider) String() string { 24 | if i >= Provider(len(_Provider_index)-1) { 25 | return "Provider(" + strconv.FormatInt(int64(i), 10) + ")" 26 | } 27 | return _Provider_name[_Provider_index[i]:_Provider_index[i+1]] 28 | } 29 | -------------------------------------------------------------------------------- /internal/cloud/gcp/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "gcp", 6 | srcs = [ 7 | "gcp.go", 8 | "interface.go", 9 | "wrappers.go", 10 | ], 11 | importpath = "cvm-reverse-proxy/internal/cloud/gcp", 12 | visibility = ["//:__subpackages__"], 13 | deps = [ 14 | "//internal/cloud", 15 | "//internal/cloud/gcpshared", 16 | "//internal/cloud/metadata", 17 | "//internal/role", 18 | "@com_github_googleapis_gax_go_v2//:gax-go", 19 | "@com_google_cloud_go_compute//apiv1", 20 | "@com_google_cloud_go_compute//apiv1/computepb", 21 | "@com_google_cloud_go_compute_metadata//:metadata", 22 | "@org_golang_google_api//iterator", 23 | "@org_golang_google_protobuf//proto", 24 | ], 25 | ) 26 | 27 | go_test( 28 | name = "gcp_test", 29 | srcs = ["gcp_test.go"], 30 | embed = [":gcp"], 31 | deps = [ 32 | "//internal/cloud", 33 | "//internal/cloud/metadata", 34 | "//internal/role", 35 | "@com_github_googleapis_gax_go_v2//:gax-go", 36 | "@com_github_stretchr_testify//assert", 37 | "@com_github_stretchr_testify//require", 38 | "@com_google_cloud_go_compute//apiv1/computepb", 39 | "@org_golang_google_api//iterator", 40 | "@org_golang_google_protobuf//proto", 41 | "@org_uber_go_goleak//:goleak", 42 | ], 43 | ) 44 | -------------------------------------------------------------------------------- /internal/cloud/gcp/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package gcp 8 | 9 | import ( 10 | "context" 11 | 12 | "cloud.google.com/go/compute/apiv1/computepb" 13 | "github.com/googleapis/gax-go/v2" 14 | ) 15 | 16 | type globalForwardingRulesAPI interface { 17 | List(ctx context.Context, req *computepb.ListGlobalForwardingRulesRequest, opts ...gax.CallOption) forwardingRuleIterator 18 | Close() error 19 | } 20 | 21 | type regionalForwardingRulesAPI interface { 22 | List(ctx context.Context, req *computepb.ListForwardingRulesRequest, opts ...gax.CallOption) forwardingRuleIterator 23 | Close() error 24 | } 25 | 26 | type imdsAPI interface { 27 | InstanceID() (string, error) 28 | ProjectID() (string, error) 29 | Zone() (string, error) 30 | InstanceName() (string, error) 31 | } 32 | 33 | type instanceAPI interface { 34 | Get(ctx context.Context, req *computepb.GetInstanceRequest, opts ...gax.CallOption) (*computepb.Instance, error) 35 | List(ctx context.Context, req *computepb.ListInstancesRequest, opts ...gax.CallOption) instanceIterator 36 | Close() error 37 | } 38 | 39 | type subnetAPI interface { 40 | Get(ctx context.Context, req *computepb.GetSubnetworkRequest, opts ...gax.CallOption) (*computepb.Subnetwork, error) 41 | Close() error 42 | } 43 | 44 | type zoneAPI interface { 45 | List(ctx context.Context, req *computepb.ListZonesRequest, opts ...gax.CallOption) zoneIterator 46 | } 47 | -------------------------------------------------------------------------------- /internal/cloud/gcpshared/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "gcpshared", 6 | srcs = [ 7 | "gcpshared.go", 8 | "providerid.go", 9 | "serviceaccountkey.go", 10 | ], 11 | importpath = "cvm-reverse-proxy/internal/cloud/gcpshared", 12 | visibility = ["//:__subpackages__"], 13 | ) 14 | 15 | go_test( 16 | name = "gcpshared_test", 17 | srcs = [ 18 | "providerid_test.go", 19 | "serviceaccountkey_test.go", 20 | ], 21 | embed = [":gcpshared"], 22 | deps = [ 23 | "@com_github_stretchr_testify//assert", 24 | "@com_github_stretchr_testify//require", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /internal/cloud/gcpshared/gcpshared.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | SPDX-License-Identifier: AGPL-3.0-only 4 | */ 5 | 6 | /* 7 | Package gcpshared contains code to parse and define data types 8 | relevant for Google Cloud Platform. 9 | 10 | This package is intended to have a minimal size and surface. If you 11 | have GCP related code that is not shared by multiple applications, 12 | or if the code interacts with the GCP API, please keep the code in 13 | the application's internal package or add it to the GCP cloud package. 14 | */ 15 | package gcpshared 16 | -------------------------------------------------------------------------------- /internal/cloud/gcpshared/providerid.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package gcpshared 8 | 9 | import ( 10 | "fmt" 11 | "regexp" 12 | ) 13 | 14 | var providerIDRegex = regexp.MustCompile(`^gce://([^/]+)/([^/]+)/([^/]+)$`) 15 | 16 | // SplitProviderID splits a k8s provider ID for GCP instances into its core components. 17 | // A provider ID is build after the schema 'gce:////' 18 | func SplitProviderID(providerID string) (project, zone, instance string, err error) { 19 | matches := providerIDRegex.FindStringSubmatch(providerID) 20 | if len(matches) != 4 { 21 | return "", "", "", fmt.Errorf("error splitting providerID: %v", providerID) 22 | } 23 | return matches[1], matches[2], matches[3], nil 24 | } 25 | 26 | // JoinProviderID builds a k8s provider ID for GCP instances. 27 | // A providerID is build after the schema 'gce:////' 28 | func JoinProviderID(project, zone, instanceName string) string { 29 | return fmt.Sprintf("gce://%v/%v/%v", project, zone, instanceName) 30 | } 31 | -------------------------------------------------------------------------------- /internal/cloud/metadata/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "metadata", 5 | srcs = ["metadata.go"], 6 | importpath = "cvm-reverse-proxy/internal/cloud/metadata", 7 | visibility = ["//:__subpackages__"], 8 | deps = ["//internal/role"], 9 | ) 10 | -------------------------------------------------------------------------------- /internal/cloud/metadata/metadata.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package metadata 8 | 9 | import ( 10 | "context" 11 | 12 | "github.com/flashbots/cvm-reverse-proxy/internal/role" 13 | ) 14 | 15 | // InstanceMetadata describes metadata of a peer. 16 | type InstanceMetadata struct { 17 | Name string 18 | ProviderID string 19 | Role role.Role 20 | // VPCIP is the primary IP address of the instance in the VPC. 21 | VPCIP string 22 | 23 | // SecondaryIPRange is the VPC wide CIDR from which subnets are attached to VMs as AliasIPRanges. 24 | // May be empty on certain CSPs. 25 | SecondaryIPRange string 26 | // AliasIPRanges is a list of IP ranges that are attached. 27 | // May be empty on certain CSPs. 28 | AliasIPRanges []string 29 | } 30 | 31 | // InstanceSelfer provide instance metadata about themselves. 32 | type InstanceSelfer interface { 33 | // Self retrieves the current instance. 34 | Self(ctx context.Context) (InstanceMetadata, error) 35 | } 36 | 37 | // InstanceLister list information about instance metadata. 38 | type InstanceLister interface { 39 | // List retrieves all instances belonging to the current constellation. 40 | List(ctx context.Context) ([]InstanceMetadata, error) 41 | } 42 | -------------------------------------------------------------------------------- /internal/cloud/openstack/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "openstack", 6 | srcs = [ 7 | "accountkey.go", 8 | "api.go", 9 | "imds.go", 10 | "openstack.go", 11 | "plumbing.go", 12 | "wrappers.go", 13 | ], 14 | importpath = "cvm-reverse-proxy/internal/cloud/openstack", 15 | visibility = ["//:__subpackages__"], 16 | deps = [ 17 | "//internal/cloud", 18 | "//internal/cloud/metadata", 19 | "//internal/constants", 20 | "//internal/role", 21 | "@com_github_gophercloud_gophercloud_v2//:gophercloud", 22 | "@com_github_gophercloud_gophercloud_v2//openstack/compute/v2/servers", 23 | "@com_github_gophercloud_gophercloud_v2//openstack/networking/v2/networks", 24 | "@com_github_gophercloud_gophercloud_v2//openstack/networking/v2/subnets", 25 | "@com_github_gophercloud_gophercloud_v2//pagination", 26 | "@com_github_gophercloud_utils_v2//openstack/clientconfig", 27 | ], 28 | ) 29 | 30 | go_test( 31 | name = "openstack_test", 32 | srcs = [ 33 | "accountkey_test.go", 34 | "api_test.go", 35 | "imds_test.go", 36 | "openstack_test.go", 37 | "plumbing_test.go", 38 | ], 39 | embed = [":openstack"], 40 | deps = [ 41 | "//internal/cloud/metadata", 42 | "//internal/role", 43 | "@com_github_gophercloud_gophercloud_v2//:gophercloud", 44 | "@com_github_gophercloud_gophercloud_v2//openstack/compute/v2/servers", 45 | "@com_github_gophercloud_gophercloud_v2//openstack/networking/v2/networks", 46 | "@com_github_gophercloud_gophercloud_v2//openstack/networking/v2/subnets", 47 | "@com_github_gophercloud_gophercloud_v2//pagination", 48 | "@com_github_stretchr_testify//assert", 49 | "@com_github_stretchr_testify//require", 50 | ], 51 | ) 52 | -------------------------------------------------------------------------------- /internal/cloud/openstack/api.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package openstack 8 | 9 | import ( 10 | "context" 11 | 12 | "github.com/flashbots/cvm-reverse-proxy/internal/role" 13 | 14 | "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" 15 | "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" 16 | "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" 17 | "github.com/gophercloud/gophercloud/v2/pagination" 18 | ) 19 | 20 | type imdsAPI interface { 21 | providerID(ctx context.Context) (string, error) 22 | name(ctx context.Context) (string, error) 23 | projectID(ctx context.Context) (string, error) 24 | uid(ctx context.Context) (string, error) 25 | initSecretHash(ctx context.Context) (string, error) 26 | role(ctx context.Context) (role.Role, error) 27 | vpcIP(ctx context.Context) (string, error) 28 | loadBalancerEndpoint(ctx context.Context) (string, error) 29 | } 30 | 31 | type serversAPI interface { 32 | ListServers(opts servers.ListOptsBuilder) pagerAPI 33 | ListNetworks(opts networks.ListOptsBuilder) pagerAPI 34 | ListSubnets(opts subnets.ListOpts) pagerAPI 35 | } 36 | 37 | type pagerAPI interface { 38 | AllPages(context.Context) (pagination.Page, error) 39 | } 40 | -------------------------------------------------------------------------------- /internal/cloud/openstack/clouds/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "clouds", 5 | srcs = [ 6 | "clouds.go", 7 | "read.go", 8 | ], 9 | importpath = "cvm-reverse-proxy/internal/cloud/openstack/clouds", 10 | visibility = ["//:__subpackages__"], 11 | deps = [ 12 | "//internal/file", 13 | "@com_github_mitchellh_go_homedir//:go-homedir", 14 | ], 15 | ) 16 | -------------------------------------------------------------------------------- /internal/cloud/openstack/clouds/read.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | package clouds 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | "path/filepath" 12 | 13 | "github.com/mitchellh/go-homedir" 14 | 15 | "github.com/flashbots/cvm-reverse-proxy/internal/file" 16 | ) 17 | 18 | // ReadCloudsYAML reads a clouds.yaml file and returns its contents. 19 | func ReadCloudsYAML(fileHandler file.Handler, path string) (Clouds, error) { 20 | // Order of operations as performed by the OpenStack CLI: 21 | 22 | // Define a search path for clouds.yaml: 23 | // 1. If OS_CLIENT_CONFIG_FILE is set, use it as search path 24 | // 2. Otherwise, use the following paths: 25 | // - current directory 26 | // - `openstack` directory under standard user config directory (e.g. ~/.config/openstack) 27 | // - /etc/openstack (Unix only) 28 | 29 | var searchPaths []string 30 | if path != "" { 31 | expanded, err := homedir.Expand(path) 32 | if err == nil { 33 | searchPaths = append(searchPaths, expanded) 34 | } else { 35 | searchPaths = append(searchPaths, path) 36 | } 37 | } else if osClientConfigFile := os.Getenv("OS_CLIENT_CONFIG_FILE"); osClientConfigFile != "" { 38 | searchPaths = append(searchPaths, filepath.Join(osClientConfigFile, "clouds.yaml")) 39 | } else { 40 | searchPaths = append(searchPaths, "clouds.yaml") 41 | confDir, err := os.UserConfigDir() 42 | if err != nil { 43 | return Clouds{}, fmt.Errorf("getting user config directory: %w", err) 44 | } 45 | searchPaths = append(searchPaths, filepath.Join(confDir, "openstack", "clouds.yaml")) 46 | if os.PathSeparator == '/' { 47 | searchPaths = append(searchPaths, "/etc/openstack/clouds.yaml") 48 | } 49 | } 50 | 51 | var cloudsYAML Clouds 52 | for _, path := range searchPaths { 53 | if err := fileHandler.ReadYAML(path, &cloudsYAML); err == nil { 54 | return cloudsYAML, nil 55 | } 56 | } 57 | 58 | return Clouds{}, fmt.Errorf("clouds.yaml not found in search paths: %v", searchPaths) 59 | } 60 | -------------------------------------------------------------------------------- /internal/cloud/openstack/wrappers.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package openstack 8 | 9 | import ( 10 | "github.com/gophercloud/gophercloud/v2" 11 | "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" 12 | "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" 13 | "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" 14 | ) 15 | 16 | type apiClient struct { 17 | servers *gophercloud.ServiceClient 18 | networks *gophercloud.ServiceClient 19 | } 20 | 21 | func (c *apiClient) ListServers(opts servers.ListOptsBuilder) pagerAPI { 22 | return servers.List(c.servers, opts) 23 | } 24 | 25 | func (c *apiClient) ListNetworks(opts networks.ListOptsBuilder) pagerAPI { 26 | return networks.List(c.networks, opts) 27 | } 28 | 29 | func (c *apiClient) ListSubnets(opts subnets.ListOpts) pagerAPI { 30 | return subnets.List(c.networks, opts) 31 | } 32 | -------------------------------------------------------------------------------- /internal/cloud/qemu/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "qemu", 5 | srcs = ["qemu.go"], 6 | importpath = "cvm-reverse-proxy/internal/cloud/qemu", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/cloud/metadata", 10 | "//internal/constants", 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /internal/compatibility/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "compatibility", 6 | srcs = ["compatibility.go"], 7 | importpath = "cvm-reverse-proxy/internal/compatibility", 8 | visibility = ["//:__subpackages__"], 9 | deps = ["@org_golang_x_mod//semver"], 10 | ) 11 | 12 | go_test( 13 | name = "compatibility_test", 14 | srcs = ["compatibility_test.go"], 15 | embed = [":compatibility"], 16 | deps = ["@com_github_stretchr_testify//assert"], 17 | ) 18 | -------------------------------------------------------------------------------- /internal/config/disktypes/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "disktypes", 5 | srcs = [ 6 | "aws.go", 7 | "azure.go", 8 | "gcp.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/config/disktypes", 11 | visibility = ["//:__subpackages__"], 12 | ) 13 | -------------------------------------------------------------------------------- /internal/config/disktypes/aws.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package disktypes 8 | 9 | // AWSDiskTypes is derived from: 10 | // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html (Last updated: August 1st, 2023). 11 | var AWSDiskTypes = []string{ 12 | "gp2", 13 | "gp3", 14 | "st1", 15 | "sc1", 16 | "io1", 17 | } 18 | -------------------------------------------------------------------------------- /internal/config/disktypes/azure.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package disktypes 8 | 9 | // AzureDiskTypes are valid Azure disk types. 10 | var AzureDiskTypes = []string{ 11 | "Premium_LRS", 12 | "Premium_ZRS", 13 | "Standard_LRS", 14 | "StandardSSD_LRS", 15 | "StandardSSD_ZRS", 16 | } 17 | -------------------------------------------------------------------------------- /internal/config/disktypes/gcp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package disktypes 8 | 9 | // GCPDiskTypes are valid GCP disk types. 10 | var GCPDiskTypes = []string{ 11 | "pd-standard", 12 | "pd-balanced", 13 | "pd-ssd", 14 | } 15 | -------------------------------------------------------------------------------- /internal/config/image_enterprise.go: -------------------------------------------------------------------------------- 1 | //go:build enterprise 2 | 3 | /* 4 | Copyright (c) Edgeless Systems GmbH 5 | 6 | SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package config 10 | 11 | const ( 12 | // defaultImage is the default image to use. 13 | defaultImage = "ref/main/stream/nightly/v2.18.0-pre.0.20240903162608-eab9aca26fca" 14 | ) 15 | -------------------------------------------------------------------------------- /internal/config/image_oss.go: -------------------------------------------------------------------------------- 1 | //go:build !enterprise 2 | 3 | /* 4 | Copyright (c) Edgeless Systems GmbH 5 | 6 | SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package config 10 | 11 | const ( 12 | // defaultImage is the default image to use. 13 | defaultImage = "" 14 | ) 15 | -------------------------------------------------------------------------------- /internal/config/imageversion/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # gazelle:ignore 2 | 3 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 4 | load("//bazel/oci:containers.bzl", "config_containers") 5 | load("//bazel/oci:pin.bzl", "oci_go_source") 6 | 7 | GENERATED_SRCS = [ 8 | ":" + container["name"] 9 | for container in config_containers() 10 | ] 11 | 12 | go_library( 13 | name = "imageversion", 14 | srcs = ["imageversion.go"] + GENERATED_SRCS, # keep 15 | importpath = "cvm-reverse-proxy/internal/config/imageversion", 16 | visibility = ["//:__subpackages__"], 17 | deps = ["//internal/containerimage"], 18 | ) 19 | 20 | [ 21 | oci_go_source( 22 | name = container["name"], 23 | identifier = container["identifier"], 24 | image_name = container["image_name"], 25 | oci = container["oci"], 26 | package = "imageversion", 27 | repotag_file = container["repotag_file"], 28 | visibility = ["//:__subpackages__"], 29 | ) 30 | for container in config_containers() 31 | ] 32 | -------------------------------------------------------------------------------- /internal/config/imageversion/imageversion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package imageversion contains the pinned container images for the config. 8 | package imageversion 9 | 10 | import "github.com/flashbots/cvm-reverse-proxy/internal/containerimage" 11 | 12 | // QEMUMetadata is the image of the QEMU metadata api service. 13 | func QEMUMetadata() string { 14 | return defaultQEMUMetadata.String() 15 | } 16 | 17 | // Libvirt is the image of the libvirt container. 18 | func Libvirt() string { 19 | return defaultLibvirt.String() 20 | } 21 | 22 | var ( 23 | defaultQEMUMetadata = containerimage.Image{ 24 | Registry: qemuMetadataRegistry, 25 | Prefix: qemuMetadataPrefix, 26 | Name: qemuMetadataName, 27 | Tag: qemuMetadataTag, 28 | Digest: qemuMetadataDigest, 29 | } 30 | defaultLibvirt = containerimage.Image{ 31 | Registry: libvirtRegistry, 32 | Prefix: libvirtPrefix, 33 | Name: libvirtName, 34 | Tag: libvirtTag, 35 | Digest: libvirtDigest, 36 | } 37 | ) 38 | -------------------------------------------------------------------------------- /internal/config/imageversion/placeholder.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | package imageversion 7 | 8 | // This file is only used if `go build` is used instead of Bazel. 9 | // It contains placeholder values for the container images so that everything 10 | // still compiles. 11 | 12 | const ( 13 | qemuMetadataRegistry = "placeholder" 14 | qemuMetadataPrefix = "placeholder" 15 | qemuMetadataName = "placeholder" 16 | qemuMetadataDigest = "placeholder" 17 | qemuMetadataTag = "placeholder" 18 | 19 | libvirtRegistry = "placeholder" 20 | libvirtPrefix = "placeholder" 21 | libvirtName = "placeholder" 22 | libvirtDigest = "placeholder" 23 | libvirtTag = "placeholder" 24 | ) 25 | -------------------------------------------------------------------------------- /internal/config/instancetypes/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "instancetypes", 5 | srcs = [ 6 | "aws.go", 7 | "azure.go", 8 | "gcp.go", 9 | "stackit.go", 10 | ], 11 | importpath = "cvm-reverse-proxy/internal/config/instancetypes", 12 | visibility = ["//:__subpackages__"], 13 | ) 14 | -------------------------------------------------------------------------------- /internal/config/instancetypes/aws.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package instancetypes 8 | 9 | // AWSSupportedInstanceFamilies is derived from: 10 | // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enable-nitrotpm-prerequisites.html (Last updated: October 20th, 2022). 11 | var AWSSupportedInstanceFamilies = []string{ 12 | "C5", 13 | "C5a", 14 | "C5ad", 15 | "C5d", 16 | "C5n", 17 | "C6a", 18 | "C6i", 19 | "D3", 20 | "D3en", 21 | "G4dn", 22 | "G5", 23 | "Hpc6a", 24 | "I3en", 25 | "I4i", 26 | "Inf1", 27 | "M5", 28 | "M5a", 29 | "M5ad", 30 | "M5d", 31 | "M5dn", 32 | "M5n", 33 | "M5zn", 34 | "M6a", 35 | "M6i", 36 | "R5", 37 | "R5a", 38 | "R5ad", 39 | "R5b", 40 | "R5d", 41 | "R5dn", 42 | "R5n", 43 | "R6a", 44 | "R6i", 45 | "U-3tb1", 46 | "U-6tb1", 47 | "U-9tb1", 48 | "U-12tb1", 49 | "X2idn", 50 | "X2iedn", 51 | "X2iezn", 52 | "z1d", 53 | } 54 | 55 | // AWSSNPSupportedInstanceFamilies is derived from: 56 | // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/snp-requirements.html 57 | var AWSSNPSupportedInstanceFamilies = []string{ 58 | "C6a", 59 | "M6a", 60 | "R6a", 61 | } 62 | -------------------------------------------------------------------------------- /internal/config/instancetypes/gcp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package instancetypes 8 | 9 | // GCPInstanceTypes are valid GCP instance types. 10 | var GCPInstanceTypes = []string{ 11 | "n2d-standard-4", 12 | "n2d-standard-8", 13 | "n2d-standard-16", 14 | "n2d-standard-32", 15 | "n2d-standard-48", 16 | "n2d-standard-64", 17 | "n2d-standard-80", 18 | "n2d-standard-96", 19 | "n2d-standard-128", 20 | "n2d-standard-224", 21 | "n2d-highmem-4", 22 | "n2d-highmem-8", 23 | "n2d-highmem-16", 24 | "n2d-highmem-32", 25 | "n2d-highmem-48", 26 | "n2d-highmem-64", 27 | "n2d-highmem-80", 28 | "n2d-highmem-96", 29 | "n2d-highcpu-4", 30 | "n2d-highcpu-8", 31 | "n2d-highcpu-16", 32 | "n2d-highcpu-32", 33 | "n2d-highcpu-48", 34 | "n2d-highcpu-64", 35 | "n2d-highcpu-80", 36 | "n2d-highcpu-96", 37 | "n2d-highcpu-128", 38 | "n2d-highcpu-224", 39 | "c2d-standard-4", 40 | "c2d-standard-8", 41 | "c2d-standard-16", 42 | "c2d-standard-32", 43 | "c2d-standard-56", 44 | "c2d-standard-112", 45 | "c2d-highcpu-4", 46 | "c2d-highcpu-8", 47 | "c2d-highcpu-16", 48 | "c2d-highcpu-32", 49 | "c2d-highcpu-56", 50 | "c2d-highcpu-112", 51 | "c2d-highmem-4", 52 | "c2d-highmem-8", 53 | "c2d-highmem-16", 54 | "c2d-highmem-32", 55 | "c2d-highmem-56", 56 | "c2d-highmem-112", 57 | } 58 | -------------------------------------------------------------------------------- /internal/config/instancetypes/stackit.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package instancetypes 8 | 9 | // STACKITInstanceTypes are valid STACKIT instance types. 10 | var STACKITInstanceTypes = []string{ 11 | "m1a.2cd", 12 | "m1a.4cd", 13 | "m1a.8cd", 14 | "m1a.16cd", 15 | "m1a.30cd", 16 | } 17 | -------------------------------------------------------------------------------- /internal/config/migration/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "migration", 5 | srcs = ["migration.go"], 6 | importpath = "cvm-reverse-proxy/internal/config/migration", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/attestation/idkeydigest", 10 | "//internal/attestation/measurements", 11 | "//internal/config", 12 | "//internal/file", 13 | "//internal/role", 14 | "//internal/semver", 15 | "//internal/versions", 16 | ], 17 | ) 18 | -------------------------------------------------------------------------------- /internal/config/testdata/configAWSV2.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | stateDiskSizeGB: 16 3 | kubernetesVersion: "1.23" 4 | debugCluster: false 5 | image: v2.5.0 6 | provider: 7 | aws: 8 | region: "us-east-2" 9 | zone: "us-east-2a" 10 | instanceType: c5.xlarge 11 | stateDiskType: gp2 12 | iamProfileControlPlane: "control_plane_instance_profile" 13 | iamProfileWorkerNodes: "node_instance_profile" 14 | measurements: 15 | 4: 16 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 17 | warnOnly: false 18 | 8: 19 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 20 | warnOnly: false 21 | 9: 22 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 23 | warnOnly: false 24 | 11: 25 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 26 | warnOnly: false 27 | 12: 28 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 29 | warnOnly: false 30 | 13: 31 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 32 | warnOnly: false 33 | 15: 34 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 35 | warnOnly: false 36 | -------------------------------------------------------------------------------- /internal/config/testdata/configAzureV2MultipleIDKeyDigest.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | stateDiskSizeGB: 16 3 | debugCluster: false 4 | image: v2.5.0 5 | kubernetesVersion: "1.23" 6 | provider: 7 | azure: 8 | tenant: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" 9 | subscription: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" 10 | location: "West Europe" 11 | resourceGroup: "resourceGroup" 12 | userAssignedIdentity: /subscriptions/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/resourceGroups/resourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/ConstellationUAMI 13 | stateDiskType: Premium_LRS 14 | confidentialVM: true 15 | instanceType: Standard_DC4as_v5 16 | idKeyDigest: 17 | - 57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696 18 | - 0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3 19 | enforceIdKeyDigest: false 20 | secureBoot: false 21 | deployCSIDriver: true 22 | measurements: 23 | 4: 24 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 25 | warnOnly: false 26 | 8: 27 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 28 | warnOnly: false 29 | 9: 30 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 31 | warnOnly: false 32 | 11: 33 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 34 | warnOnly: false 35 | 12: 36 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 37 | warnOnly: false 38 | 13: 39 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 40 | warnOnly: false 41 | 15: 42 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 43 | warnOnly: false 44 | -------------------------------------------------------------------------------- /internal/config/testdata/configAzureV2SingleIDKeyDigest.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | stateDiskSizeGB: 16 3 | debugCluster: false 4 | image: v2.5.0 5 | kubernetesVersion: "1.23" 6 | provider: 7 | azure: 8 | subscription: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" 9 | tenant: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" 10 | location: "West Europe" 11 | resourceGroup: "resourceGroup" 12 | userAssignedIdentity: /subscriptions/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/resourceGroups/resourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/ConstellationUAMI 13 | stateDiskType: Premium_LRS 14 | confidentialVM: true 15 | instanceType: Standard_DC4as_v5 16 | idKeyDigest: "0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3" 17 | enforceIdKeyDigest: false 18 | secureBoot: false 19 | deployCSIDriver: true 20 | measurements: 21 | 4: 22 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 23 | warnOnly: false 24 | 8: 25 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 26 | warnOnly: false 27 | 9: 28 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 29 | warnOnly: false 30 | 11: 31 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 32 | warnOnly: false 33 | 12: 34 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 35 | warnOnly: false 36 | 13: 37 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 38 | warnOnly: false 39 | 15: 40 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 41 | warnOnly: false 42 | -------------------------------------------------------------------------------- /internal/config/testdata/configGCPV2.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | image: "v2.5.0" 3 | stateDiskSizeGB: 16 4 | kubernetesVersion: "1.23" 5 | debugCluster: false 6 | provider: 7 | gcp: 8 | project: "project-12345" 9 | region: "europe-west3" 10 | zone: "europe-west3-b" 11 | serviceAccountKeyPath: "serviceAccountKey.json" 12 | instanceType: n2d-standard-4 13 | stateDiskType: pd-ssd 14 | deployCSIDriver: true 15 | measurements: 16 | 4: 17 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 18 | warnOnly: false 19 | 8: 20 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 21 | warnOnly: false 22 | 9: 23 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 24 | warnOnly: false 25 | 11: 26 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 27 | warnOnly: false 28 | 12: 29 | expected: "1234123412341234123412341234123412341234123412341234123412341234" 30 | warnOnly: false 31 | 13: 32 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 33 | warnOnly: false 34 | 15: 35 | expected: "0000000000000000000000000000000000000000000000000000000000000000" 36 | warnOnly: false 37 | -------------------------------------------------------------------------------- /internal/constants/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "constants", 5 | srcs = [ 6 | "constants.go", 7 | # keep 8 | "enterprise.go", 9 | # keep 10 | "oss.go", 11 | ], 12 | importpath = "cvm-reverse-proxy/internal/constants", 13 | visibility = ["//:__subpackages__"], 14 | x_defs = { 15 | "commit": "{STABLE_STAMP_COMMIT}", 16 | "state": "{STABLE_STAMP_STATE}", 17 | "timestamp": "{STABLE_STAMP_TIME}", 18 | "versionInfo": "{STABLE_STAMP_VERSION}", 19 | }, 20 | deps = ["//internal/semver"], 21 | ) 22 | -------------------------------------------------------------------------------- /internal/constants/enterprise.go: -------------------------------------------------------------------------------- 1 | //go:build enterprise 2 | 3 | /* 4 | Copyright (c) Edgeless Systems GmbH 5 | 6 | SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package constants 10 | 11 | // VersionBuild is the category of the current build. 12 | const VersionBuild = "Enterprise build; see documentation for license agreement" 13 | -------------------------------------------------------------------------------- /internal/constants/oss.go: -------------------------------------------------------------------------------- 1 | //go:build !enterprise 2 | 3 | /* 4 | Copyright (c) Edgeless Systems GmbH 5 | 6 | SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package constants 10 | 11 | // VersionBuild is the category of the current build. 12 | const VersionBuild = "Open-source software build; AGPL-3.0-only applies" 13 | -------------------------------------------------------------------------------- /internal/containerimage/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "containerimage", 5 | srcs = ["containerimage.go"], 6 | importpath = "cvm-reverse-proxy/internal/containerimage", 7 | visibility = ["//:__subpackages__"], 8 | ) 9 | -------------------------------------------------------------------------------- /internal/crypto/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "crypto", 6 | srcs = ["crypto.go"], 7 | importpath = "cvm-reverse-proxy/internal/crypto", 8 | visibility = ["//:__subpackages__"], 9 | deps = ["@org_golang_x_crypto//hkdf"], 10 | ) 11 | 12 | go_test( 13 | name = "crypto_test", 14 | srcs = ["crypto_test.go"], 15 | embed = [":crypto"], 16 | deps = [ 17 | "//internal/crypto/testvector", 18 | "@com_github_stretchr_testify//assert", 19 | "@com_github_stretchr_testify//require", 20 | "@org_uber_go_goleak//:goleak", 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /internal/crypto/testvector/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "testvector", 5 | srcs = ["testvector.go"], 6 | importpath = "cvm-reverse-proxy/internal/crypto/testvector", 7 | visibility = ["//:__subpackages__"], 8 | ) 9 | -------------------------------------------------------------------------------- /internal/cryptsetup/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "cryptsetup", 5 | srcs = [ 6 | "cryptsetup.go", 7 | "cryptsetup_cgo.go", 8 | "cryptsetup_cross.go", 9 | ], 10 | # keep 11 | cdeps = [ 12 | "@//nix/cc:cryptsetup", 13 | ], 14 | cgo = True, 15 | importpath = "cvm-reverse-proxy/internal/cryptsetup", 16 | visibility = ["//:__subpackages__"], 17 | deps = select({ 18 | "@io_bazel_rules_go//go/platform:android": [ 19 | "@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup", 20 | ], 21 | "@io_bazel_rules_go//go/platform:linux": [ 22 | "@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup", 23 | ], 24 | "//conditions:default": [], 25 | }), 26 | ) 27 | -------------------------------------------------------------------------------- /internal/cryptsetup/cryptsetup_cross.go: -------------------------------------------------------------------------------- 1 | //go:build !linux || !cgo 2 | 3 | /* 4 | Copyright (c) Edgeless Systems GmbH 5 | 6 | SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | package cryptsetup 9 | 10 | import ( 11 | "errors" 12 | ) 13 | 14 | const ( 15 | // ReadWriteQueueBypass is a flag to disable the write and read workqueues for a crypt device. 16 | ReadWriteQueueBypass = cryptActivateNoReadWorkqueue | cryptActivateNoWriteWorkqueue 17 | cryptActivateNoReadWorkqueue = 0x1000000 18 | cryptActivateNoWriteWorkqueue = 0x2000000 19 | wipeFlags = 0x10 | 0x1000 20 | wipePattern = 0 21 | ) 22 | 23 | var errCGONotSupported = errors.New("using cryptsetup requires building with CGO") 24 | 25 | func format(_ cryptDevice, _ bool) error { 26 | return errCGONotSupported 27 | } 28 | 29 | func initByDevicePath(_ string) (cryptDevice, error) { 30 | return nil, errCGONotSupported 31 | } 32 | 33 | func initByName(_ string) (cryptDevice, error) { 34 | return nil, errCGONotSupported 35 | } 36 | 37 | func loadLUKS2(_ cryptDevice) error { 38 | return errCGONotSupported 39 | } 40 | -------------------------------------------------------------------------------- /internal/encoding/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "encoding", 6 | srcs = ["encoding.go"], 7 | importpath = "cvm-reverse-proxy/internal/encoding", 8 | visibility = ["//:__subpackages__"], 9 | ) 10 | 11 | go_test( 12 | name = "encoding_test", 13 | srcs = ["encoding_test.go"], 14 | embed = [":encoding"], 15 | deps = [ 16 | "@com_github_stretchr_testify//assert", 17 | "@com_github_stretchr_testify//require", 18 | "@in_gopkg_yaml_v3//:yaml_v3", 19 | ], 20 | ) 21 | -------------------------------------------------------------------------------- /internal/file/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "file", 6 | srcs = ["file.go"], 7 | importpath = "cvm-reverse-proxy/internal/file", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "@com_github_siderolabs_talos_pkg_machinery//config/encoder", 11 | "@com_github_spf13_afero//:afero", 12 | "@in_gopkg_yaml_v3//:yaml_v3", 13 | ], 14 | ) 15 | 16 | go_test( 17 | name = "file_test", 18 | srcs = ["file_test.go"], 19 | embed = [":file"], 20 | deps = [ 21 | "//internal/constants", 22 | "@com_github_spf13_afero//:afero", 23 | "@com_github_stretchr_testify//assert", 24 | "@com_github_stretchr_testify//require", 25 | "@in_gopkg_yaml_v3//:yaml_v3", 26 | "@org_uber_go_goleak//:goleak", 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /internal/grpc/atlscredentials/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "atlscredentials", 6 | srcs = ["atlscredentials.go"], 7 | importpath = "cvm-reverse-proxy/internal/grpc/atlscredentials", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/atls", 11 | "@org_golang_google_grpc//credentials", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "atlscredentials_test", 17 | srcs = ["atlscredentials_test.go"], 18 | embed = [":atlscredentials"], 19 | deps = [ 20 | "//bootstrapper/initproto", 21 | "//internal/atls", 22 | "@com_github_stretchr_testify//assert", 23 | "@com_github_stretchr_testify//require", 24 | "@org_golang_google_grpc//:grpc", 25 | "@org_golang_google_grpc//test/bufconn", 26 | "@org_uber_go_goleak//:goleak", 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /internal/grpc/dialer/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "dialer", 6 | srcs = ["dialer.go"], 7 | importpath = "cvm-reverse-proxy/internal/grpc/dialer", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/atls", 11 | "//internal/grpc/atlscredentials", 12 | "@org_golang_google_grpc//:grpc", 13 | "@org_golang_google_grpc//credentials/insecure", 14 | ], 15 | ) 16 | 17 | go_test( 18 | name = "dialer_test", 19 | srcs = ["dialer_test.go"], 20 | embed = [":dialer"], 21 | deps = [ 22 | "//internal/atls", 23 | "//internal/attestation/variant", 24 | "//internal/grpc/atlscredentials", 25 | "//internal/grpc/testdialer", 26 | "@com_github_stretchr_testify//assert", 27 | "@com_github_stretchr_testify//require", 28 | "@org_golang_google_grpc//:grpc", 29 | "@org_golang_google_grpc//interop/grpc_testing", 30 | "@org_uber_go_goleak//:goleak", 31 | ], 32 | ) 33 | -------------------------------------------------------------------------------- /internal/grpc/grpclog/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "grpclog", 6 | srcs = ["grpclog.go"], 7 | importpath = "cvm-reverse-proxy/internal/grpc/grpclog", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "@org_golang_google_grpc//connectivity", 11 | "@org_golang_google_grpc//peer", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "grpclog_test", 17 | srcs = ["grpclog_test.go"], 18 | embed = [":grpclog"], 19 | deps = [ 20 | "@com_github_stretchr_testify//assert", 21 | "@com_github_stretchr_testify//require", 22 | "@org_golang_google_grpc//connectivity", 23 | ], 24 | ) 25 | -------------------------------------------------------------------------------- /internal/grpc/grpclog/grpclog.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // grpclog provides a logging utilities for gRPC. 8 | package grpclog 9 | 10 | import ( 11 | "context" 12 | "fmt" 13 | "sync" 14 | 15 | "google.golang.org/grpc/connectivity" 16 | "google.golang.org/grpc/peer" 17 | ) 18 | 19 | // PeerAddrFromContext returns a peer's address from context, or "unknown" if not found. 20 | func PeerAddrFromContext(ctx context.Context) string { 21 | p, ok := peer.FromContext(ctx) 22 | if !ok { 23 | return "unknown" 24 | } 25 | return p.Addr.String() 26 | } 27 | 28 | // LogStateChangesUntilReady logs the state changes of a gRPC connection. 29 | func LogStateChangesUntilReady(ctx context.Context, conn getStater, log debugLog, wg *sync.WaitGroup, isReadyCallback func()) { 30 | wg.Add(1) 31 | go func() { 32 | defer wg.Done() 33 | state := conn.GetState() 34 | log.Debug(fmt.Sprintf("Connection state started as %q", state)) 35 | for ; state != connectivity.Ready && conn.WaitForStateChange(ctx, state); state = conn.GetState() { 36 | log.Debug(fmt.Sprintf("Connection state changed to %q", state)) 37 | } 38 | if state == connectivity.Ready { 39 | log.Debug("Connection ready") 40 | isReadyCallback() 41 | } else { 42 | log.Debug(fmt.Sprintf("Connection state ended with %q", state)) 43 | } 44 | }() 45 | } 46 | 47 | type getStater interface { 48 | GetState() connectivity.State 49 | WaitForStateChange(context.Context, connectivity.State) bool 50 | } 51 | 52 | type debugLog interface { 53 | Debug(msg string, args ...any) 54 | } 55 | -------------------------------------------------------------------------------- /internal/grpc/retry/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "retry", 6 | srcs = ["retry.go"], 7 | importpath = "cvm-reverse-proxy/internal/grpc/retry", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "@org_golang_google_grpc//codes", 11 | "@org_golang_google_grpc//status", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "retry_test", 17 | srcs = ["retry_test.go"], 18 | embed = [":retry"], 19 | deps = [ 20 | "@com_github_stretchr_testify//assert", 21 | "@org_golang_google_grpc//codes", 22 | "@org_golang_google_grpc//status", 23 | ], 24 | ) 25 | -------------------------------------------------------------------------------- /internal/grpc/testdialer/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "testdialer", 5 | srcs = ["testdialer.go"], 6 | importpath = "cvm-reverse-proxy/internal/grpc/testdialer", 7 | visibility = ["//:__subpackages__"], 8 | deps = ["@org_golang_google_grpc//test/bufconn"], 9 | ) 10 | -------------------------------------------------------------------------------- /internal/grpc/testdialer/testdialer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package testdialer provides a fake dialer for testing. 8 | package testdialer 9 | 10 | import ( 11 | "context" 12 | "fmt" 13 | "net" 14 | "sync" 15 | 16 | "google.golang.org/grpc/test/bufconn" 17 | ) 18 | 19 | // BufconnDialer is a fake dialer based on gRPC bufconn package. 20 | type BufconnDialer struct { 21 | mut sync.Mutex 22 | listeners map[string]*bufconn.Listener 23 | } 24 | 25 | // NewBufconnDialer creates a new bufconn dialer for testing. 26 | func NewBufconnDialer() *BufconnDialer { 27 | return &BufconnDialer{listeners: make(map[string]*bufconn.Listener)} 28 | } 29 | 30 | // DialContext implements the Dialer interface. 31 | func (b *BufconnDialer) DialContext(ctx context.Context, _, address string) (net.Conn, error) { 32 | b.mut.Lock() 33 | listener, ok := b.listeners[address] 34 | b.mut.Unlock() 35 | if !ok { 36 | return nil, fmt.Errorf("could not connect to server on %v", address) 37 | } 38 | return listener.DialContext(ctx) 39 | } 40 | 41 | // GetListener returns a fake listener that is coupled with this dialer. 42 | func (b *BufconnDialer) GetListener(endpoint string) net.Listener { 43 | listener := bufconn.Listen(1024) 44 | b.mut.Lock() 45 | b.listeners[endpoint] = listener 46 | b.mut.Unlock() 47 | return listener 48 | } 49 | -------------------------------------------------------------------------------- /internal/imagefetcher/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "imagefetcher", 6 | srcs = [ 7 | "imagefetcher.go", 8 | "raw.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/imagefetcher", 11 | visibility = ["//:__subpackages__"], 12 | deps = [ 13 | "//internal/api/fetcher", 14 | "//internal/api/versionsapi", 15 | "//internal/attestation/variant", 16 | "//internal/cloud/cloudprovider", 17 | "//internal/mpimage", 18 | "//internal/semver", 19 | "@com_github_schollz_progressbar_v3//:progressbar", 20 | "@com_github_spf13_afero//:afero", 21 | ], 22 | ) 23 | 24 | go_test( 25 | name = "imagefetcher_test", 26 | srcs = [ 27 | "imagefetcher_test.go", 28 | "raw_test.go", 29 | ], 30 | embed = [":imagefetcher"], 31 | deps = [ 32 | "//internal/api/versionsapi", 33 | "//internal/attestation/variant", 34 | "//internal/cloud/cloudprovider", 35 | "//internal/file", 36 | "@com_github_spf13_afero//:afero", 37 | "@com_github_stretchr_testify//assert", 38 | "@com_github_stretchr_testify//require", 39 | "@org_uber_go_goleak//:goleak", 40 | ], 41 | ) 42 | -------------------------------------------------------------------------------- /internal/installer/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "installer", 6 | srcs = ["installer.go"], 7 | importpath = "cvm-reverse-proxy/internal/installer", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/retry", 11 | "//internal/versions/components", 12 | "@com_github_spf13_afero//:afero", 13 | "@com_github_vincent_petithory_dataurl//:dataurl", 14 | "@io_k8s_utils//clock", 15 | ], 16 | ) 17 | 18 | go_test( 19 | name = "installer_test", 20 | srcs = ["installer_test.go"], 21 | embed = [":installer"], 22 | deps = [ 23 | "//internal/versions/components", 24 | "@com_github_spf13_afero//:afero", 25 | "@com_github_stretchr_testify//assert", 26 | "@com_github_stretchr_testify//require", 27 | "@io_k8s_utils//clock/testing", 28 | "@org_golang_google_grpc//test/bufconn", 29 | ], 30 | ) 31 | -------------------------------------------------------------------------------- /internal/kms/config/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "config", 5 | srcs = ["config.go"], 6 | importpath = "cvm-reverse-proxy/internal/kms/config", 7 | visibility = ["//:__subpackages__"], 8 | ) 9 | -------------------------------------------------------------------------------- /internal/kms/config/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package config provides configuration constants for the KeyService. 8 | package config 9 | 10 | const ( 11 | // SymmetricKeyLength is the length of symmetric encryption keys in bytes. We use AES256, therefore this is 32 Bytes. 12 | SymmetricKeyLength = 32 13 | ) 14 | 15 | var ( 16 | // KmsTags are the default tags for kms client created KMS solutions. 17 | KmsTags = map[string]string{ 18 | "createdBy": "constellation-kms-client", 19 | "component": "constellation-kek", 20 | } 21 | // StorageTags are the default tags for kms client created storage solutions. 22 | StorageTags = map[string]*string{ 23 | "createdBy": toPtr("constellation-kms-client"), 24 | "component": toPtr("constellation-dek-store"), 25 | } 26 | // AWSS3Tag is the default tag string for kms client created AWS S3 storage solutions. 27 | AWSS3Tag = "createdBy=constellation-kms-client&component=constellation-dek-store" 28 | ) 29 | 30 | func toPtr[T any](v T) *T { 31 | return &v 32 | } 33 | -------------------------------------------------------------------------------- /internal/kms/kms/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "kms", 5 | srcs = ["kms.go"], 6 | importpath = "cvm-reverse-proxy/internal/kms/kms", 7 | visibility = ["//:__subpackages__"], 8 | ) 9 | -------------------------------------------------------------------------------- /internal/kms/kms/aws/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "aws", 5 | srcs = ["aws.go"], 6 | importpath = "cvm-reverse-proxy/internal/kms/kms/aws", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/kms/kms", 10 | "//internal/kms/kms/internal", 11 | "//internal/kms/uri", 12 | "@com_github_hashicorp_go_kms_wrapping_v2//:go-kms-wrapping", 13 | "@com_github_hashicorp_go_kms_wrapping_wrappers_awskms_v2//:awskms", 14 | ], 15 | ) 16 | -------------------------------------------------------------------------------- /internal/kms/kms/aws/aws.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package aws implements a KMS backend for AWS KMS. 8 | package aws 9 | 10 | import ( 11 | "context" 12 | "errors" 13 | "fmt" 14 | 15 | kmsInterface "github.com/flashbots/cvm-reverse-proxy/internal/kms/kms" 16 | "github.com/flashbots/cvm-reverse-proxy/internal/kms/kms/internal" 17 | "github.com/flashbots/cvm-reverse-proxy/internal/kms/uri" 18 | 19 | wrapping "github.com/hashicorp/go-kms-wrapping/v2" 20 | awskms "github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2" 21 | ) 22 | 23 | // KMSClient implements the CloudKMS interface for AWS. 24 | type KMSClient struct { 25 | kms *internal.KMSClient 26 | } 27 | 28 | // New creates and initializes a new KMSClient for AWS. 29 | func New(ctx context.Context, store kmsInterface.Storage, cfg uri.AWSConfig) (*KMSClient, error) { 30 | if store == nil { 31 | return nil, errors.New("no storage backend provided for KMS") 32 | } 33 | 34 | wrapper := awskms.NewWrapper() 35 | if _, err := wrapper.SetConfig( 36 | ctx, 37 | wrapping.WithKeyId(cfg.KeyName), 38 | awskms.WithRegion(cfg.Region), 39 | awskms.WithAccessKey(cfg.AccessKeyID), 40 | awskms.WithSecretKey(cfg.AccessKey), 41 | ); err != nil { 42 | return nil, fmt.Errorf("setting AWS KMS config: %w", err) 43 | } 44 | return &KMSClient{ 45 | kms: &internal.KMSClient{ 46 | Storage: store, 47 | Wrapper: wrapper, 48 | }, 49 | }, nil 50 | } 51 | 52 | // GetDEK fetches an encrypted Data Encryption Key from storage and decrypts it using a KEK stored in AWS KMS. 53 | func (c *KMSClient) GetDEK(ctx context.Context, keyID string, dekSize int) ([]byte, error) { 54 | return c.kms.GetDEK(ctx, keyID, dekSize) 55 | } 56 | 57 | // Close is a no-op for AWS. 58 | func (c *KMSClient) Close() {} 59 | -------------------------------------------------------------------------------- /internal/kms/kms/azure/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "azure", 5 | srcs = ["azure.go"], 6 | importpath = "cvm-reverse-proxy/internal/kms/kms/azure", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/kms/kms", 10 | "//internal/kms/kms/internal", 11 | "//internal/kms/uri", 12 | "@com_github_hashicorp_go_kms_wrapping_wrappers_azurekeyvault_v2//:azurekeyvault", 13 | ], 14 | ) 15 | -------------------------------------------------------------------------------- /internal/kms/kms/azure/azure.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package azure implements KMS backends for Azure Key Vault and Azure managed HSM. 8 | package azure 9 | 10 | import ( 11 | "context" 12 | "errors" 13 | "fmt" 14 | 15 | "github.com/flashbots/cvm-reverse-proxy/internal/kms/kms" 16 | "github.com/flashbots/cvm-reverse-proxy/internal/kms/kms/internal" 17 | "github.com/flashbots/cvm-reverse-proxy/internal/kms/uri" 18 | 19 | azurekeyvault "github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2" 20 | ) 21 | 22 | // KMSClient implements the CloudKMS interface for Azure Key Vault. 23 | type KMSClient struct { 24 | kms *internal.KMSClient 25 | } 26 | 27 | // New initializes a KMS client for Azure Key Vault. 28 | func New(ctx context.Context, store kms.Storage, cfg uri.AzureConfig) (*KMSClient, error) { 29 | if store == nil { 30 | return nil, errors.New("no storage backend provided for KMS") 31 | } 32 | 33 | wrapper := azurekeyvault.NewWrapper() 34 | if _, err := wrapper.SetConfig( 35 | ctx, 36 | azurekeyvault.WithTenantId(cfg.TenantID), 37 | azurekeyvault.WithClientId(cfg.ClientID), 38 | azurekeyvault.WithClientSecret(cfg.ClientSecret), 39 | azurekeyvault.WithResource(string(cfg.VaultType)), 40 | azurekeyvault.WithVaultName(cfg.VaultName), 41 | azurekeyvault.WithKeyName(cfg.KeyName), 42 | ); err != nil { 43 | return nil, fmt.Errorf("setting Azure Key Vault config: %w", err) 44 | } 45 | 46 | return &KMSClient{ 47 | kms: &internal.KMSClient{ 48 | Storage: store, 49 | Wrapper: wrapper, 50 | }, 51 | }, nil 52 | } 53 | 54 | // GetDEK fetches an encrypted Data Encryption Key from storage and decrypts it using a KEK stored in Azure Key Vault. 55 | func (c *KMSClient) GetDEK(ctx context.Context, keyID string, dekSize int) ([]byte, error) { 56 | return c.kms.GetDEK(ctx, keyID, dekSize) 57 | } 58 | 59 | // Close is a no-op for Azure. 60 | func (c *KMSClient) Close() {} 61 | -------------------------------------------------------------------------------- /internal/kms/kms/cluster/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "cluster", 6 | srcs = ["cluster.go"], 7 | importpath = "cvm-reverse-proxy/internal/kms/kms/cluster", 8 | visibility = ["//:__subpackages__"], 9 | deps = ["//internal/crypto"], 10 | ) 11 | 12 | go_test( 13 | name = "cluster_test", 14 | srcs = ["cluster_test.go"], 15 | embed = [":cluster"], 16 | deps = [ 17 | "//internal/crypto/testvector", 18 | "@com_github_stretchr_testify//assert", 19 | "@com_github_stretchr_testify//require", 20 | "@org_uber_go_goleak//:goleak", 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /internal/kms/kms/cluster/cluster.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | Package cluster implements a KMS backend for in cluster key management. 9 | 10 | The cluster backend holds a master key, and corresponding salt. 11 | Data Encryption Keys (DEK) are derived from master key and salt using HKDF. 12 | 13 | This backend does not require a storage backend, as keys are derived on demand and not stored anywhere. 14 | For that purpose the special NoStoreURI can be used during KMS initialization. 15 | */ 16 | package cluster 17 | 18 | import ( 19 | "context" 20 | "errors" 21 | 22 | "github.com/flashbots/cvm-reverse-proxy/internal/crypto" 23 | ) 24 | 25 | // KMS implements the kms.CloudKMS interface for in cluster key management. 26 | type KMS struct { 27 | masterKey []byte 28 | salt []byte 29 | } 30 | 31 | // New creates a new ClusterKMS. 32 | func New(key []byte, salt []byte) (*KMS, error) { 33 | if len(key) == 0 { 34 | return nil, errors.New("missing master key") 35 | } 36 | if len(salt) == 0 { 37 | return nil, errors.New("missing salt") 38 | } 39 | 40 | return &KMS{masterKey: key, salt: salt}, nil 41 | } 42 | 43 | // GetDEK derives a key from the KMS masterKey. 44 | func (c *KMS) GetDEK(_ context.Context, dekID string, dekSize int) ([]byte, error) { 45 | if len(c.masterKey) == 0 { 46 | return nil, errors.New("master key not set for Constellation KMS") 47 | } 48 | return crypto.DeriveKey(c.masterKey, c.salt, []byte(dekID), uint(dekSize)) 49 | } 50 | 51 | // Close is a no-op for cKMS. 52 | func (c *KMS) Close() {} 53 | -------------------------------------------------------------------------------- /internal/kms/kms/gcp/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "gcp", 5 | srcs = ["gcp.go"], 6 | importpath = "cvm-reverse-proxy/internal/kms/kms/gcp", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/kms/kms", 10 | "//internal/kms/kms/internal", 11 | "//internal/kms/uri", 12 | "@com_github_hashicorp_go_kms_wrapping_wrappers_gcpckms_v2//:gcpckms", 13 | ], 14 | ) 15 | -------------------------------------------------------------------------------- /internal/kms/kms/internal/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "internal", 6 | srcs = ["internal.go"], 7 | importpath = "cvm-reverse-proxy/internal/kms/kms/internal", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/crypto", 11 | "//internal/kms/kms", 12 | "//internal/kms/storage", 13 | "@com_github_hashicorp_go_kms_wrapping_v2//:go-kms-wrapping", 14 | ], 15 | ) 16 | 17 | go_test( 18 | name = "internal_test", 19 | srcs = ["internal_test.go"], 20 | embed = [":internal"], 21 | deps = [ 22 | "//internal/kms/storage", 23 | "@com_github_hashicorp_go_kms_wrapping_v2//:go-kms-wrapping", 24 | "@com_github_hashicorp_go_kms_wrapping_wrappers_gcpckms_v2//:gcpckms", 25 | "@com_github_stretchr_testify//assert", 26 | "@com_github_stretchr_testify//require", 27 | "@com_google_cloud_go_kms//apiv1", 28 | "@org_golang_google_grpc//codes", 29 | "@org_golang_google_grpc//status", 30 | "@org_uber_go_goleak//:goleak", 31 | ], 32 | ) 33 | -------------------------------------------------------------------------------- /internal/kms/kms/kms.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package kms provides an abstract interface for Key Management Services. 8 | package kms 9 | 10 | import ( 11 | "context" 12 | ) 13 | 14 | // CloudKMS enables using cloud base Key Management Services. 15 | type CloudKMS interface { 16 | // GetDEK returns the DEK for dekID and kekID from the KMS. 17 | // If the DEK does not exist, a new one is created and saved to storage. 18 | GetDEK(ctx context.Context, dekID string, dekSize int) ([]byte, error) 19 | // Close closes any open connection on the KMS client. 20 | Close() 21 | } 22 | 23 | // Storage provides an abstract interface for the storage backend used for DEKs. 24 | type Storage interface { 25 | // Get returns a DEK from the storage by key ID. If the DEK does not exist, returns storage.ErrDEKUnset. 26 | Get(context.Context, string) ([]byte, error) 27 | // Put saves a DEK to the storage by key ID. 28 | Put(context.Context, string, []byte) error 29 | } 30 | -------------------------------------------------------------------------------- /internal/kms/setup/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "setup", 6 | srcs = ["setup.go"], 7 | importpath = "cvm-reverse-proxy/internal/kms/setup", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/kms/kms", 11 | "//internal/kms/kms/aws", 12 | "//internal/kms/kms/azure", 13 | "//internal/kms/kms/cluster", 14 | "//internal/kms/kms/gcp", 15 | "//internal/kms/storage/awss3", 16 | "//internal/kms/storage/azureblob", 17 | "//internal/kms/storage/gcs", 18 | "//internal/kms/uri", 19 | ], 20 | ) 21 | 22 | go_test( 23 | name = "setup_test", 24 | srcs = ["setup_test.go"], 25 | embed = [":setup"], 26 | deps = [ 27 | "//internal/kms/uri", 28 | "@com_github_stretchr_testify//assert", 29 | "@org_uber_go_goleak//:goleak", 30 | ], 31 | ) 32 | -------------------------------------------------------------------------------- /internal/kms/setup/setup_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package setup 8 | 9 | import ( 10 | "context" 11 | "testing" 12 | 13 | "github.com/flashbots/cvm-reverse-proxy/internal/kms/uri" 14 | 15 | "github.com/stretchr/testify/assert" 16 | "go.uber.org/goleak" 17 | ) 18 | 19 | func TestMain(m *testing.M) { 20 | goleak.VerifyTestMain(m, 21 | // https://github.com/census-instrumentation/opencensus-go/issues/1262 22 | goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), 23 | goleak.IgnoreAnyFunction("github.com/bazelbuild/rules_go/go/tools/bzltestutil.RegisterTimeoutHandler.func1"), 24 | ) 25 | } 26 | 27 | func TestSetUpKMS(t *testing.T) { 28 | assert := assert.New(t) 29 | 30 | kms, err := KMS(context.Background(), "storage://unknown", "kms://unknown") 31 | assert.Error(err) 32 | assert.Nil(kms) 33 | 34 | masterSecret := uri.MasterSecret{Key: []byte("key"), Salt: []byte("salt")} 35 | kms, err = KMS(context.Background(), "storage://no-store", masterSecret.EncodeToURI()) 36 | assert.NoError(err) 37 | assert.NotNil(kms) 38 | } 39 | -------------------------------------------------------------------------------- /internal/kms/storage/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "storage", 5 | srcs = ["storage.go"], 6 | importpath = "cvm-reverse-proxy/internal/kms/storage", 7 | visibility = ["//:__subpackages__"], 8 | ) 9 | -------------------------------------------------------------------------------- /internal/kms/storage/awss3/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "awss3", 6 | srcs = ["awss3.go"], 7 | importpath = "cvm-reverse-proxy/internal/kms/storage/awss3", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/kms/config", 11 | "//internal/kms/storage", 12 | "//internal/kms/uri", 13 | "@com_github_aws_aws_sdk_go_v2_config//:config", 14 | "@com_github_aws_aws_sdk_go_v2_credentials//:credentials", 15 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 16 | "@com_github_aws_aws_sdk_go_v2_service_s3//types", 17 | ], 18 | ) 19 | 20 | go_test( 21 | name = "awss3_test", 22 | srcs = ["awss3_test.go"], 23 | embed = [":awss3"], 24 | deps = [ 25 | "//internal/kms/storage", 26 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 27 | "@com_github_aws_aws_sdk_go_v2_service_s3//types", 28 | "@com_github_stretchr_testify//assert", 29 | ], 30 | ) 31 | -------------------------------------------------------------------------------- /internal/kms/storage/azureblob/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "azureblob", 6 | srcs = ["azureblob.go"], 7 | importpath = "cvm-reverse-proxy/internal/kms/storage/azureblob", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/kms/config", 11 | "//internal/kms/storage", 12 | "//internal/kms/uri", 13 | "@com_github_azure_azure_sdk_for_go_sdk_azcore//:azcore", 14 | "@com_github_azure_azure_sdk_for_go_sdk_azidentity//:azidentity", 15 | "@com_github_azure_azure_sdk_for_go_sdk_storage_azblob//:azblob", 16 | "@com_github_azure_azure_sdk_for_go_sdk_storage_azblob//blob", 17 | "@com_github_azure_azure_sdk_for_go_sdk_storage_azblob//bloberror", 18 | "@com_github_azure_azure_sdk_for_go_sdk_storage_azblob//container", 19 | ], 20 | ) 21 | 22 | go_test( 23 | name = "azureblob_test", 24 | srcs = ["azureblob_test.go"], 25 | embed = [":azureblob"], 26 | deps = [ 27 | "//internal/kms/storage", 28 | "@com_github_azure_azure_sdk_for_go_sdk_azcore//:azcore", 29 | "@com_github_azure_azure_sdk_for_go_sdk_storage_azblob//:azblob", 30 | "@com_github_azure_azure_sdk_for_go_sdk_storage_azblob//blob", 31 | "@com_github_azure_azure_sdk_for_go_sdk_storage_azblob//bloberror", 32 | "@com_github_azure_azure_sdk_for_go_sdk_storage_azblob//container", 33 | "@com_github_stretchr_testify//assert", 34 | ], 35 | ) 36 | -------------------------------------------------------------------------------- /internal/kms/storage/gcs/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "gcs", 6 | srcs = ["gcs.go"], 7 | importpath = "cvm-reverse-proxy/internal/kms/storage/gcs", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/kms/storage", 11 | "//internal/kms/uri", 12 | "@com_google_cloud_go_storage//:storage", 13 | "@org_golang_google_api//option", 14 | ], 15 | ) 16 | 17 | go_test( 18 | name = "gcs_test", 19 | srcs = ["gcs_test.go"], 20 | embed = [":gcs"], 21 | deps = [ 22 | "//internal/kms/storage", 23 | "@com_github_stretchr_testify//assert", 24 | "@com_google_cloud_go_storage//:storage", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /internal/kms/storage/memfs/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "memfs", 6 | srcs = ["memfs.go"], 7 | importpath = "cvm-reverse-proxy/internal/kms/storage/memfs", 8 | visibility = ["//:__subpackages__"], 9 | deps = ["//internal/kms/storage"], 10 | ) 11 | 12 | go_test( 13 | name = "memfs_test", 14 | srcs = ["memfs_test.go"], 15 | embed = [":memfs"], 16 | deps = [ 17 | "//internal/kms/storage", 18 | "@com_github_stretchr_testify//assert", 19 | "@org_uber_go_goleak//:goleak", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /internal/kms/storage/memfs/memfs.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package memfs implements a storage backend for the KMS that stores keys in memory only. 8 | // This package should be used for testing only. 9 | package memfs 10 | 11 | import ( 12 | "context" 13 | 14 | "github.com/flashbots/cvm-reverse-proxy/internal/kms/storage" 15 | ) 16 | 17 | // Storage is the standard implementation of the Storage interface, storing keys in memory only. 18 | type Storage struct { 19 | dekPool map[string][]byte 20 | } 21 | 22 | // New creates and initializes a new Storage object. 23 | func New() *Storage { 24 | s := &Storage{ 25 | dekPool: make(map[string][]byte), 26 | } 27 | 28 | return s 29 | } 30 | 31 | // Get returns a DEK from Storage by key ID. 32 | func (s *Storage) Get(_ context.Context, keyID string) ([]byte, error) { 33 | encDEK, ok := s.dekPool[keyID] 34 | if ok { 35 | return encDEK, nil 36 | } 37 | return nil, storage.ErrDEKUnset 38 | } 39 | 40 | // Put saves a DEK to Storage by key ID. 41 | func (s *Storage) Put(_ context.Context, keyID string, encDEK []byte) error { 42 | s.dekPool[keyID] = encDEK 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /internal/kms/storage/memfs/memfs_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package memfs 8 | 9 | import ( 10 | "context" 11 | "testing" 12 | 13 | "github.com/flashbots/cvm-reverse-proxy/internal/kms/storage" 14 | 15 | "github.com/stretchr/testify/assert" 16 | "go.uber.org/goleak" 17 | ) 18 | 19 | func TestMain(m *testing.M) { 20 | goleak.VerifyTestMain(m, 21 | // https://github.com/census-instrumentation/opencensus-go/issues/1262 22 | goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), 23 | goleak.IgnoreAnyFunction("github.com/bazelbuild/rules_go/go/tools/bzltestutil.RegisterTimeoutHandler.func1"), 24 | ) 25 | } 26 | 27 | func TestMemMapStorage(t *testing.T) { 28 | assert := assert.New(t) 29 | 30 | store := New() 31 | 32 | testDEK1 := []byte("test DEK") 33 | testDEK2 := []byte("more test DEK") 34 | ctx := context.Background() 35 | 36 | // request unset value 37 | _, err := store.Get(ctx, "test:input") 38 | assert.ErrorIs(err, storage.ErrDEKUnset) 39 | 40 | // test Put method 41 | assert.NoError(store.Put(ctx, "volume01", testDEK1)) 42 | assert.NoError(store.Put(ctx, "volume02", testDEK2)) 43 | 44 | // make sure values have been set 45 | val, err := store.Get(ctx, "volume01") 46 | assert.NoError(err) 47 | assert.Equal(testDEK1, val) 48 | val, err = store.Get(ctx, "volume02") 49 | assert.NoError(err) 50 | assert.Equal(testDEK2, val) 51 | 52 | _, err = store.Get(ctx, "invalid:key") 53 | assert.ErrorIs(err, storage.ErrDEKUnset) 54 | } 55 | -------------------------------------------------------------------------------- /internal/kms/storage/storage.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | Package storage implements storage backends for DEKs. 9 | 10 | If an unset DEK is requested, the backend MUST return [ErrDEKUnset]. 11 | */ 12 | package storage 13 | 14 | import ( 15 | "errors" 16 | ) 17 | 18 | // ErrDEKUnset indicates if a key is not found in storage. 19 | var ErrDEKUnset = errors.New("requested DEK not set") 20 | -------------------------------------------------------------------------------- /internal/kms/test/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("//bazel/go:go_test.bzl", "go_test") 2 | 3 | go_test( 4 | name = "test_test", 5 | srcs = [ 6 | "aws_test.go", 7 | "azure_test.go", 8 | "gcp_test.go", 9 | "integration_test.go", 10 | ], 11 | deps = [ 12 | "//internal/kms/config", 13 | "//internal/kms/kms", 14 | "//internal/kms/kms/aws", 15 | "//internal/kms/kms/azure", 16 | "//internal/kms/kms/gcp", 17 | "//internal/kms/storage", 18 | "//internal/kms/storage/awss3", 19 | "//internal/kms/storage/azureblob", 20 | "//internal/kms/storage/gcs", 21 | "//internal/kms/storage/memfs", 22 | "//internal/kms/uri", 23 | "@com_github_aws_aws_sdk_go_v2_config//:config", 24 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 25 | "@com_github_aws_aws_sdk_go_v2_service_s3//types", 26 | "@com_github_stretchr_testify//assert", 27 | "@com_github_stretchr_testify//require", 28 | ], 29 | ) 30 | -------------------------------------------------------------------------------- /internal/kms/uri/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "uri", 6 | srcs = ["uri.go"], 7 | importpath = "cvm-reverse-proxy/internal/kms/uri", 8 | visibility = ["//:__subpackages__"], 9 | ) 10 | 11 | go_test( 12 | name = "uri_test", 13 | srcs = ["uri_test.go"], 14 | embed = [":uri"], 15 | deps = [ 16 | "@com_github_stretchr_testify//assert", 17 | "@com_github_stretchr_testify//require", 18 | "@org_uber_go_goleak//:goleak", 19 | ], 20 | ) 21 | -------------------------------------------------------------------------------- /internal/kubernetes/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "kubernetes", 6 | srcs = [ 7 | "configmaps.go", 8 | "kubernetes.go", 9 | "marshal.go", 10 | "secrets.go", 11 | ], 12 | importpath = "cvm-reverse-proxy/internal/kubernetes", 13 | visibility = ["//:__subpackages__"], 14 | deps = [ 15 | "//internal/constants", 16 | "//internal/versions/components", 17 | "@in_gopkg_yaml_v3//:yaml_v3", 18 | "@io_k8s_api//core/v1:core", 19 | "@io_k8s_apimachinery//pkg/apis/meta/v1:meta", 20 | "@io_k8s_apimachinery//pkg/runtime", 21 | "@io_k8s_apimachinery//pkg/runtime/serializer", 22 | "@io_k8s_apimachinery//pkg/runtime/serializer/json", 23 | "@io_k8s_client_go//kubernetes/scheme", 24 | ], 25 | ) 26 | 27 | go_test( 28 | name = "kubernetes_test", 29 | srcs = [ 30 | "configmaps_test.go", 31 | "marshal_test.go", 32 | "secrets_test.go", 33 | ], 34 | embed = [":kubernetes"], 35 | deps = [ 36 | "@com_github_stretchr_testify//assert", 37 | "@com_github_stretchr_testify//require", 38 | "@io_k8s_api//core/v1:core", 39 | "@io_k8s_apimachinery//pkg/apis/meta/v1:meta", 40 | "@io_k8s_apimachinery//pkg/runtime", 41 | "@org_golang_google_protobuf//proto", 42 | ], 43 | ) 44 | -------------------------------------------------------------------------------- /internal/kubernetes/configmaps.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package kubernetes 8 | 9 | import ( 10 | "encoding/json" 11 | "fmt" 12 | "strings" 13 | 14 | "github.com/flashbots/cvm-reverse-proxy/internal/constants" 15 | "github.com/flashbots/cvm-reverse-proxy/internal/versions/components" 16 | 17 | corev1 "k8s.io/api/core/v1" 18 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 | "k8s.io/apimachinery/pkg/runtime" 20 | ) 21 | 22 | // ConfigMaps represent a list of k8s ConfigMap. 23 | type ConfigMaps []*corev1.ConfigMap 24 | 25 | // Marshal marshals config maps into multiple YAML documents. 26 | func (s ConfigMaps) Marshal() ([]byte, error) { 27 | objects := make([]runtime.Object, len(s)) 28 | for i := range s { 29 | objects[i] = s[i] 30 | } 31 | return MarshalK8SResourcesList(objects) 32 | } 33 | 34 | // ConstructK8sComponentsCM creates a k8s-components config map for the given components. 35 | func ConstructK8sComponentsCM(components components.Components, clusterVersion string) (corev1.ConfigMap, error) { 36 | componentsMarshalled, err := json.Marshal(components) 37 | if err != nil { 38 | return corev1.ConfigMap{}, fmt.Errorf("marshalling component versions: %w", err) 39 | } 40 | 41 | componentsHash := components.GetHash() 42 | componentConfigMapName := fmt.Sprintf("k8s-components-%s", strings.ReplaceAll(componentsHash, ":", "-")) 43 | 44 | return corev1.ConfigMap{ 45 | TypeMeta: metav1.TypeMeta{ 46 | APIVersion: "v1", 47 | Kind: "ConfigMap", 48 | }, 49 | Immutable: toPtr(true), 50 | ObjectMeta: metav1.ObjectMeta{ 51 | Name: componentConfigMapName, 52 | Namespace: "kube-system", 53 | }, 54 | Data: map[string]string{ 55 | constants.ComponentsListKey: string(componentsMarshalled), 56 | constants.K8sVersionFieldName: clusterVersion, 57 | }, 58 | }, nil 59 | } 60 | 61 | func toPtr[T any](v T) *T { 62 | return &v 63 | } 64 | -------------------------------------------------------------------------------- /internal/kubernetes/configmaps_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package kubernetes 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | k8s "k8s.io/api/core/v1" 15 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 | ) 17 | 18 | func TestConfigMaps(t *testing.T) { 19 | require := require.New(t) 20 | assert := assert.New(t) 21 | 22 | configMaps := ConfigMaps{ 23 | &k8s.ConfigMap{ 24 | TypeMeta: v1.TypeMeta{ 25 | Kind: "ConfigMap", 26 | APIVersion: "v1", 27 | }, 28 | Data: map[string]string{"key": "value1"}, 29 | }, 30 | &k8s.ConfigMap{ 31 | TypeMeta: v1.TypeMeta{ 32 | Kind: "ConfigMap", 33 | APIVersion: "v1", 34 | }, 35 | Data: map[string]string{"key": "value2"}, 36 | }, 37 | } 38 | data, err := configMaps.Marshal() 39 | require.NoError(err) 40 | 41 | assert.Equal(`apiVersion: v1 42 | data: 43 | key: value1 44 | kind: ConfigMap 45 | metadata: 46 | creationTimestamp: null 47 | --- 48 | apiVersion: v1 49 | data: 50 | key: value2 51 | kind: ConfigMap 52 | metadata: 53 | creationTimestamp: null 54 | `, string(data)) 55 | } 56 | -------------------------------------------------------------------------------- /internal/kubernetes/kubectl/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "kubectl", 6 | srcs = ["kubectl.go"], 7 | importpath = "cvm-reverse-proxy/internal/kubernetes/kubectl", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "@io_k8s_api//core/v1:core", 11 | "@io_k8s_apiextensions_apiserver//pkg/apis/apiextensions/v1:apiextensions", 12 | "@io_k8s_apiextensions_apiserver//pkg/client/clientset/clientset/typed/apiextensions/v1:apiextensions", 13 | "@io_k8s_apimachinery//pkg/apis/meta/v1:meta", 14 | "@io_k8s_apimachinery//pkg/apis/meta/v1/unstructured", 15 | "@io_k8s_apimachinery//pkg/labels", 16 | "@io_k8s_apimachinery//pkg/runtime", 17 | "@io_k8s_apimachinery//pkg/runtime/schema", 18 | "@io_k8s_apimachinery//pkg/runtime/serializer", 19 | "@io_k8s_apimachinery//pkg/types", 20 | "@io_k8s_client_go//dynamic", 21 | "@io_k8s_client_go//kubernetes", 22 | "@io_k8s_client_go//rest", 23 | "@io_k8s_client_go//scale/scheme", 24 | "@io_k8s_client_go//tools/clientcmd", 25 | "@io_k8s_client_go//util/retry", 26 | ], 27 | ) 28 | 29 | go_test( 30 | name = "kubectl_test", 31 | srcs = ["kubectl_test.go"], 32 | embed = [":kubectl"], 33 | deps = ["@com_github_stretchr_testify//assert"], 34 | ) 35 | -------------------------------------------------------------------------------- /internal/kubernetes/kubectl/kubectl_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package kubectl 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestParseCRDs(t *testing.T) { 16 | testCases := map[string]struct { 17 | data string 18 | wantErr bool 19 | }{ 20 | "success": { 21 | data: "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n name: nodeversions.update.edgeless.systems\nspec:\n group: update.edgeless.systems\n names:\n kind: NodeImage\n", 22 | wantErr: false, 23 | }, 24 | "wrong kind": { 25 | data: "apiVersion: v1\nkind: Secret\ntype: Opaque\nmetadata:\n name: supersecret\n namespace: testNamespace\ndata:\n data: YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE=\n", 26 | wantErr: true, 27 | }, 28 | "decoding error": { 29 | data: "asdf", 30 | wantErr: true, 31 | }, 32 | } 33 | 34 | for name, tc := range testCases { 35 | t.Run(name, func(t *testing.T) { 36 | assert := assert.New(t) 37 | 38 | _, err := parseCRD([]byte(tc.data)) 39 | if tc.wantErr { 40 | assert.Error(err) 41 | return 42 | } 43 | assert.NoError(err) 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /internal/kubernetes/kubernetes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | /* 8 | Package kubernetes provides data types and custom marshalers for Kubernetes API objects. 9 | 10 | Interaction with the Kubernetes API should be handled by the kubectl subpackage. 11 | */ 12 | package kubernetes 13 | -------------------------------------------------------------------------------- /internal/kubernetes/secrets.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package kubernetes 8 | 9 | import ( 10 | k8s "k8s.io/api/core/v1" 11 | "k8s.io/apimachinery/pkg/runtime" 12 | ) 13 | 14 | // Secrets represent a list of k8s Secret. 15 | type Secrets []*k8s.Secret 16 | 17 | // Marshal marshals secrets into multiple YAML documents. 18 | func (s Secrets) Marshal() ([]byte, error) { 19 | objects := make([]runtime.Object, len(s)) 20 | for i := range s { 21 | objects[i] = s[i] 22 | } 23 | return MarshalK8SResourcesList(objects) 24 | } 25 | -------------------------------------------------------------------------------- /internal/kubernetes/secrets_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package kubernetes 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | k8s "k8s.io/api/core/v1" 15 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 | ) 17 | 18 | func TestSecrets(t *testing.T) { 19 | require := require.New(t) 20 | assert := assert.New(t) 21 | 22 | secrets := Secrets{ 23 | &k8s.Secret{ 24 | TypeMeta: v1.TypeMeta{ 25 | Kind: "Secret", 26 | APIVersion: "v1", 27 | }, 28 | Data: map[string][]byte{"key": []byte("value1")}, 29 | }, 30 | &k8s.Secret{ 31 | TypeMeta: v1.TypeMeta{ 32 | Kind: "Secret", 33 | APIVersion: "v1", 34 | }, 35 | Data: map[string][]byte{"key": []byte("value2")}, 36 | }, 37 | } 38 | data, err := secrets.Marshal() 39 | require.NoError(err) 40 | 41 | assert.Equal(`apiVersion: v1 42 | data: 43 | key: dmFsdWUx 44 | kind: Secret 45 | metadata: 46 | creationTimestamp: null 47 | --- 48 | apiVersion: v1 49 | data: 50 | key: dmFsdWUy 51 | kind: Secret 52 | metadata: 53 | creationTimestamp: null 54 | `, string(data)) 55 | } 56 | -------------------------------------------------------------------------------- /internal/license/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "license", 6 | srcs = [ 7 | # keep 8 | "checker_enterprise.go", 9 | # keep 10 | "checker_oss.go", 11 | "file.go", 12 | "license.go", 13 | ], 14 | importpath = "cvm-reverse-proxy/internal/license", 15 | visibility = ["//:__subpackages__"], 16 | deps = [ 17 | "//internal/cloud/cloudprovider", 18 | # keep 19 | "//internal/constants", 20 | ], 21 | ) 22 | 23 | go_test( 24 | name = "license_test", 25 | srcs = ["file_test.go"], 26 | embed = [":license"], 27 | tags = [ 28 | "enterprise", 29 | ], 30 | deps = [ 31 | "@com_github_stretchr_testify//assert", 32 | "@com_github_stretchr_testify//require", 33 | ], 34 | ) 35 | -------------------------------------------------------------------------------- /internal/license/checker_oss.go: -------------------------------------------------------------------------------- 1 | //go:build !enterprise 2 | 3 | /* 4 | Copyright (c) Edgeless Systems GmbH 5 | 6 | SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package license 10 | 11 | import ( 12 | "context" 13 | 14 | "github.com/flashbots/cvm-reverse-proxy/internal/cloud/cloudprovider" 15 | ) 16 | 17 | // Checker checks the Constellation license. 18 | type Checker struct{} 19 | 20 | // NewChecker creates a new Checker. 21 | func NewChecker() *Checker { 22 | return &Checker{} 23 | } 24 | 25 | // CheckLicense is a no-op for open source version of Constellation. 26 | func (c *Checker) CheckLicense(context.Context, cloudprovider.Provider, Action, string) (int, error) { 27 | return 0, nil 28 | } 29 | -------------------------------------------------------------------------------- /internal/license/file.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package license 8 | 9 | import ( 10 | "encoding/base64" 11 | "fmt" 12 | ) 13 | 14 | // FromBytes reads the given license bytes and returns it as a string. 15 | func FromBytes(license []byte) (string, error) { 16 | maxSize := base64.StdEncoding.DecodedLen(len(license)) 17 | decodedLicense := make([]byte, maxSize) 18 | n, err := base64.StdEncoding.Decode(decodedLicense, license) 19 | if err != nil { 20 | return "", fmt.Errorf("unable to base64 decode license file: %w", err) 21 | } 22 | if n != 36 { // length of UUID 23 | return "", fmt.Errorf("license file corrupt: wrong length") 24 | } 25 | decodedLicense = decodedLicense[:n] 26 | return string(decodedLicense), nil 27 | } 28 | -------------------------------------------------------------------------------- /internal/license/file_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package license 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestFromBytes(t *testing.T) { 17 | testCases := map[string]struct { 18 | licenseBytes []byte 19 | wantLicense string 20 | wantErr bool 21 | }{ 22 | "community license": { 23 | licenseBytes: []byte("MDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAw"), 24 | wantLicense: CommunityLicense, 25 | }, 26 | "too short": { 27 | licenseBytes: []byte("MDAwMDAwMDAtMDAwMC0wMDAwLTAwMDA="), 28 | wantErr: true, 29 | }, 30 | "too long": { 31 | licenseBytes: []byte("MDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwMA=="), 32 | wantErr: true, 33 | }, 34 | "not base64": { 35 | licenseBytes: []byte("not base64"), 36 | wantErr: true, 37 | }, 38 | "empty": { 39 | licenseBytes: []byte(""), 40 | wantErr: true, 41 | }, 42 | "nil": { 43 | licenseBytes: nil, 44 | wantErr: true, 45 | }, 46 | } 47 | 48 | for name, tc := range testCases { 49 | t.Run(name, func(t *testing.T) { 50 | require := require.New(t) 51 | assert := assert.New(t) 52 | 53 | out, err := FromBytes(tc.licenseBytes) 54 | if tc.wantErr { 55 | require.Error(err) 56 | } else { 57 | require.NoError(err) 58 | } 59 | assert.Equal(tc.wantLicense, out) 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /internal/license/integration/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("//bazel/go:go_test.bzl", "go_test") 2 | 3 | go_test( 4 | name = "integration_test", 5 | srcs = ["license_integration_test.go"], 6 | tags = [ 7 | "enterprise", 8 | "integration", 9 | "requires-network", 10 | ], 11 | deps = [ 12 | "//internal/cloud/cloudprovider", 13 | "//internal/license", 14 | "@com_github_stretchr_testify//assert", 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /internal/license/integration/license_integration_test.go: -------------------------------------------------------------------------------- 1 | //go:build integration 2 | 3 | /* 4 | Copyright (c) Edgeless Systems GmbH 5 | 6 | SPDX-License-Identifier: AGPL-3.0-only 7 | */ 8 | 9 | package integration 10 | 11 | import ( 12 | "context" 13 | "testing" 14 | 15 | "github.com/flashbots/cvm-reverse-proxy/internal/cloud/cloudprovider" 16 | "github.com/flashbots/cvm-reverse-proxy/internal/license" 17 | 18 | "github.com/stretchr/testify/assert" 19 | ) 20 | 21 | func TestQuotaCheckIntegration(t *testing.T) { 22 | testCases := map[string]struct { 23 | license string 24 | wantQuota int 25 | wantError bool 26 | }{ 27 | "OSS license has quota 8": { 28 | license: license.CommunityLicense, 29 | wantQuota: 8, 30 | }, 31 | "Empty license assumes community": { 32 | license: "", 33 | wantQuota: 8, 34 | }, 35 | } 36 | 37 | for name, tc := range testCases { 38 | t.Run(name, func(t *testing.T) { 39 | assert := assert.New(t) 40 | 41 | client := license.NewChecker() 42 | 43 | quota, err := client.CheckLicense(context.Background(), cloudprovider.Unknown, "test", tc.license) 44 | 45 | if tc.wantError { 46 | assert.Error(err) 47 | return 48 | } 49 | assert.NoError(err) 50 | assert.Equal(tc.wantQuota, quota) 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /internal/license/license.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package license provides functions to check a user's Constellation license. 8 | package license 9 | 10 | // Action performed by Constellation. 11 | type Action string 12 | 13 | const ( 14 | // CommunityLicense is used by everyone who has not bought an enterprise license. 15 | CommunityLicense = "00000000-0000-0000-0000-000000000000" 16 | // MarketplaceLicense is used by everyone who uses a marketplace image. 17 | MarketplaceLicense = "11111111-1111-1111-1111-111111111111" 18 | 19 | // Init action denotes the initialization of a Constellation cluster. 20 | Init Action = "init" 21 | // Apply action denotes an update of a Constellation cluster. 22 | // It is used after a cluster has already been initialized once. 23 | Apply Action = "apply" 24 | ) 25 | -------------------------------------------------------------------------------- /internal/logger/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "logger", 5 | srcs = [ 6 | "cmdline.go", 7 | "grpclogger.go", 8 | "levelhandler.go", 9 | "log.go", 10 | ], 11 | importpath = "cvm-reverse-proxy/internal/logger", 12 | visibility = ["//:__subpackages__"], 13 | deps = [ 14 | "@com_github_grpc_ecosystem_go_grpc_middleware_v2//interceptors/logging", 15 | "@org_golang_google_grpc//:grpc", 16 | "@org_golang_google_grpc//grpclog", 17 | ], 18 | ) 19 | -------------------------------------------------------------------------------- /internal/logger/cmdline.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package logger 8 | 9 | import ( 10 | "log/slog" 11 | ) 12 | 13 | // CmdLineVerbosityDescription explains numeric log levels. 14 | const CmdLineVerbosityDescription = "log verbosity: Use -1 for debug information, 0 for info, 1 for warn, 2 for error" 15 | 16 | // VerbosityFromInt converts a verbosity level from an integer to a slog.Level. 17 | func VerbosityFromInt(verbosity int) slog.Level { 18 | switch { 19 | case verbosity <= -1: 20 | return slog.LevelDebug 21 | case verbosity == 0: 22 | return slog.LevelInfo 23 | case verbosity == 1: 24 | return slog.LevelWarn 25 | case verbosity >= 2: 26 | return slog.LevelError 27 | default: 28 | return slog.LevelInfo 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /internal/logger/levelhandler.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package logger 8 | 9 | import ( 10 | "context" 11 | "log/slog" 12 | ) 13 | 14 | // LevelHandler copied from the official LevelHandler example in the slog package documentation. 15 | 16 | // levelHandler wraps a Handler with an Enabled method 17 | // that returns false for levels below a minimum. 18 | type levelHandler struct { 19 | level slog.Leveler 20 | handler slog.Handler 21 | } 22 | 23 | // newLevelHandler returns a LevelHandler with the given level. 24 | // All methods except Enabled delegate to h. 25 | func newLevelHandler(level slog.Leveler, h slog.Handler) *levelHandler { 26 | // Optimization: avoid chains of LevelHandlers. 27 | if lh, ok := h.(*levelHandler); ok { 28 | h = lh.Handler() 29 | } 30 | return &levelHandler{level, h} 31 | } 32 | 33 | // Enabled implements Handler.Enabled by reporting whether 34 | // level is at least as large as h's level. 35 | func (h *levelHandler) Enabled(_ context.Context, level slog.Level) bool { 36 | return level >= h.level.Level() 37 | } 38 | 39 | // Handle implements Handler.Handle. 40 | func (h *levelHandler) Handle(ctx context.Context, r slog.Record) error { 41 | return h.handler.Handle(ctx, r) 42 | } 43 | 44 | // WithAttrs implements Handler.WithAttrs. 45 | func (h *levelHandler) WithAttrs(attrs []slog.Attr) slog.Handler { 46 | return newLevelHandler(h.level, h.handler.WithAttrs(attrs)) 47 | } 48 | 49 | // WithGroup implements Handler.WithGroup. 50 | func (h *levelHandler) WithGroup(name string) slog.Handler { 51 | return newLevelHandler(h.level, h.handler.WithGroup(name)) 52 | } 53 | 54 | // Handler returns the Handler wrapped by h. 55 | func (h *levelHandler) Handler() slog.Handler { 56 | return h.handler 57 | } 58 | -------------------------------------------------------------------------------- /internal/maa/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "maa", 6 | srcs = [ 7 | "maa.go", 8 | "patch.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/maa", 11 | visibility = ["//:__subpackages__"], 12 | deps = [ 13 | "@com_github_azure_azure_sdk_for_go//profiles/latest/attestation/attestation", 14 | "@com_github_azure_azure_sdk_for_go_sdk_azcore//policy", 15 | "@com_github_azure_azure_sdk_for_go_sdk_azidentity//:azidentity", 16 | ], 17 | ) 18 | 19 | go_test( 20 | name = "maa_test", 21 | srcs = ["patch_test.go"], 22 | embed = [":maa"], 23 | deps = ["@com_github_stretchr_testify//assert"], 24 | ) 25 | -------------------------------------------------------------------------------- /internal/maa/maa.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package maa provides an interface for interacting with an MAA service 8 | // on an infrastructure level. 9 | package maa 10 | -------------------------------------------------------------------------------- /internal/maa/patch_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | package maa 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestEncodeAttestationPolicy(t *testing.T) { 15 | assert := assert.New(t) 16 | 17 | p := AzurePolicyPatcher{} 18 | 19 | // taken from /providers/Microsoft.Attestation/attestationProviders//mrsg_item2 20 | expected := "eyJhbGciOiJub25lIn0.eyJBdHRlc3RhdGlvblBvbGljeSI6IkNpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNCMlpYSnphVzl1UFNBeExqQTdDaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQmhkWFJvYjNKcGVtRjBhVzl1Y25Wc1pYTUtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lIc0tJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0JiZEhsd1pUMDlJbmd0YlhNdFlYcDFjbVYyYlMxa1pXWmhkV3gwTFhObFkzVnlaV0p2YjNSclpYbHpkbUZzYVdSaGRHVmtJaXdnZG1Gc2RXVTlQV1poYkhObFhTQTlQaUJrWlc1NUtDazdDaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnVzNSNWNHVTlQU0o0TFcxekxXRjZkWEpsZG0wdFpHVmlkV2RuWlhKelpHbHpZV0pzWldRaUxDQjJZV3gxWlQwOVptRnNjMlZkSUQwLUlHUmxibmtvS1RzS0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQXZMeUJVYUdVZ2JHbHVaU0JpWld4dmR5QjNZWE1nWldScGRHVmtJR0o1SUhSb1pTQkRiMjV6ZEdWc2JHRjBhVzl1SUVOTVNTNGdSRzhnYm05MElHVmthWFFnYldGdWRXRnNiSGt1Q2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0x5OWJkSGx3WlQwOUluTmxZM1Z5WldKdmIzUWlMQ0IyWVd4MVpUMDlabUZzYzJWZElEMC1JR1JsYm5rb0tUc0tJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0JiZEhsd1pUMDlJbmd0YlhNdFlYcDFjbVYyYlMxemFXZHVhVzVuWkdsellXSnNaV1FpTENCMllXeDFaVDA5Wm1Gc2MyVmRJRDAtSUdSbGJua29LVHNLSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNCYmRIbHdaVDA5SW5ndGJYTXRZWHAxY21WMmJTMWtZblpoYkdsa1lYUmxaQ0lzSUhaaGJIVmxQVDFtWVd4elpWMGdQVDRnWkdWdWVTZ3BPd29nSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUZ0MGVYQmxQVDBpZUMxdGN5MWhlblZ5WlhadExXUmllSFpoYkdsa1lYUmxaQ0lzSUhaaGJIVmxQVDFtWVd4elpWMGdQVDRnWkdWdWVTZ3BPd29nSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUQwLUlIQmxjbTFwZENncE93b2dJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ2ZUc0tJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lHbHpjM1ZoYm1ObGNuVnNaWE1LSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJSHNLSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJSDA3In0." 21 | assert.Equal(expected, p.encodeAttestationPolicy()) 22 | } 23 | -------------------------------------------------------------------------------- /internal/mpimage/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "mpimage", 6 | srcs = [ 7 | "mpimage.go", 8 | "uri.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/mpimage", 11 | visibility = ["//:__subpackages__"], 12 | deps = [ 13 | "//internal/cloud/cloudprovider", 14 | "//internal/constants", 15 | "//internal/semver", 16 | ], 17 | ) 18 | 19 | go_test( 20 | name = "mpimage_test", 21 | srcs = ["uri_test.go"], 22 | embed = [":mpimage"], 23 | deps = ["@com_github_stretchr_testify//assert"], 24 | ) 25 | -------------------------------------------------------------------------------- /internal/mpimage/mpimage.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // The mpimage package provides utilities for handling CSP marketplace OS images. 8 | package mpimage 9 | -------------------------------------------------------------------------------- /internal/nodestate/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "nodestate", 6 | srcs = ["nodestate.go"], 7 | importpath = "cvm-reverse-proxy/internal/nodestate", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/file", 11 | "//internal/role", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "nodestate_test", 17 | srcs = ["nodestate_test.go"], 18 | embed = [":nodestate"], 19 | deps = [ 20 | "//internal/file", 21 | "//internal/role", 22 | "@com_github_spf13_afero//:afero", 23 | "@com_github_stretchr_testify//assert", 24 | "@com_github_stretchr_testify//require", 25 | "@org_uber_go_goleak//:goleak", 26 | ], 27 | ) 28 | -------------------------------------------------------------------------------- /internal/nodestate/nodestate.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package nodestate is used to persist the state of a Constellation node to disk. 8 | package nodestate 9 | 10 | import ( 11 | "fmt" 12 | 13 | "github.com/flashbots/cvm-reverse-proxy/internal/file" 14 | "github.com/flashbots/cvm-reverse-proxy/internal/role" 15 | ) 16 | 17 | const nodeStatePath = "/run/state/constellation/node_state.json" 18 | 19 | // NodeState is the state of a constellation node that is required to recover from a reboot. 20 | // Can be persisted to disk and reloaded later. 21 | type NodeState struct { 22 | Role role.Role 23 | MeasurementSalt []byte 24 | } 25 | 26 | // FromFile reads a NodeState from disk. 27 | func FromFile(fileHandler file.Handler) (*NodeState, error) { 28 | nodeState := &NodeState{} 29 | if err := fileHandler.ReadJSON(nodeStatePath, nodeState); err != nil { 30 | return nil, fmt.Errorf("loading node state: %w", err) 31 | } 32 | return nodeState, nil 33 | } 34 | 35 | // ToFile writes a NodeState to disk. 36 | func (nodeState *NodeState) ToFile(fileHandler file.Handler) error { 37 | return fileHandler.WriteJSON(nodeStatePath, nodeState, file.OptMkdirAll) 38 | } 39 | -------------------------------------------------------------------------------- /internal/osimage/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "osimage", 5 | srcs = ["osimage.go"], 6 | importpath = "cvm-reverse-proxy/internal/osimage", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/api/versionsapi", 10 | "//internal/cloud/cloudprovider", 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /internal/osimage/archive/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "archive", 5 | srcs = ["archive.go"], 6 | importpath = "cvm-reverse-proxy/internal/osimage/archive", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/api/versionsapi", 10 | "//internal/constants", 11 | "//internal/staticupload", 12 | "@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager", 13 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 14 | "@com_github_aws_aws_sdk_go_v2_service_s3//types", 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /internal/osimage/imageinfo/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "imageinfo", 5 | srcs = ["imageinfo.go"], 6 | importpath = "cvm-reverse-proxy/internal/osimage/imageinfo", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/api/versionsapi", 10 | "//internal/constants", 11 | "//internal/staticupload", 12 | "@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager", 13 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 14 | "@com_github_aws_aws_sdk_go_v2_service_s3//types", 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /internal/osimage/measurementsuploader/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "measurementsuploader", 5 | srcs = ["measurementsuploader.go"], 6 | importpath = "cvm-reverse-proxy/internal/osimage/measurementsuploader", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/api/versionsapi", 10 | "//internal/attestation/measurements", 11 | "//internal/constants", 12 | "//internal/staticupload", 13 | "@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager", 14 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 15 | "@com_github_aws_aws_sdk_go_v2_service_s3//types", 16 | ], 17 | ) 18 | -------------------------------------------------------------------------------- /internal/osimage/nop/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "nop", 5 | srcs = ["nop.go"], 6 | importpath = "cvm-reverse-proxy/internal/osimage/nop", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/api/versionsapi", 10 | "//internal/osimage", 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /internal/osimage/nop/nop.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // package nop implements a no-op for CSPs that don't require custom image upload functionality. 8 | package nop 9 | 10 | import ( 11 | "context" 12 | "fmt" 13 | "log/slog" 14 | 15 | "github.com/flashbots/cvm-reverse-proxy/internal/api/versionsapi" 16 | "github.com/flashbots/cvm-reverse-proxy/internal/osimage" 17 | ) 18 | 19 | // Uploader is a no-op uploader. 20 | type Uploader struct { 21 | log *slog.Logger 22 | } 23 | 24 | // New creates a new Uploader. 25 | func New(log *slog.Logger) *Uploader { 26 | return &Uploader{log: log} 27 | } 28 | 29 | // Upload pretends to upload images to a csp. 30 | func (u *Uploader) Upload(_ context.Context, req *osimage.UploadRequest) ([]versionsapi.ImageInfoEntry, error) { 31 | u.log.Debug(fmt.Sprintf("Skipping image upload of %q since this CSP does not require images to be uploaded in advance.", req.Version.ShortPath())) 32 | return nil, nil 33 | } 34 | -------------------------------------------------------------------------------- /internal/osimage/osimage.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // package osimage is used to handle osimages in the CI (uploading and maintenance). 8 | package osimage 9 | 10 | import ( 11 | "io" 12 | "time" 13 | 14 | "github.com/flashbots/cvm-reverse-proxy/internal/api/versionsapi" 15 | "github.com/flashbots/cvm-reverse-proxy/internal/cloud/cloudprovider" 16 | ) 17 | 18 | // UploadRequest is a request to upload an os image. 19 | type UploadRequest struct { 20 | Provider cloudprovider.Provider 21 | Version versionsapi.Version 22 | AttestationVariant string 23 | Timestamp time.Time 24 | ImageReader func() (io.ReadSeekCloser, error) 25 | ImagePath string 26 | } 27 | -------------------------------------------------------------------------------- /internal/osimage/secureboot/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "secureboot", 6 | srcs = [ 7 | "secureboot.go", 8 | "zlibdict.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/osimage/secureboot", 11 | visibility = ["//:__subpackages__"], 12 | deps = ["@com_github_spf13_afero//:afero"], 13 | ) 14 | 15 | go_test( 16 | name = "secureboot_test", 17 | srcs = ["secureboot_test.go"], 18 | embed = [":secureboot"], 19 | deps = [ 20 | "@com_github_spf13_afero//:afero", 21 | "@com_github_stretchr_testify//assert", 22 | "@com_github_stretchr_testify//require", 23 | "@org_uber_go_goleak//:goleak", 24 | ], 25 | ) 26 | -------------------------------------------------------------------------------- /internal/osimage/uplosi/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "uplosi", 5 | srcs = ["uplosiupload.go"], 6 | embedsrcs = ["uplosi.conf.in"], 7 | importpath = "cvm-reverse-proxy/internal/osimage/uplosi", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/api/versionsapi", 11 | "//internal/cloud/cloudprovider", 12 | "//internal/osimage", 13 | "@com_github_burntsushi_toml//:toml", 14 | ], 15 | ) 16 | -------------------------------------------------------------------------------- /internal/osimage/uplosi/uplosi.conf.in: -------------------------------------------------------------------------------- 1 | [base] 2 | name = "constellation" 3 | 4 | [base.aws] 5 | region = "eu-central-1" 6 | replicationRegions = ["eu-west-1", "eu-west-3", "us-east-1", "us-east-2", "ap-south-1"] 7 | bucket = "constellation-images" 8 | publish = true 9 | 10 | [base.azure] 11 | subscriptionID = "0d202bbb-4fa7-4af8-8125-58c269a05435" 12 | location = "northeurope" 13 | resourceGroup = "constellation-images" 14 | sharingNamePrefix = "constellation" 15 | sku = "constellation" 16 | publisher = "edgelesssys" 17 | 18 | [base.gcp] 19 | project = "constellation-images" 20 | location = "europe-west3" 21 | bucket = "constellation-os-images" 22 | 23 | [base.openstack] 24 | cloud = "stackit" 25 | visibility = "private" 26 | properties = { "hw_firmware_type" = "uefi", "os_type" = "linux" } 27 | -------------------------------------------------------------------------------- /internal/retry/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "retry", 6 | srcs = ["retry.go"], 7 | importpath = "cvm-reverse-proxy/internal/retry", 8 | visibility = ["//:__subpackages__"], 9 | deps = ["@io_k8s_utils//clock"], 10 | ) 11 | 12 | go_test( 13 | name = "retry_test", 14 | srcs = ["retry_test.go"], 15 | embed = [":retry"], 16 | deps = [ 17 | "@com_github_stretchr_testify//assert", 18 | "@io_k8s_utils//clock/testing", 19 | "@org_uber_go_goleak//:goleak", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /internal/retry/retry.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package retry provides a simple interface for retrying operations. 8 | package retry 9 | 10 | import ( 11 | "context" 12 | "time" 13 | 14 | "k8s.io/utils/clock" 15 | ) 16 | 17 | // IntervalRetrier retries a call with an interval. The call is defined in the Doer property. 18 | type IntervalRetrier struct { 19 | interval time.Duration 20 | doer Doer 21 | clock clock.WithTicker 22 | retriable func(error) bool 23 | } 24 | 25 | // NewIntervalRetrier returns a new IntervalRetrier. The optional clock is used for testing. 26 | func NewIntervalRetrier(doer Doer, interval time.Duration, retriable func(error) bool, optClock ...clock.WithTicker) *IntervalRetrier { 27 | var clock clock.WithTicker = clock.RealClock{} 28 | if len(optClock) > 0 { 29 | clock = optClock[0] 30 | } 31 | 32 | return &IntervalRetrier{ 33 | interval: interval, 34 | doer: doer, 35 | clock: clock, 36 | retriable: retriable, 37 | } 38 | } 39 | 40 | // Do retries performing a call until it succeeds, returns a permanent error or the context is cancelled. 41 | func (r *IntervalRetrier) Do(ctx context.Context) error { 42 | ticker := r.clock.NewTicker(r.interval) 43 | defer ticker.Stop() 44 | 45 | for { 46 | err := r.doer.Do(ctx) 47 | if err == nil { 48 | return nil 49 | } 50 | 51 | if !r.retriable(err) { 52 | return err 53 | } 54 | 55 | select { 56 | case <-ctx.Done(): 57 | return ctx.Err() 58 | case <-ticker.C(): 59 | } 60 | } 61 | } 62 | 63 | // Doer does something and returns an error. 64 | type Doer interface { 65 | // Do performs an operation. 66 | // 67 | // It should return an error that can be checked for retriability. 68 | Do(ctx context.Context) error 69 | } 70 | -------------------------------------------------------------------------------- /internal/role/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "role", 6 | srcs = [ 7 | "role.go", 8 | "role_string.go", 9 | ], 10 | importpath = "cvm-reverse-proxy/internal/role", 11 | visibility = ["//:__subpackages__"], 12 | ) 13 | 14 | go_test( 15 | name = "role_test", 16 | srcs = ["role_test.go"], 17 | embed = [":role"], 18 | deps = [ 19 | "@com_github_stretchr_testify//assert", 20 | "@com_github_stretchr_testify//require", 21 | "@org_uber_go_goleak//:goleak", 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /internal/role/role.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package role 8 | 9 | import ( 10 | "encoding/json" 11 | "strings" 12 | ) 13 | 14 | //go:generate stringer -type=Role 15 | 16 | // Role is a peer's role. 17 | type Role uint 18 | 19 | const ( 20 | // Unknown is the default value for Role and should have no meaning. 21 | Unknown Role = iota 22 | // ControlPlane declares this node as a Kubernetes control plane node. 23 | ControlPlane 24 | // Worker declares this node as a Kubernetes worker node. 25 | Worker 26 | ) 27 | 28 | // TFString returns the role as a string for Terraform. 29 | func (r Role) TFString() string { 30 | switch r { 31 | case ControlPlane: 32 | return "control-plane" 33 | case Worker: 34 | return "worker" 35 | default: 36 | return "unknown" 37 | } 38 | } 39 | 40 | // MarshalJSON marshals the Role to JSON string. 41 | func (r Role) MarshalJSON() ([]byte, error) { 42 | return json.Marshal(r.String()) 43 | } 44 | 45 | // UnmarshalJSON unmarshals the Role from JSON string. 46 | func (r *Role) UnmarshalJSON(b []byte) error { 47 | var roleString string 48 | if err := json.Unmarshal(b, &roleString); err != nil { 49 | return err 50 | } 51 | *r = FromString(roleString) 52 | return nil 53 | } 54 | 55 | // FromString returns the Role for the given string. 56 | func FromString(s string) Role { 57 | switch strings.ToLower(s) { 58 | case "controlplane", "control-plane": 59 | return ControlPlane 60 | case "worker": 61 | return Worker 62 | default: 63 | return Unknown 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /internal/role/role_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Role"; DO NOT EDIT. 2 | 3 | package role 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[Unknown-0] 12 | _ = x[ControlPlane-1] 13 | _ = x[Worker-2] 14 | } 15 | 16 | const _Role_name = "UnknownControlPlaneWorker" 17 | 18 | var _Role_index = [...]uint8{0, 7, 19, 25} 19 | 20 | func (i Role) String() string { 21 | if i >= Role(len(_Role_index)-1) { 22 | return "Role(" + strconv.FormatInt(int64(i), 10) + ")" 23 | } 24 | return _Role_name[_Role_index[i]:_Role_index[i+1]] 25 | } 26 | -------------------------------------------------------------------------------- /internal/semver/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "semver", 6 | srcs = ["semver.go"], 7 | importpath = "cvm-reverse-proxy/internal/semver", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/compatibility", 11 | "@org_golang_x_mod//semver", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "semver_test", 17 | srcs = ["semver_test.go"], 18 | embed = [":semver"], 19 | deps = [ 20 | "@com_github_stretchr_testify//assert", 21 | "@com_github_stretchr_testify//require", 22 | "@in_gopkg_yaml_v3//:yaml_v3", 23 | ], 24 | ) 25 | -------------------------------------------------------------------------------- /internal/sigstore/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "sigstore", 6 | srcs = [ 7 | "rekor.go", 8 | "sign.go", 9 | "sigstore.go", 10 | "verify.go", 11 | ], 12 | importpath = "cvm-reverse-proxy/internal/sigstore", 13 | visibility = ["//:__subpackages__"], 14 | deps = [ 15 | "@com_github_secure_systems_lab_go_securesystemslib//encrypted", 16 | "@com_github_sigstore_rekor//pkg/client", 17 | "@com_github_sigstore_rekor//pkg/generated/client", 18 | "@com_github_sigstore_rekor//pkg/generated/client/entries", 19 | "@com_github_sigstore_rekor//pkg/generated/client/index", 20 | "@com_github_sigstore_rekor//pkg/generated/models", 21 | "@com_github_sigstore_rekor//pkg/types/hashedrekord/v0.0.1:v0_0_1", 22 | "@com_github_sigstore_rekor//pkg/verify", 23 | "@com_github_sigstore_sigstore//pkg/cryptoutils", 24 | "@com_github_sigstore_sigstore//pkg/signature", 25 | ], 26 | ) 27 | 28 | go_test( 29 | name = "sigstore_test", 30 | srcs = [ 31 | "rekor_integration_test.go", 32 | "rekor_test.go", 33 | "sign_test.go", 34 | "verify_test.go", 35 | ], 36 | embed = [":sigstore"], 37 | deps = [ 38 | "@com_github_sigstore_rekor//pkg/generated/models", 39 | "@com_github_sigstore_rekor//pkg/types/hashedrekord/v0.0.1:v0_0_1", 40 | "@com_github_stretchr_testify//assert", 41 | "@com_github_stretchr_testify//require", 42 | "@org_uber_go_goleak//:goleak", 43 | ], 44 | ) 45 | -------------------------------------------------------------------------------- /internal/sigstore/keyselect/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "keyselect", 5 | srcs = ["keyselect.go"], 6 | importpath = "cvm-reverse-proxy/internal/sigstore/keyselect", 7 | visibility = ["//:__subpackages__"], 8 | deps = [ 9 | "//internal/api/versionsapi", 10 | "//internal/constants", 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /internal/sigstore/keyselect/keyselect.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package keyselect is used to select the correct public key for signature verification. 8 | // The content of keyselect must be kept separate from internal/sigstore because keyselect relies on internal/api/versionsapi. 9 | // Since internal/api relies on internal/sigstore, we need to separate the functions to avoid import cycles. 10 | package keyselect 11 | 12 | import ( 13 | "fmt" 14 | 15 | "github.com/flashbots/cvm-reverse-proxy/internal/api/versionsapi" 16 | "github.com/flashbots/cvm-reverse-proxy/internal/constants" 17 | ) 18 | 19 | // CosignPublicKeyForVersion returns the public key for the given version. 20 | func CosignPublicKeyForVersion(ver versionsapi.Version) ([]byte, error) { 21 | if err := ver.Validate(); err != nil { 22 | return nil, fmt.Errorf("selecting public key: invalid version %s: %w", ver.ShortPath(), err) 23 | } 24 | if ver.Ref() == versionsapi.ReleaseRef && ver.Stream() == "stable" { 25 | return []byte(constants.CosignPublicKeyReleases), nil 26 | } 27 | return []byte(constants.CosignPublicKeyDev), nil 28 | } 29 | -------------------------------------------------------------------------------- /internal/sigstore/sigstore.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | // Package sigstore is used to verify Constellation components using sigstore, cosign and rekor. 8 | package sigstore 9 | -------------------------------------------------------------------------------- /internal/staticupload/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "staticupload", 6 | srcs = [ 7 | "delete.go", 8 | "get.go", 9 | "staticupload.go", 10 | "upload.go", 11 | ], 12 | importpath = "cvm-reverse-proxy/internal/staticupload", 13 | visibility = ["//:__subpackages__"], 14 | deps = [ 15 | "//internal/constants", 16 | "@com_github_aws_aws_sdk_go_v2_config//:config", 17 | "@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager", 18 | "@com_github_aws_aws_sdk_go_v2_service_cloudfront//:cloudfront", 19 | "@com_github_aws_aws_sdk_go_v2_service_cloudfront//types", 20 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 21 | "@com_github_google_uuid//:uuid", 22 | ], 23 | ) 24 | 25 | go_test( 26 | name = "staticupload_test", 27 | srcs = ["staticupload_test.go"], 28 | embed = [":staticupload"], 29 | deps = [ 30 | "//internal/logger", 31 | "@com_github_aws_aws_sdk_go_v2_feature_s3_manager//:manager", 32 | "@com_github_aws_aws_sdk_go_v2_service_cloudfront//:cloudfront", 33 | "@com_github_aws_aws_sdk_go_v2_service_cloudfront//types", 34 | "@com_github_aws_aws_sdk_go_v2_service_s3//:s3", 35 | "@com_github_aws_aws_sdk_go_v2_service_s3//types", 36 | "@com_github_stretchr_testify//assert", 37 | "@com_github_stretchr_testify//require", 38 | "@org_uber_go_goleak//:goleak", 39 | ], 40 | ) 41 | -------------------------------------------------------------------------------- /internal/staticupload/get.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package staticupload 8 | 9 | import ( 10 | "context" 11 | 12 | "github.com/aws/aws-sdk-go-v2/service/s3" 13 | ) 14 | 15 | // GetObject retrieves objects from Amazon S3. 16 | func (c *Client) GetObject( 17 | ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options), 18 | ) (*s3.GetObjectOutput, error) { 19 | return c.s3Client.GetObject(ctx, params, optFns...) 20 | } 21 | 22 | // ListObjectsV2 returns some or all (up to 1,000) of the objects in a bucket. 23 | func (c *Client) ListObjectsV2( 24 | ctx context.Context, params *s3.ListObjectsV2Input, optFns ...func(*s3.Options), 25 | ) (*s3.ListObjectsV2Output, error) { 26 | return c.s3Client.ListObjectsV2(ctx, params, optFns...) 27 | } 28 | -------------------------------------------------------------------------------- /internal/staticupload/upload.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package staticupload 8 | 9 | import ( 10 | "context" 11 | "errors" 12 | "fmt" 13 | 14 | s3manager "github.com/aws/aws-sdk-go-v2/feature/s3/manager" 15 | "github.com/aws/aws-sdk-go-v2/service/s3" 16 | ) 17 | 18 | // Upload uploads the given object to S3 and invalidates the CDN cache. 19 | // It returns the upload output or an error. 20 | // The error will be of type InvalidationError if the CDN cache could not be invalidated. 21 | func (c *Client) Upload( 22 | ctx context.Context, input *s3.PutObjectInput, opts ...func(*s3manager.Uploader), 23 | ) (*s3manager.UploadOutput, error) { 24 | if input == nil || input.Key == nil { 25 | return nil, errors.New("key is not set") 26 | } 27 | output, uploadErr := c.uploadClient.Upload(ctx, input, opts...) 28 | if uploadErr != nil { 29 | return nil, fmt.Errorf("uploading object: %w", uploadErr) 30 | } 31 | 32 | if err := c.invalidate(ctx, []string{*input.Key}); err != nil { 33 | return nil, err 34 | } 35 | return output, nil 36 | } 37 | -------------------------------------------------------------------------------- /internal/validation/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "validation", 6 | srcs = [ 7 | "constraints.go", 8 | "errors.go", 9 | "validation.go", 10 | ], 11 | importpath = "cvm-reverse-proxy/internal/validation", 12 | visibility = ["//:__subpackages__"], 13 | ) 14 | 15 | go_test( 16 | name = "validation_test", 17 | srcs = [ 18 | "constraints_test.go", 19 | "errors_test.go", 20 | "validation_test.go", 21 | ], 22 | embed = [":validation"], 23 | deps = [ 24 | "@com_github_stretchr_testify//assert", 25 | "@com_github_stretchr_testify//require", 26 | ], 27 | ) 28 | -------------------------------------------------------------------------------- /internal/verify/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "verify", 6 | srcs = ["verify.go"], 7 | importpath = "cvm-reverse-proxy/internal/verify", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/attestation/snp", 11 | "//internal/config", 12 | "@com_github_golang_jwt_jwt_v5//:jwt", 13 | "@com_github_google_go_sev_guest//abi", 14 | "@com_github_google_go_sev_guest//kds", 15 | "@com_github_google_go_sev_guest//verify/trust", 16 | ], 17 | ) 18 | 19 | go_test( 20 | name = "verify_test", 21 | srcs = ["verify_test.go"], 22 | embed = [":verify"], 23 | deps = [ 24 | "//internal/attestation/snp/testdata", 25 | "//internal/logger", 26 | "@com_github_stretchr_testify//assert", 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /internal/versions/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "versions", 6 | srcs = ["versions.go"], 7 | importpath = "cvm-reverse-proxy/internal/versions", 8 | visibility = ["//:__subpackages__"], 9 | deps = [ 10 | "//internal/compatibility", 11 | "//internal/constants", 12 | "//internal/versions/components", 13 | "@org_golang_x_mod//semver", 14 | ], 15 | ) 16 | 17 | go_test( 18 | name = "versions_test", 19 | srcs = ["versions_test.go"], 20 | embed = [":versions"], 21 | deps = [ 22 | "@com_github_stretchr_testify//assert", 23 | "@com_github_stretchr_testify//require", 24 | "@com_github_vincent_petithory_dataurl//:dataurl", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /internal/versions/components/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 3 | load("@rules_proto//proto:defs.bzl", "proto_library") 4 | load("//bazel/go:go_test.bzl", "go_test") 5 | load("//bazel/proto:rules.bzl", "write_go_proto_srcs") 6 | 7 | go_library( 8 | name = "components", 9 | srcs = ["components.go"], 10 | embed = [":components_go_proto"], 11 | importpath = "cvm-reverse-proxy/internal/versions/components", 12 | visibility = ["//:__subpackages__"], 13 | ) 14 | 15 | proto_library( 16 | name = "components_proto", 17 | srcs = ["components.proto"], 18 | visibility = ["//:__subpackages__"], 19 | ) 20 | 21 | go_proto_library( 22 | name = "components_go_proto", 23 | importpath = "cvm-reverse-proxy/internal/versions/components", 24 | proto = ":components_proto", 25 | visibility = ["//:__subpackages__"], 26 | ) 27 | 28 | write_go_proto_srcs( 29 | name = "write_generated_protos", 30 | src = "components.pb.go", 31 | go_proto_library = ":components_go_proto", 32 | visibility = ["//visibility:public"], 33 | ) 34 | 35 | go_test( 36 | name = "components_test", 37 | srcs = ["components_test.go"], 38 | embed = [":components"], 39 | deps = [ 40 | "@com_github_stretchr_testify//assert", 41 | "@com_github_stretchr_testify//require", 42 | ], 43 | ) 44 | -------------------------------------------------------------------------------- /internal/versions/components/components.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package components; 4 | 5 | option go_package = "cvm-reverse-proxy/internal/versions/components"; 6 | 7 | // Component is a Kubernetes component to install. 8 | message Component { 9 | // URL of the component. Usually, this would be an HTTP download link. 10 | string url = 1; 11 | // Hash contains the expected digest of the resource retrieved from the URL, 12 | // in the format ":". 13 | string hash = 2; 14 | // InstallPath is the path to install the component to. 15 | string install_path = 3; 16 | // Extract indicates whether the resource at above URL is an archive, such as 17 | // a gzipped tarball, and should be extracted to the install_path. 18 | bool extract = 4; 19 | } 20 | -------------------------------------------------------------------------------- /internal/versions/hash-generator/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 2 | load("//bazel/go:go_test.bzl", "go_test") 3 | 4 | go_library( 5 | name = "hash-generator_lib", 6 | srcs = ["generate.go"], 7 | importpath = "cvm-reverse-proxy/internal/versions/hash-generator", 8 | visibility = ["//visibility:private"], 9 | deps = [ 10 | "@com_github_regclient_regclient//:regclient", 11 | "@com_github_regclient_regclient//types/ref", 12 | "@com_github_vincent_petithory_dataurl//:dataurl", 13 | "@io_k8s_kubernetes//cmd/kubeadm/app/apis/kubeadm", 14 | "@io_k8s_kubernetes//cmd/kubeadm/app/images", 15 | "@org_golang_x_tools//go/ast/astutil", 16 | ], 17 | ) 18 | 19 | go_binary( 20 | name = "hash-generator", 21 | embed = [":hash-generator_lib"], 22 | visibility = ["//:__subpackages__"], 23 | ) 24 | 25 | go_test( 26 | name = "hash-generator_test", 27 | srcs = ["generate_test.go"], 28 | embed = [":hash-generator_lib"], 29 | deps = ["@org_uber_go_goleak//:goleak"], 30 | ) 31 | -------------------------------------------------------------------------------- /internal/versions/hash-generator/generate_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Edgeless Systems GmbH 3 | 4 | SPDX-License-Identifier: AGPL-3.0-only 5 | */ 6 | 7 | package main 8 | 9 | import ( 10 | "testing" 11 | 12 | "go.uber.org/goleak" 13 | ) 14 | 15 | func TestMain(m *testing.M) { 16 | goleak.VerifyTestMain(m, goleak.IgnoreAnyFunction("github.com/bazelbuild/rules_go/go/tools/bzltestutil.RegisterTimeoutHandler.func1")) 17 | } 18 | -------------------------------------------------------------------------------- /proxy-server.dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM golang:1.23 AS builder 3 | ARG VERSION 4 | WORKDIR /build 5 | ADD go.mod /build/ 6 | RUN --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 GOOS=linux \ 7 | go mod download 8 | ADD . /build/ 9 | RUN --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 GOOS=linux \ 10 | go build \ 11 | -trimpath \ 12 | -ldflags "-s -X github.com/flashbots/cvm-reverse-proxy/common.Version=${VERSION}" \ 13 | -v \ 14 | -o proxy-server \ 15 | cmd/proxy-server/main.go 16 | 17 | FROM alpine:latest 18 | WORKDIR /app 19 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ 20 | COPY --from=builder /build/proxy-server /app/proxy-server 21 | RUN echo "[{}]" > /app/measurements-empty.json 22 | ENV LISTEN_ADDR=":8080" 23 | EXPOSE 8080 24 | CMD ["/app/proxy-server"] 25 | -------------------------------------------------------------------------------- /proxy/mutli_validator.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "context" 5 | "encoding/asn1" 6 | 7 | "github.com/flashbots/cvm-reverse-proxy/internal/atls" 8 | ) 9 | 10 | // MultiValidator is a validator for Azure confidential VM attestation using TDX which accepts multiple measurements 11 | type MultiValidator struct { 12 | oid asn1.ObjectIdentifier 13 | validators []atls.Validator 14 | } 15 | 16 | // NewMultiValidator returns a new Validator for Azure confidential VM attestation using TDX which accepts multiple measurements 17 | func NewMultiValidator(validators []atls.Validator) *MultiValidator { 18 | for _, v := range validators { 19 | if !v.OID().Equal(validators[0].OID()) { 20 | // This is not an error! This is a bug! 21 | panic("validators with mismatching OIDs passed in!") 22 | } 23 | } 24 | return &MultiValidator{ 25 | oid: validators[0].OID(), 26 | validators: validators, 27 | } 28 | } 29 | 30 | func (v *MultiValidator) OID() asn1.ObjectIdentifier { 31 | return v.oid 32 | } 33 | 34 | func (v *MultiValidator) Validate(ctx context.Context, attDocRaw, nonce []byte) (userData []byte, err error) { 35 | for _, validator := range v.validators { 36 | if userData, err = validator.Validate(ctx, attDocRaw, nonce); err == nil { 37 | return 38 | } 39 | } 40 | 41 | return 42 | } 43 | -------------------------------------------------------------------------------- /proxy/proxy_test.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "io" 5 | "log/slog" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | 10 | "github.com/flashbots/cvm-reverse-proxy/common" 11 | "github.com/flashbots/cvm-reverse-proxy/internal/atls" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func getTestLogger() *slog.Logger { 16 | return common.SetupLogger(&common.LoggingOpts{ 17 | Debug: true, 18 | JSON: false, 19 | Service: "test", 20 | Version: "test", 21 | }) 22 | } 23 | 24 | func Test_Handlers_Healthcheck_Drain_Undrain(t *testing.T) { 25 | testEchoServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 26 | _, _ = io.WriteString(w, "checkcheck") 27 | })) 28 | defer testEchoServer.Close() 29 | 30 | proxy := NewProxy(getTestLogger(), testEchoServer.URL, []atls.Validator{}) 31 | 32 | { // Check green path 33 | req := httptest.NewRequest(http.MethodGet, "http://proxyhost.should.not.matter/", nil) //nolint:goconst,nolintlint 34 | w := httptest.NewRecorder() 35 | proxy.ServeHTTP(w, req) 36 | resp := w.Result() 37 | defer resp.Body.Close() 38 | respBody, err := io.ReadAll(resp.Body) 39 | require.NoError(t, err) 40 | require.Equal(t, http.StatusOK, resp.StatusCode, "Must return `Ok`") 41 | require.Equal(t, []byte("checkcheck"), respBody) 42 | } 43 | 44 | // Check failure if measurement header is present 45 | for _, header := range []string{AttestationTypeHeader, MeasurementHeader} { 46 | req := httptest.NewRequest(http.MethodGet, "http://proxyhost.should.not.matter/", nil) //nolint:goconst,nolintlint 47 | req.Header.Add(header, "xx") 48 | w := httptest.NewRecorder() 49 | proxy.ServeHTTP(w, req) 50 | resp := w.Result() 51 | defer resp.Body.Close() 52 | respBody, err := io.ReadAll(resp.Body) 53 | require.NoError(t, err) 54 | require.Equal(t, http.StatusForbidden, resp.StatusCode, "Must return `Forbidden` on measurements header") 55 | require.Contains(t, string(respBody), "unexpected") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /staticcheck.conf: -------------------------------------------------------------------------------- 1 | checks = ["all"] 2 | # checks = ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022", "-ST1023"] 3 | initialisms = ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS", "SIP", "RTP", "AMQP", "DB", "TS"] 4 | dot_import_whitelist = ["github.com/mmcloughlin/avo/build", "github.com/mmcloughlin/avo/operand", "github.com/mmcloughlin/avo/reg"] 5 | http_status_code_whitelist = ["200", "400", "404", "500"] 6 | --------------------------------------------------------------------------------