diff --git a/Dockerfile b/Dockerfile index 86071de..e1e9a0f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -74,21 +74,21 @@ COPY --from=build-autoscaler /usr/local/bin/autoscaler /usr/local/bin/autoscaler ENTRYPOINT ["autoscaler"] -## tpod-proxy: ## +## tpodproxy: ## -FROM build-common as build-tpod-proxy +FROM build-common as build-tpodproxy -COPY pkg/proxy/ ./proxy -RUN --mount=type=cache,target=/root/.cache/go-build go build -v -o /usr/local/bin/tpod-proxy ./proxy +COPY cmd/tpodproxy/ ./cmd/tpodproxy +RUN --mount=type=cache,target=/root/.cache/go-build go build -v -o /usr/local/bin/tpodproxy ./cmd/tpodproxy -FROM run-common as tpod-proxy +FROM run-common as tpodproxy -COPY --from=build-tpod-proxy /usr/local/bin/tpod-proxy /usr/local/bin/tpod-proxy +COPY --from=build-tpodproxy /usr/local/bin/tpodproxy /usr/local/bin/tpodproxy -ENTRYPOINT ["tpod-proxy"] +ENTRYPOINT ["tpodproxy"] -FROM run-common AS tpod-proxy-copy-local +FROM run-common AS tpodproxy-copy-local -COPY ./bin/proxy /usr/local/bin/tpod-proxy +COPY ./bin/tpodproxy /usr/local/bin/tpodproxy -ENTRYPOINT ["tpod-proxy"] +ENTRYPOINT ["tpodproxy"] diff --git a/cmd/tpodproxy/main.go b/cmd/tpodproxy/main.go new file mode 100644 index 0000000..2f605e3 --- /dev/null +++ b/cmd/tpodproxy/main.go @@ -0,0 +1,160 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os" + "os/signal" + "regexp" + + tpk8s "github.com/comrade-coop/apocryph/pkg/kubernetes" + "github.com/spf13/cobra" +) + +const SpecialHeaderName = "X-Apocryph-Expected" + +// const SpecialCookieName = "X-Apocryph-Expected" // Could support it as a cookie too + +const VerificationServiceURL = "http://verification-service.kube-system.svc.cluster.local:8080" + +var ValidServiceNames = regexp.MustCompile("^tpod-vh-[a-z0-9-]{52}$") //^[a-z][a-z0-9-]{0,62}$ + +var currentBackingService string // e.g. tpod-XXX +var backingServiceSuffix string // e.g. .NS.svc.cluster.local +var extraAttestationDetails []tpk8s.AttestationValue // TODO: wire up +var serveAddress = ":9999" + +func main() { + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + interruptChan := make(chan os.Signal, 1) + signal.Notify(interruptChan, os.Interrupt) + + go func() { + <-interruptChan + cancel() + }() + + if err := proxyCmd.ExecuteContext(ctx); err != nil { + os.Exit(1) + } +} + +var proxyCmd = &cobra.Command{ + Use: "tpodproxy [backing service] [backing service suffix] [extra attestation info]", + Short: "Start a facade routing requests to the right version of an application and serving attestation information", + Long: "Start a facade routing requests to the right version of an application and serving attestation information.\n" + + "Example: tpodproxy svc1 .namespace.cluster.local:80 docker.io/org/image@sha256:1234...", + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + currentBackingService = args[0] + backingServiceSuffix = args[1] + extraAttestationDetailsJson := args[2] + err := json.Unmarshal([]byte(extraAttestationDetailsJson), &extraAttestationDetails) + if err != nil { + return err + } + if !ValidServiceNames.MatchString(currentBackingService) { + return fmt.Errorf("Invalid value (%s) for backing service, must be a valid service name", currentBackingService) + } + + mux := http.NewServeMux() + mux.HandleFunc("GET /.well-known/network.apocryph.attest", attestHandler) + mux.HandleFunc("/", wildcardHandler) + s := &http.Server{ + Addr: serveAddress, + Handler: mux, + } + go func() { + <-cmd.Context().Done() + err := s.Shutdown(context.TODO()) + cmd.PrintErr(err) + }() + + cmd.PrintErr("Server is listening on ", serveAddress) + err = s.ListenAndServe() + cmd.PrintErr(err) + + return err + }, +} + +func init() { + proxyCmd.Flags().StringVar(&serveAddress, "address", "", "port to serve on") +} + +type attestation struct { // From constellation/verify/server/server.go + Data []byte `json:"data"` +} +type AttestationResult struct { + AttestationData []byte `json:"attestation"` + AttestationError string `json:"error"` + ExtraData []tpk8s.AttestationValue `json:"extra"` + Header string `json:"header"` +} + +func attestHandler(w http.ResponseWriter, r *http.Request) { + result := AttestationResult{} + + // TODO: Have the extra attestation data be the user data parameter for validation + attestationJson, attestationErr := http.Get(VerificationServiceURL + "/?nonce=" + url.QueryEscape(r.URL.Query().Get("nonce"))) + if attestationErr != nil { + result.AttestationError = attestationErr.Error() + } else { + err := json.NewDecoder(attestationJson.Body).Decode(&result.AttestationData) + if err != nil { + w.WriteHeader(http.StatusBadGateway) + _, _ = w.Write([]byte(err.Error())) + return + } + } + + result.ExtraData = extraAttestationDetails + result.Header = fmt.Sprintf("%s: %s", SpecialHeaderName, currentBackingService) + + // Write the info string to the response + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(result) +} + +func wildcardHandler(w http.ResponseWriter, r *http.Request) { + expectedService := r.Header.Get(SpecialHeaderName) + if expectedService == "" { + expectedService = currentBackingService + } + if !ValidServiceNames.MatchString(expectedService) { + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte("Invalid value for " + SpecialHeaderName)) + return + } + proxyRequest := r.Clone(r.Context()) + proxyRequest.URL.Scheme = "http" + proxyRequest.URL.Host = expectedService + backingServiceSuffix + proxyRequest.RequestURI = "" + // TODO: Figure out if we are doing anything about proxyRequest.Host / proxyRequest.Header["Host"] + proxyResponse, err := http.DefaultClient.Do(proxyRequest) + if err != nil { + _, _ = w.Write([]byte(err.Error())) + return + } + if r.Method == http.MethodOptions { + proxyResponse.Header.Add("Access-Control-Request-Headers", SpecialHeaderName) + } + for header, values := range proxyResponse.Header { // HACK: ...there's got to be a simpler way... + for _, value := range values { + w.Header().Add(header, value) + } + } + w.WriteHeader(proxyResponse.StatusCode) + io.Copy(w, proxyResponse.Body) + for header, values := range proxyResponse.Trailer { + for _, value := range values { + w.Header().Add(header, value) + } + } +} diff --git a/cmd/trustedpods/verify.go b/cmd/trustedpods/verify.go index d2688f0..d738ad8 100644 --- a/cmd/trustedpods/verify.go +++ b/cmd/trustedpods/verify.go @@ -37,6 +37,13 @@ var verifyPodCmd = &cobra.Command{ }, } +type AttestationResult struct { // HACK: From ../tpodproxy/main.go + AttestationData []byte `json:"attestation"` + AttestationError string `json:"error"` + ExtraData []tpk8s.AttestationValue `json:"extra"` + Header string `json:"header"` +} + var verifyImageCmd = &cobra.Command{ Use: fmt.Sprintf("verify image"), Short: "Verify image signature", @@ -73,14 +80,14 @@ var verifyImageCmd = &cobra.Command{ return fmt.Errorf("Failed to read response body: %v", err) } - var annotationValues []tpk8s.AnnotationValue - if err := json.Unmarshal(body, &annotationValues); err != nil { + var attestationResult AttestationResult + if err := json.Unmarshal(body, &attestationResult); err != nil { return fmt.Errorf("Failed to unmarshal JSON response: %v", err) } // Verify each image from the response verifyOptions := publisher.DefaultVerifyOptions() images := []*proto.Image{} - for _, av := range annotationValues { + for _, av := range attestationResult.ExtraData { image := &proto.Image{Url: av.URL, VerificationDetails: &proto.VerificationDetails{Signature: av.Signature, Identity: av.Identity, Issuer: av.Issuer}} images = append(images, image) } diff --git a/deploy/Tiltfile b/deploy/Tiltfile index 9865347..7a3a0fd 100644 --- a/deploy/Tiltfile +++ b/deploy/Tiltfile @@ -169,7 +169,7 @@ def apocryph_resource( kubectl_command = "kubectl get all -o yaml -n $(%s)" % namespace_cmd apply_cmd = "set -ex; " + deploy_cmd + " 1>&2" + " && " + kubectl_command - delete_cmd = ( + undeploy_cmd = ( manifest_cmd + " | " + cmdline_in_builder( @@ -178,6 +178,8 @@ def apocryph_resource( interactive=True, ) ) + kubectl_wait_command = "kubectl wait --for=delete ns $(%s)" % namespace_cmd + delete_cmd = "set -ex; " + undeploy_cmd + " 1>&2" + " && " + kubectl_wait_command k8s_custom_deploy( name, @@ -332,7 +334,7 @@ def apocryph_build_with_builder( local_resource_in_builder( "tpodserver-go-compile", - 'go build -v -buildvcs=false -ldflags="-s -w" -o bin/ ./cmd/tpodserver ./cmd/ipfs-p2p-helper ./cmd/trustedpods ./pkg/proxy', + 'go build -v -buildvcs=false -ldflags="-s -w" -o bin/ ./cmd/tpodserver ./cmd/ipfs-p2p-helper ./cmd/trustedpods ./cmd/tpodproxy', "apocryph-go-builder", deps=[root_dir + "/cmd", root_dir + "/pkg"], allow_parallel=True, @@ -361,14 +363,22 @@ def apocryph_build_with_builder( sync(root_dir + "/bin", "/usr/local/bin/"), ], ) - + docker_build( + "comradecoop/apocryph/tpodproxy", + root_dir, + dockerfile="./Dockerfile", + target="tpodproxy-copy-local", + entrypoint=["/usr/local/bin/tpodproxy"], + only=[root_dir + "/bin"], + ) + """ # https://stackoverflow.com/a/33511811 for $(docker inspect --format ...) docker_build_with_restart( - "comradecoop/apocryph/tpod-proxy-unsigned", + "comradecoop/apocryph/tpodproxy-unsigned", root_dir, dockerfile="./Dockerfile", - target="tpod-proxy-copy-local", - entrypoint=["/usr/local/bin/tpod-proxy"], + target="tpodproxy-copy-local", + entrypoint=["/usr/local/bin/tpodproxy"], only=[root_dir + "/bin"], live_update=[ sync(root_dir + "/bin", "/usr/local/bin/"), @@ -376,14 +386,15 @@ def apocryph_build_with_builder( ) cosign_sign_image_key( - "comradecoop/apocryph/tpod-proxy", - "comradecoop/apocryph/tpod-proxy-unsigned", + "comradecoop/apocryph/tpodproxy", + "comradecoop/apocryph/tpodproxy-unsigned", cosign_key=cosign_key, cosign_key_path=cosign_key_path, live_update=[ sync(root_dir + "/bin", "/usr/local/bin/"), ], ) + """ """ # TODO: Need to also build a trustedpods image for use with apocryph_resource... @@ -513,8 +524,10 @@ def deploy_apocryph_stack( resource_deps=["anvil", "ipfs", "loki", "anvil-deploy-contracts", "policy-controller"], labels=["apocryph"], image_keys=["image", "policy.image"], - image_deps=["comradecoop/apocryph/server", "comradecoop/apocryph/tpod-proxy"], + image_deps=["comradecoop/apocryph/server", "comradecoop/apocryph/tpodproxy"], flags=[ + "--set-json", + "policy.enable=false", "--set-json", "policy.issuer=false", "--set-json", diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 9d805c3..f541dfe 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -6,7 +6,7 @@ const PAYMENT_ADDR_KEY = "PAYMENT_ADDRESS" const PUBLISHER_ADDR_KEY = "PUBLISHER_ADDRESS" const PROVIDER_ADDR_KEY = "PROVIDER_ADDRESS" const POD_ID_KEY = "POD_ID" -const NamespaceKey = "NAMESPACE_NAME" const OUTPUT_SIGNATURE_PATH = "~/.apocryph/signatures/" +const ReservedTpodProxyPort int32 = 63917 diff --git a/pkg/kubernetes/namespaces.go b/pkg/kubernetes/namespaces.go index c4d3224..f287d44 100644 --- a/pkg/kubernetes/namespaces.go +++ b/pkg/kubernetes/namespaces.go @@ -24,7 +24,6 @@ const ( LabelIpfsP2P string = "coop.comrade/apocryph-p2p-helper" AnnotationsIpfsP2P string = "coop.comrade/apocryph-p2p-helper" SigstorePolicy string = "policy.sigstore.dev/include" - AnnotationVerificationInfo string = "coop.comrade/apocryph-verification-info" LabelClusterImagePolicy string = "coop.comrade/apocryph-for-pod" ) diff --git a/pkg/kubernetes/pods.go b/pkg/kubernetes/pods.go index 2a45988..a0c1d79 100644 --- a/pkg/kubernetes/pods.go +++ b/pkg/kubernetes/pods.go @@ -4,6 +4,7 @@ package kubernetes import ( "context" + "encoding/base32" "encoding/json" "fmt" "log" @@ -14,12 +15,12 @@ import ( "github.com/ethereum/go-ethereum/common" kedahttpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" policy "github.com/sigstore/policy-controller/pkg/apis/policy/v1beta1" + "golang.org/x/crypto/sha3" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" - "knative.dev/pkg/ptr" k8cl "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -78,6 +79,10 @@ func ApplyPodRequest( } labels := map[string]string{"tpod": "1"} + podLabels := map[string]string{} + for k, v := range labels { + podLabels[k] = v + } depLabels := map[string]string{} activeResource := []string{} startupReplicas := int32(0) @@ -94,12 +99,12 @@ func ApplyPodRequest( Selector: metav1.SetAsLabelSelector(labels), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: labels, + Labels: podLabels, }, }, }, } - deploymentAnnotationValues := []AnnotationValue{} + attestationValues := []AttestationValue{} podTemplate := &deployment.Spec.Template @@ -108,6 +113,7 @@ func ApplyPodRequest( localhostAliases := corev1.HostAlias{IP: "127.0.0.1"} + var httpPortName string for cIdx, container := range podManifest.Containers { containerSpec := corev1.Container{ @@ -131,7 +137,14 @@ func ApplyPodRequest( containerSpec.Env = append(containerSpec.Env, corev1.EnvVar{Name: constants.PUBLIC_ADDRESS_KEY, Value: podManifest.KeyPair.PubAddress}) containerSpec.Env = append(containerSpec.Env, corev1.EnvVar{Name: constants.PRIVATE_KEY, Value: podManifest.KeyPair.PrivateKey}) } - + + attestationValues = append(attestationValues, AttestationValue{ + URL: container.GetImage().GetUrl(), + Signature: container.GetImage().VerificationDetails.GetSignature(), + Issuer: container.GetImage().VerificationDetails.GetIssuer(), + Identity: container.GetImage().VerificationDetails.GetIdentity(), + }) + if podManifest.VerificationSettings.GetForcePolicy() && container.Image.VerificationDetails != nil { policyName := fmt.Sprintf("policy-%v-%v", podId, cIdx) // NOTE: Will break for two-digit container ids (see note around podId) sigstorePolicy := &policy.ClusterImagePolicy{ @@ -149,13 +162,6 @@ func ApplyPodRequest( } identity := policy.Identity{Issuer: container.Image.VerificationDetails.Issuer, Subject: container.Image.VerificationDetails.Identity} sigstorePolicy.Spec.Authorities[0].Keyless.Identities = []policy.Identity{identity} - annotationValue := AnnotationValue{ - URL: container.Image.Url, - Signature: container.Image.VerificationDetails.Signature, - Issuer: container.Image.VerificationDetails.Issuer, - Identity: container.Image.VerificationDetails.Identity, - } - deploymentAnnotationValues = append(deploymentAnnotationValues, annotationValue) err := updateOrCreate(ctx, policyName, "ClusterImagePolicy", namespace, sigstorePolicy, client, update) if err != nil && strings.Contains(err.Error(), "already exists") { log.Println("warning: Policies were not deleted properly") @@ -170,6 +176,9 @@ func ApplyPodRequest( } for _, port := range container.Ports { + if int32(port.ContainerPort) == constants.ReservedTpodProxyPort { + return fmt.Errorf("Port %d is reserved", port.ContainerPort) + } portName := fmt.Sprintf("p%d-%d", cIdx, port.ContainerPort) containerSpec.Ports = append(containerSpec.Ports, corev1.ContainerPort{ ContainerPort: int32(port.ContainerPort), @@ -190,6 +199,7 @@ func ApplyPodRequest( switch port.ExposedPort.(type) { case *pb.Container_Port_HostHttpHost: + httpPortName = portName httpSO.Spec.ScaleTargetRef.Service = service.ObjectMeta.Name httpSO.Spec.ScaleTargetRef.Port = servicePort httpSO.Spec.ScaleTargetRef.APIVersion = "apps/v1" @@ -228,9 +238,9 @@ func ApplyPodRequest( depLabels["containers"] = depLabels["containers"] + "_" + containerSpec.Name } } - + podTemplate.Spec.HostAliases = append(podTemplate.Spec.HostAliases, localhostAliases) - + for _, volume := range podManifest.Volumes { volumeSpec := corev1.Volume{ Name: volume.Name, @@ -296,24 +306,57 @@ func ApplyPodRequest( podTemplate.Spec.Volumes = append(podTemplate.Spec.Volumes, volumeSpec) } + // Public verifiability configured; deploy the proxy if podManifest.VerificationSettings.GetPublicVerifiability() == true { - verificationHost := podManifest.VerificationSettings.GetVerificationHost() - if verificationHost == "" && len(httpSO.Spec.Hosts) > 0 { - httpHost := httpSO.Spec.Hosts[0] - lastDotIndex := strings.LastIndex(httpHost, ".") - if lastDotIndex == -1 { - verificationHost = httpHost + ".tpodinfo" - } else { - verificationHost = httpHost[:lastDotIndex] + ".tpodinfo" + httpHost[lastDotIndex:] - } + if httpPortName == "" { + return fmt.Errorf("Public verifiability requires having an HTTP-exposed port") + } + + // TODO: Do not delete the previous versionedService(s) until the new deployment is fully rolled out + versionedServiceName := getHttpBackingServiceName(attestationValues) + + versionedLabels := map[string]string{"tpod-version": versionedServiceName} + for k, v := range versionedLabels { + podLabels[k] = v } - if verificationHost == "" { - return fmt.Errorf("Public verifiability is set but no verification host path is available or could be derived") + + versionedService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: versionedServiceName, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{ + Port: 80, + TargetPort: intstr.FromString(httpPortName), + }}, + Selector: versionedLabels, + }, + } + err := updateOrCreate(ctx, versionedServiceName, "Service", namespace, versionedService, client, update) + if err != nil { + return err + } + activeResource = append(activeResource, versionedServiceName) + attestationValuesJson, err := json.Marshal(attestationValues) + if err != nil { + return fmt.Errorf("Failed to marshal attestation values: %v", err) } - response.VerificationHost = verificationHost - // used only to use the routing from keda ingress controller - routeHttpsoName := "route-hso" - routeHttpso := NewHttpSo(namespace, routeHttpsoName) + + proxyContainer := corev1.Container{ + Name: "tpodsproxy-sc", + Ports: []corev1.ContainerPort{{ContainerPort: constants.ReservedTpodProxyPort, Name: "tpodsproxy-sc"}}, + Image: proxyImage, + Args: []string{ + versionedServiceName, + "." + namespace + ".svc.cluster.local", + string(attestationValuesJson), + "--address", + fmt.Sprintf(":%d", constants.ReservedTpodProxyPort), + }, + } + podTemplate.Spec.Containers = append(podTemplate.Spec.Containers, proxyContainer) + + serviceProxyPort := constants.ReservedTpodProxyPort serviceProxyName := "tpod-svc-proxy" serviceProxy := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -321,45 +364,24 @@ func ApplyPodRequest( }, Spec: corev1.ServiceSpec{ Type: corev1.ServiceTypeClusterIP, - Ports: []corev1.ServicePort{{Port: 9999, TargetPort: intstr.FromString("tpod-proxy")}}, + Ports: []corev1.ServicePort{{Port: serviceProxyPort, TargetPort: intstr.FromInt32(constants.ReservedTpodProxyPort)}}, Selector: labels, }, } - routeHttpso.Spec.ScaleTargetRef.Service = serviceProxy.ObjectMeta.Name - routeHttpso.Spec.ScaleTargetRef.Port = 9999 - routeHttpso.Spec.ScaleTargetRef.APIVersion = "apps/v1" - routeHttpso.Spec.Hosts = []string{verificationHost} - routeHttpso.Spec.Replicas = &kedahttpv1alpha1.ReplicaStruct{Min: ptr.Int32(1), Max: ptr.Int32(1)} - proxyContainer := corev1.Container{ - Name: "proxy", - Image: proxyImage, - Ports: []corev1.ContainerPort{{ContainerPort: 9999, Name: "tpod-proxy"}}, - } - err := updateOrCreate(ctx, serviceProxyName, "Service", namespace, serviceProxy, client, update) + err = updateOrCreate(ctx, serviceProxyName, "Service", namespace, serviceProxy, client, update) if err != nil { return err } - err = updateOrCreate(ctx, routeHttpsoName, "HttpSo", namespace, routeHttpso, client, update) - if err != nil { - return err - } - proxyContainer.Env = append(proxyContainer.Env, corev1.EnvVar{Name: constants.NamespaceKey, Value: namespace}) - podTemplate.Spec.Containers = append(podTemplate.Spec.Containers, proxyContainer) - } - - annotationValuesJson, err := json.Marshal(deploymentAnnotationValues) - if err != nil { - return fmt.Errorf("Failed to marshal annotation values: %v", err) + activeResource = append(activeResource, serviceProxyName) + httpSO.Spec.ScaleTargetRef.Service = serviceProxyName + httpSO.Spec.ScaleTargetRef.Port = serviceProxyPort } - deployment.Annotations = map[string]string{ - AnnotationVerificationInfo: string(annotationValuesJson), - } - + if httpSO.Spec.ScaleTargetRef.Service == "" { // No scaler configured - just deploy min replicas startupReplicas = int32(podManifest.Replicas.GetMin()) } - err = updateOrCreate(ctx, deploymentName, "Deployment", namespace, deployment, client, update) + err := updateOrCreate(ctx, deploymentName, "Deployment", namespace, deployment, client, update) if err != nil { return err } @@ -402,6 +424,16 @@ func ApplyPodRequest( return nil } +func getHttpBackingServiceName(attestationValues []AttestationValue) string { + hash := sha3.New256() + for _, v := range attestationValues { + hash.Write([]byte(v.URL)) + hash.Write([]byte{0}) + } + resultHash := hash.Sum(nil) + return "tpod-vh-" + strings.TrimRight(strings.ToLower(base32.StdEncoding.EncodeToString(resultHash)), "=") +} + func convertResourceList(resources []*pb.Resource) corev1.ResourceList { result := make(corev1.ResourceList, len(resources)) for _, res := range resources { @@ -412,11 +444,11 @@ func convertResourceList(resources []*pb.Resource) corev1.ResourceList { case *pb.Resource_AmountMillis: quantity = *resource.NewMilliQuantity(int64(q.AmountMillis), resource.BinarySI) case *pb.Resource_AmountKibi: - quantity = *resource.NewQuantity(int64(q.AmountKibi) * 1024, resource.BinarySI) + quantity = *resource.NewQuantity(int64(q.AmountKibi)*1024, resource.BinarySI) case *pb.Resource_AmountMebi: - quantity = *resource.NewQuantity(int64(q.AmountMebi) * 1024 * 1024, resource.BinarySI) + quantity = *resource.NewQuantity(int64(q.AmountMebi)*1024*1024, resource.BinarySI) case *pb.Resource_AmountGibi: - quantity = *resource.NewQuantity(int64(q.AmountGibi) * 1024 * 1024 * 1024, resource.BinarySI) + quantity = *resource.NewQuantity(int64(q.AmountGibi)*1024*1024*1024, resource.BinarySI) } result[corev1.ResourceName(res.Resource)] = quantity } diff --git a/pkg/kubernetes/utils.go b/pkg/kubernetes/utils.go index 39e45a7..646295a 100644 --- a/pkg/kubernetes/utils.go +++ b/pkg/kubernetes/utils.go @@ -41,6 +41,7 @@ func NewService(port *pb.Container_Port, portName string, httpSO *kedahttpv1alph switch ep := port.ExposedPort.(type) { case *pb.Container_Port_HostHttpHost: service.Spec.Type = corev1.ServiceTypeClusterIP + service.Spec.Ports[0].Port = servicePort if len(httpSO.Spec.Hosts) > 0 { return nil, 0, errors.New("Multiple HTTP hosts in pod definition") } @@ -88,7 +89,7 @@ func GetResource(kind string) k8cl.Object { return nil } -type AnnotationValue struct { +type AttestationValue struct { URL string `json:"url"` Issuer string `json:"issuer"` Identity string `json:"identity"` diff --git a/pkg/proto/protoconnect/interceptors.go b/pkg/proto/protoconnect/interceptors.go index d2e677d..fd425d0 100644 --- a/pkg/proto/protoconnect/interceptors.go +++ b/pkg/proto/protoconnect/interceptors.go @@ -51,9 +51,6 @@ func NewAuthInterceptor(c client.Client) connect.Interceptor { func (i authInterceptor) WrapUnary(handler connect.UnaryFunc) connect.UnaryFunc { return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) { fmt.Printf("Authenticating gRPC call: %v \n", req.Spec()) - if req.Spec().Procedure == ProvisionPodServiceGetPodInfosProcedure { - return handler(ctx, req) - } expectedPublisher, err := i.authenticate(req.Header()) if err != nil { return nil, err @@ -189,9 +186,6 @@ func NewAuthInterceptorClient(deployment *pb.Deployment, expirationOffset int64, func (a *AuthInterceptorClient) WrapUnary(handler connect.UnaryFunc) connect.UnaryFunc { return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) { - if req.Spec().Procedure == ProvisionPodServiceGetPodInfosProcedure { - return handler(ctx, req) - } a.authorize(req.Spec().Procedure, req.Header()) return handler(ctx, req) } diff --git a/pkg/proto/protoconnect/provision-pod.connect.go b/pkg/proto/protoconnect/provision-pod.connect.go index 9ad6638..d40c3ab 100644 --- a/pkg/proto/protoconnect/provision-pod.connect.go +++ b/pkg/proto/protoconnect/provision-pod.connect.go @@ -44,9 +44,6 @@ const ( // ProvisionPodServiceDeletePodProcedure is the fully-qualified name of the ProvisionPodService's // DeletePod RPC. ProvisionPodServiceDeletePodProcedure = "/apocryph.proto.v0.provisionPod.ProvisionPodService/DeletePod" - // ProvisionPodServiceGetPodInfosProcedure is the fully-qualified name of the ProvisionPodService's - // GetPodInfos RPC. - ProvisionPodServiceGetPodInfosProcedure = "/apocryph.proto.v0.provisionPod.ProvisionPodService/GetPodInfos" // ProvisionPodServiceGetPodLogsProcedure is the fully-qualified name of the ProvisionPodService's // GetPodLogs RPC. ProvisionPodServiceGetPodLogsProcedure = "/apocryph.proto.v0.provisionPod.ProvisionPodService/GetPodLogs" @@ -58,7 +55,6 @@ var ( provisionPodServiceProvisionPodMethodDescriptor = provisionPodServiceServiceDescriptor.Methods().ByName("ProvisionPod") provisionPodServiceUpdatePodMethodDescriptor = provisionPodServiceServiceDescriptor.Methods().ByName("UpdatePod") provisionPodServiceDeletePodMethodDescriptor = provisionPodServiceServiceDescriptor.Methods().ByName("DeletePod") - provisionPodServiceGetPodInfosMethodDescriptor = provisionPodServiceServiceDescriptor.Methods().ByName("GetPodInfos") provisionPodServiceGetPodLogsMethodDescriptor = provisionPodServiceServiceDescriptor.Methods().ByName("GetPodLogs") ) @@ -68,7 +64,6 @@ type ProvisionPodServiceClient interface { ProvisionPod(context.Context, *connect.Request[proto.ProvisionPodRequest]) (*connect.Response[proto.ProvisionPodResponse], error) UpdatePod(context.Context, *connect.Request[proto.UpdatePodRequest]) (*connect.Response[proto.ProvisionPodResponse], error) DeletePod(context.Context, *connect.Request[proto.DeletePodRequest]) (*connect.Response[proto.DeletePodResponse], error) - GetPodInfos(context.Context, *connect.Request[proto.PodInfoRequest]) (*connect.Response[proto.PodInfoResponse], error) GetPodLogs(context.Context, *connect.Request[proto.PodLogRequest]) (*connect.ServerStreamForClient[proto.PodLogResponse], error) } @@ -101,12 +96,6 @@ func NewProvisionPodServiceClient(httpClient connect.HTTPClient, baseURL string, connect.WithSchema(provisionPodServiceDeletePodMethodDescriptor), connect.WithClientOptions(opts...), ), - getPodInfos: connect.NewClient[proto.PodInfoRequest, proto.PodInfoResponse]( - httpClient, - baseURL+ProvisionPodServiceGetPodInfosProcedure, - connect.WithSchema(provisionPodServiceGetPodInfosMethodDescriptor), - connect.WithClientOptions(opts...), - ), getPodLogs: connect.NewClient[proto.PodLogRequest, proto.PodLogResponse]( httpClient, baseURL+ProvisionPodServiceGetPodLogsProcedure, @@ -121,7 +110,6 @@ type provisionPodServiceClient struct { provisionPod *connect.Client[proto.ProvisionPodRequest, proto.ProvisionPodResponse] updatePod *connect.Client[proto.UpdatePodRequest, proto.ProvisionPodResponse] deletePod *connect.Client[proto.DeletePodRequest, proto.DeletePodResponse] - getPodInfos *connect.Client[proto.PodInfoRequest, proto.PodInfoResponse] getPodLogs *connect.Client[proto.PodLogRequest, proto.PodLogResponse] } @@ -140,11 +128,6 @@ func (c *provisionPodServiceClient) DeletePod(ctx context.Context, req *connect. return c.deletePod.CallUnary(ctx, req) } -// GetPodInfos calls apocryph.proto.v0.provisionPod.ProvisionPodService.GetPodInfos. -func (c *provisionPodServiceClient) GetPodInfos(ctx context.Context, req *connect.Request[proto.PodInfoRequest]) (*connect.Response[proto.PodInfoResponse], error) { - return c.getPodInfos.CallUnary(ctx, req) -} - // GetPodLogs calls apocryph.proto.v0.provisionPod.ProvisionPodService.GetPodLogs. func (c *provisionPodServiceClient) GetPodLogs(ctx context.Context, req *connect.Request[proto.PodLogRequest]) (*connect.ServerStreamForClient[proto.PodLogResponse], error) { return c.getPodLogs.CallServerStream(ctx, req) @@ -156,7 +139,6 @@ type ProvisionPodServiceHandler interface { ProvisionPod(context.Context, *connect.Request[proto.ProvisionPodRequest]) (*connect.Response[proto.ProvisionPodResponse], error) UpdatePod(context.Context, *connect.Request[proto.UpdatePodRequest]) (*connect.Response[proto.ProvisionPodResponse], error) DeletePod(context.Context, *connect.Request[proto.DeletePodRequest]) (*connect.Response[proto.DeletePodResponse], error) - GetPodInfos(context.Context, *connect.Request[proto.PodInfoRequest]) (*connect.Response[proto.PodInfoResponse], error) GetPodLogs(context.Context, *connect.Request[proto.PodLogRequest], *connect.ServerStream[proto.PodLogResponse]) error } @@ -184,12 +166,6 @@ func NewProvisionPodServiceHandler(svc ProvisionPodServiceHandler, opts ...conne connect.WithSchema(provisionPodServiceDeletePodMethodDescriptor), connect.WithHandlerOptions(opts...), ) - provisionPodServiceGetPodInfosHandler := connect.NewUnaryHandler( - ProvisionPodServiceGetPodInfosProcedure, - svc.GetPodInfos, - connect.WithSchema(provisionPodServiceGetPodInfosMethodDescriptor), - connect.WithHandlerOptions(opts...), - ) provisionPodServiceGetPodLogsHandler := connect.NewServerStreamHandler( ProvisionPodServiceGetPodLogsProcedure, svc.GetPodLogs, @@ -204,8 +180,6 @@ func NewProvisionPodServiceHandler(svc ProvisionPodServiceHandler, opts ...conne provisionPodServiceUpdatePodHandler.ServeHTTP(w, r) case ProvisionPodServiceDeletePodProcedure: provisionPodServiceDeletePodHandler.ServeHTTP(w, r) - case ProvisionPodServiceGetPodInfosProcedure: - provisionPodServiceGetPodInfosHandler.ServeHTTP(w, r) case ProvisionPodServiceGetPodLogsProcedure: provisionPodServiceGetPodLogsHandler.ServeHTTP(w, r) default: @@ -229,10 +203,6 @@ func (UnimplementedProvisionPodServiceHandler) DeletePod(context.Context, *conne return nil, connect.NewError(connect.CodeUnimplemented, errors.New("apocryph.proto.v0.provisionPod.ProvisionPodService.DeletePod is not implemented")) } -func (UnimplementedProvisionPodServiceHandler) GetPodInfos(context.Context, *connect.Request[proto.PodInfoRequest]) (*connect.Response[proto.PodInfoResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("apocryph.proto.v0.provisionPod.ProvisionPodService.GetPodInfos is not implemented")) -} - func (UnimplementedProvisionPodServiceHandler) GetPodLogs(context.Context, *connect.Request[proto.PodLogRequest], *connect.ServerStream[proto.PodLogResponse]) error { return connect.NewError(connect.CodeUnimplemented, errors.New("apocryph.proto.v0.provisionPod.ProvisionPodService.GetPodLogs is not implemented")) } diff --git a/pkg/proto/provision-pod.pb.go b/pkg/proto/provision-pod.pb.go index 088dfa0..097dad5 100644 --- a/pkg/proto/provision-pod.pb.go +++ b/pkg/proto/provision-pod.pb.go @@ -579,100 +579,6 @@ func (x *LogEntry) GetLine() string { return "" } -type PodInfoRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` -} - -func (x *PodInfoRequest) Reset() { - *x = PodInfoRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_provision_pod_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PodInfoRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PodInfoRequest) ProtoMessage() {} - -func (x *PodInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_provision_pod_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PodInfoRequest.ProtoReflect.Descriptor instead. -func (*PodInfoRequest) Descriptor() ([]byte, []int) { - return file_provision_pod_proto_rawDescGZIP(), []int{10} -} - -func (x *PodInfoRequest) GetNamespace() string { - if x != nil { - return x.Namespace - } - return "" -} - -type PodInfoResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Info string `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"` -} - -func (x *PodInfoResponse) Reset() { - *x = PodInfoResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_provision_pod_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PodInfoResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PodInfoResponse) ProtoMessage() {} - -func (x *PodInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_provision_pod_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PodInfoResponse.ProtoReflect.Descriptor instead. -func (*PodInfoResponse) Descriptor() ([]byte, []int) { - return file_provision_pod_proto_rawDescGZIP(), []int{11} -} - -func (x *PodInfoResponse) GetInfo() string { - if x != nil { - return x.Info - } - return "" -} - type ProvisionPodResponse_ExposedHostPort struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -686,7 +592,7 @@ type ProvisionPodResponse_ExposedHostPort struct { func (x *ProvisionPodResponse_ExposedHostPort) Reset() { *x = ProvisionPodResponse_ExposedHostPort{} if protoimpl.UnsafeEnabled { - mi := &file_provision_pod_proto_msgTypes[12] + mi := &file_provision_pod_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -699,7 +605,7 @@ func (x *ProvisionPodResponse_ExposedHostPort) String() string { func (*ProvisionPodResponse_ExposedHostPort) ProtoMessage() {} func (x *ProvisionPodResponse_ExposedHostPort) ProtoReflect() protoreflect.Message { - mi := &file_provision_pod_proto_msgTypes[12] + mi := &file_provision_pod_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -818,54 +724,41 @@ var file_provision_pod_proto_rawDesc = []byte{ 0x64, 0x73, 0x55, 0x6e, 0x69, 0x78, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x55, 0x6e, 0x69, 0x78, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x2e, 0x0a, 0x0e, 0x50, - 0x6f, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, - 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x25, 0x0a, 0x0f, 0x50, - 0x6f, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, - 0x66, 0x6f, 0x32, 0xd6, 0x04, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x50, 0x6f, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x79, 0x0a, 0x0c, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x12, 0x33, 0x2e, 0x61, 0x70, 0x6f, - 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x34, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, - 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x73, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, - 0x6f, 0x64, 0x12, 0x30, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x50, 0x6f, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, - 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x09, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x64, 0x12, 0x30, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, - 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, - 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x61, 0x70, 0x6f, 0x63, - 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x50, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x0b, - 0x47, 0x65, 0x74, 0x50, 0x6f, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x2e, 0x2e, 0x61, 0x70, - 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x50, 0x6f, 0x64, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x61, 0x70, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0xe6, 0x03, 0x0a, 0x13, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x79, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x50, 0x6f, 0x64, 0x12, 0x33, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, + 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x73, + 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x64, 0x12, 0x30, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x50, 0x6f, 0x64, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x0a, - 0x47, 0x65, 0x74, 0x50, 0x6f, 0x64, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x2d, 0x2e, 0x61, 0x70, 0x6f, - 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x50, 0x6f, 0x64, 0x4c, - 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x61, 0x70, 0x6f, 0x63, - 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x50, 0x6f, 0x64, 0x4c, 0x6f, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, 0x2c, 0x5a, 0x2a, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6d, 0x72, 0x61, 0x64, - 0x65, 0x2d, 0x63, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x50, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, + 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, + 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x64, + 0x12, 0x30, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, + 0x64, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x50, 0x6f, 0x64, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x64, 0x4c, + 0x6f, 0x67, 0x73, 0x12, 0x2d, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x50, 0x6f, 0x64, 0x2e, 0x50, 0x6f, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x50, 0x6f, 0x64, 0x2e, 0x50, 0x6f, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x30, 0x01, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6d, 0x72, 0x61, 0x64, 0x65, 0x2d, 0x63, 0x6f, 0x6f, 0x70, 0x2f, + 0x61, 0x70, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x68, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -880,7 +773,7 @@ func file_provision_pod_proto_rawDescGZIP() []byte { return file_provision_pod_proto_rawDescData } -var file_provision_pod_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_provision_pod_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_provision_pod_proto_goTypes = []interface{}{ (*ProvisionPodRequest)(nil), // 0: apocryph.proto.v0.provisionPod.ProvisionPodRequest (*DeletePodRequest)(nil), // 1: apocryph.proto.v0.provisionPod.DeletePodRequest @@ -892,30 +785,26 @@ var file_provision_pod_proto_goTypes = []interface{}{ (*PodLogRequest)(nil), // 7: apocryph.proto.v0.provisionPod.PodLogRequest (*PodLogResponse)(nil), // 8: apocryph.proto.v0.provisionPod.PodLogResponse (*LogEntry)(nil), // 9: apocryph.proto.v0.provisionPod.LogEntry - (*PodInfoRequest)(nil), // 10: apocryph.proto.v0.provisionPod.PodInfoRequest - (*PodInfoResponse)(nil), // 11: apocryph.proto.v0.provisionPod.PodInfoResponse - (*ProvisionPodResponse_ExposedHostPort)(nil), // 12: apocryph.proto.v0.provisionPod.ProvisionPodResponse.ExposedHostPort - (*Pod)(nil), // 13: apocryph.proto.v0.pod.Pod + (*ProvisionPodResponse_ExposedHostPort)(nil), // 10: apocryph.proto.v0.provisionPod.ProvisionPodResponse.ExposedHostPort + (*Pod)(nil), // 11: apocryph.proto.v0.pod.Pod } var file_provision_pod_proto_depIdxs = []int32{ - 13, // 0: apocryph.proto.v0.provisionPod.ProvisionPodRequest.pod:type_name -> apocryph.proto.v0.pod.Pod + 11, // 0: apocryph.proto.v0.provisionPod.ProvisionPodRequest.pod:type_name -> apocryph.proto.v0.pod.Pod 5, // 1: apocryph.proto.v0.provisionPod.ProvisionPodRequest.payment:type_name -> apocryph.proto.v0.provisionPod.PaymentChannel - 13, // 2: apocryph.proto.v0.provisionPod.UpdatePodRequest.pod:type_name -> apocryph.proto.v0.pod.Pod + 11, // 2: apocryph.proto.v0.provisionPod.UpdatePodRequest.pod:type_name -> apocryph.proto.v0.pod.Pod 5, // 3: apocryph.proto.v0.provisionPod.UpdatePodRequest.payment:type_name -> apocryph.proto.v0.provisionPod.PaymentChannel - 12, // 4: apocryph.proto.v0.provisionPod.ProvisionPodResponse.addresses:type_name -> apocryph.proto.v0.provisionPod.ProvisionPodResponse.ExposedHostPort + 10, // 4: apocryph.proto.v0.provisionPod.ProvisionPodResponse.addresses:type_name -> apocryph.proto.v0.provisionPod.ProvisionPodResponse.ExposedHostPort 9, // 5: apocryph.proto.v0.provisionPod.PodLogResponse.logEntry:type_name -> apocryph.proto.v0.provisionPod.LogEntry 0, // 6: apocryph.proto.v0.provisionPod.ProvisionPodService.ProvisionPod:input_type -> apocryph.proto.v0.provisionPod.ProvisionPodRequest 3, // 7: apocryph.proto.v0.provisionPod.ProvisionPodService.UpdatePod:input_type -> apocryph.proto.v0.provisionPod.UpdatePodRequest 1, // 8: apocryph.proto.v0.provisionPod.ProvisionPodService.DeletePod:input_type -> apocryph.proto.v0.provisionPod.DeletePodRequest - 10, // 9: apocryph.proto.v0.provisionPod.ProvisionPodService.GetPodInfos:input_type -> apocryph.proto.v0.provisionPod.PodInfoRequest - 7, // 10: apocryph.proto.v0.provisionPod.ProvisionPodService.GetPodLogs:input_type -> apocryph.proto.v0.provisionPod.PodLogRequest - 6, // 11: apocryph.proto.v0.provisionPod.ProvisionPodService.ProvisionPod:output_type -> apocryph.proto.v0.provisionPod.ProvisionPodResponse - 6, // 12: apocryph.proto.v0.provisionPod.ProvisionPodService.UpdatePod:output_type -> apocryph.proto.v0.provisionPod.ProvisionPodResponse - 2, // 13: apocryph.proto.v0.provisionPod.ProvisionPodService.DeletePod:output_type -> apocryph.proto.v0.provisionPod.DeletePodResponse - 11, // 14: apocryph.proto.v0.provisionPod.ProvisionPodService.GetPodInfos:output_type -> apocryph.proto.v0.provisionPod.PodInfoResponse - 8, // 15: apocryph.proto.v0.provisionPod.ProvisionPodService.GetPodLogs:output_type -> apocryph.proto.v0.provisionPod.PodLogResponse - 11, // [11:16] is the sub-list for method output_type - 6, // [6:11] is the sub-list for method input_type + 7, // 9: apocryph.proto.v0.provisionPod.ProvisionPodService.GetPodLogs:input_type -> apocryph.proto.v0.provisionPod.PodLogRequest + 6, // 10: apocryph.proto.v0.provisionPod.ProvisionPodService.ProvisionPod:output_type -> apocryph.proto.v0.provisionPod.ProvisionPodResponse + 6, // 11: apocryph.proto.v0.provisionPod.ProvisionPodService.UpdatePod:output_type -> apocryph.proto.v0.provisionPod.ProvisionPodResponse + 2, // 12: apocryph.proto.v0.provisionPod.ProvisionPodService.DeletePod:output_type -> apocryph.proto.v0.provisionPod.DeletePodResponse + 8, // 13: apocryph.proto.v0.provisionPod.ProvisionPodService.GetPodLogs:output_type -> apocryph.proto.v0.provisionPod.PodLogResponse + 10, // [10:14] is the sub-list for method output_type + 6, // [6:10] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name 6, // [6:6] is the sub-list for extension extendee 0, // [0:6] is the sub-list for field type_name @@ -1049,30 +938,6 @@ func file_provision_pod_proto_init() { } } file_provision_pod_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PodInfoRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_provision_pod_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PodInfoResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_provision_pod_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ProvisionPodResponse_ExposedHostPort); i { case 0: return &v.state @@ -1091,7 +956,7 @@ func file_provision_pod_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_provision_pod_proto_rawDesc, NumEnums: 0, - NumMessages: 13, + NumMessages: 11, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/provider/server.go b/pkg/provider/server.go index 3fddba3..53aec55 100644 --- a/pkg/provider/server.go +++ b/pkg/provider/server.go @@ -17,9 +17,9 @@ import ( "github.com/comrade-coop/apocryph/pkg/loki" pb "github.com/comrade-coop/apocryph/pkg/proto" pbcon "github.com/comrade-coop/apocryph/pkg/proto/protoconnect" - policy "github.com/sigstore/policy-controller/pkg/apis/policy/v1beta1" "github.com/containerd/containerd" "github.com/ipfs/kubo/client/rpc" + policy "github.com/sigstore/policy-controller/pkg/apis/policy/v1beta1" "golang.org/x/exp/slices" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" @@ -47,21 +47,6 @@ func transformError(err error) (*connect.Response[pb.ProvisionPodResponse], erro }), nil } -func (s *provisionPodServer) GetPodInfos(ctx context.Context, request *connect.Request[pb.PodInfoRequest]) (*connect.Response[pb.PodInfoResponse], error) { - log.Printf("Received Request for retreiving info on namespace: %v \n", request.Msg.Namespace) - list := &appsv1.DeploymentList{} - err := s.k8cl.List(ctx, list, &cl.ListOptions{Namespace: request.Msg.Namespace}) - if err != nil { - return nil, err - } - var info strings.Builder - if len(list.Items) > 0 { - info.WriteString(list.Items[0].Annotations[tpk8s.AnnotationVerificationInfo]) - } - response := &pb.PodInfoResponse{Info: info.String()} - return connect.NewResponse(response), nil -} - func (s *provisionPodServer) DeletePod(ctx context.Context, request *connect.Request[pb.DeletePodRequest]) (*connect.Response[pb.DeletePodResponse], error) { log.Println("Received request for pod deletion") @@ -74,6 +59,7 @@ func (s *provisionPodServer) DeletePod(ctx context.Context, request *connect.Req }, } + // TODO: Wait for namespace to be deleted err := s.k8cl.Delete(ctx, ns) if err != nil { log.Printf("Could not delete namespace: %v\n", request) @@ -88,7 +74,7 @@ func (s *provisionPodServer) DeletePod(ctx context.Context, request *connect.Req log.Printf("Could not delete cluster image policies: %v\n", request) return nil, err } - + response := &pb.DeletePodResponse{Success: true} return connect.NewResponse(response), nil } diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go deleted file mode 100644 index d9a96b2..0000000 --- a/pkg/proxy/proxy.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "context" - "fmt" - "log" - "net/http" - "net/url" - "os" - - "connectrpc.com/connect" - "github.com/comrade-coop/apocryph/pkg/constants" - pb "github.com/comrade-coop/apocryph/pkg/proto" - pbcon "github.com/comrade-coop/apocryph/pkg/proto/protoconnect" -) - -// TargetServerURL is the URL of the server to forward the request to -const TargetServerURL = "tpodserver.trustedpods.svc.cluster.local:8080" - -func main() { - http.HandleFunc("/", handler) - log.Printf("Server is listening on port 9999") - log.Fatal(http.ListenAndServe(":9999", nil)) -} - -func handler(w http.ResponseWriter, r *http.Request) { - // Get the environment variable value - namespace := os.Getenv(constants.NamespaceKey) - if namespace == "" { - http.Error(w, "Environment variable not set", http.StatusInternalServerError) - return - } - - r.Header.Set("X-Namespace", namespace) - client := pbcon.NewProvisionPodServiceClient( - http.DefaultClient, - (&url.URL{Scheme: "http", Host: TargetServerURL}).String()) - // Forward the request to the target server - request := pb.PodInfoRequest{Namespace: namespace} - info, err := forwardRequest(client, &request) - if err != nil { - http.Error(w, fmt.Sprintf("Error forwarding request: %v", err), http.StatusInternalServerError) - return - } - - // Write the info string to the response - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(info)) -} - -func forwardRequest(client pbcon.ProvisionPodServiceClient, request *pb.PodInfoRequest) (string, error) { - response, err := client.GetPodInfos(context.Background(), connect.NewRequest(request)) - if err != nil { - return "", err - } - log.Printf("Retrived info: %v\n", response.Msg.Info) - return response.Msg.Info, nil -} diff --git a/pkg/publisher/connect.go b/pkg/publisher/connect.go index bf8387a..ee9b02a 100644 --- a/pkg/publisher/connect.go +++ b/pkg/publisher/connect.go @@ -15,8 +15,8 @@ import ( "github.com/comrade-coop/apocryph/pkg/ipfs" pb "github.com/comrade-coop/apocryph/pkg/proto" pbcon "github.com/comrade-coop/apocryph/pkg/proto/protoconnect" + retryablehttp "github.com/hashicorp/go-retryablehttp" "github.com/libp2p/go-libp2p/core/peer" - retryablehttp "github.com/hashicorp/go-retryablehttp" tpipfs "github.com/comrade-coop/apocryph/pkg/ipfs" ) @@ -47,7 +47,7 @@ func ConnectToProvider(ipfsP2p *ipfs.P2pApi, deployment *pb.Deployment, intercep retryClient := retryablehttp.NewClient() retryClient.RetryMax = 8 - + client := pbcon.NewProvisionPodServiceClient( retryClient.StandardClient(), url.String(), diff --git a/pkg/publisher/deployment.go b/pkg/publisher/deployment.go index e84d92a..e62ae54 100644 --- a/pkg/publisher/deployment.go +++ b/pkg/publisher/deployment.go @@ -93,7 +93,7 @@ func ReadPodAndDeployment(args []string, manifestFormat string, deploymentFormat err = fmt.Errorf("Failed reading deployment file %s: %w", deploymentFile, err) return } - + if deployment.PodManifestFile == "" { var absPodFile string var absDeploymentFile string @@ -118,7 +118,7 @@ func ReadPodAndDeployment(args []string, manifestFormat string, deploymentFormat } else { err = pb.UnmarshalStdin(manifestFormat, pod) } - + if err != nil { err = fmt.Errorf("Failed reading manifest file %s: %w", podFile, err) } diff --git a/pkg/publisher/upload.go b/pkg/publisher/upload.go index 666ef2a..650f39f 100644 --- a/pkg/publisher/upload.go +++ b/pkg/publisher/upload.go @@ -143,8 +143,8 @@ func LinkUploadsFromDeployment(pod *pb.Pod, deployment *pb.Deployment) *pb.Pod { Key: uploadedImage.Key, VerificationDetails: &pb.VerificationDetails{ Signature: uploadedImage.VerificationDetails.Signature, - Identity: uploadedImage.VerificationDetails.Identity, - Issuer: uploadedImage.VerificationDetails.Issuer, + Identity: uploadedImage.VerificationDetails.Identity, + Issuer: uploadedImage.VerificationDetails.Issuer, }, } } diff --git a/proto/provision-pod.proto b/proto/provision-pod.proto index c617cae..ce7ef75 100644 --- a/proto/provision-pod.proto +++ b/proto/provision-pod.proto @@ -11,7 +11,6 @@ service ProvisionPodService { rpc ProvisionPod(ProvisionPodRequest) returns (ProvisionPodResponse); rpc UpdatePod(UpdatePodRequest) returns (ProvisionPodResponse); rpc DeletePod(DeletePodRequest) returns (DeletePodResponse); - rpc GetPodInfos(PodInfoRequest) returns (PodInfoResponse); rpc GetPodLogs(PodLogRequest) returns (stream PodLogResponse); } @@ -68,11 +67,3 @@ message LogEntry { uint64 NanosecondsUnixEpoch = 1; string line = 2; } - -message PodInfoRequest { - string namespace = 1; -} - -message PodInfoResponse { - string info = 1; -} diff --git a/spec/ATTESTATION.md b/spec/ATTESTATION.md new file mode 100644 index 0000000..ff38340 --- /dev/null +++ b/spec/ATTESTATION.md @@ -0,0 +1,65 @@ +# Attestation + +Within Apocryph, all applications running on one providers run inside the same TEE enclave, sandboxed from each other through the use of gVisor. This allows us to use Constellation for all TEE needs for now, while still leaving the door open to switch to per-application enclaves later on. + +In order to be able to attest the individual pods deployed through Apocryph, we maintain a facade which (1) attests the whole TEE and (2) forwards the requests to the correct application, as referenced by a full container image hash. That way, end users get what they need: an attestation that their request will be processed by the exact code they expect. + +Architecturally, this looks like the following: + +```mermaid +flowchart LR + subgraph TEE[TEE enclave] + subgraph Apo[Apocryph stack] + %%Apocryph + Proxy + end + subgraph App + Application + end + end + %%Publisher-.-|Deploy|Apocryph + %%Apocryph-.-|Create|Application + User---|Attest|Proxy + User---|HTTP request|Proxy + Proxy---|Forward request|Application +``` + +## Attesting an application running inside Apocryph + +To attest an application in Apocryph, a prospective client needs to do a couple of steps, as outlined in the following diagram: + +```mermaid +sequenceDiagram + autonumber + User->>+Apocryph Node: GET https:///.well-known
/network.apocryph.attest?nonce=... + + Apocryph Node->>-User: {"attestation": {TEE Attestation...},
"header": , ...} + User->>User: Check attestation and certificate + + User->>+Apocryph Node: GET https:///path
X-Apocryph-Expected: + + Apocryph Node->>Apocryph Node: Check version + Apocryph Node->>+Application: GET ... + Application->>-Apocryph Node: + Apocryph Node->>-User: + box TEE + participant Apocryph Node + participant Application + end +``` + +The first step (1) is connecting to the Apocryph node over HTTPS, and requesting the attestation for the given application. This is done by issuing a GET request to the special path, `/.well-known/network.apocryph.attest`, which the proxy will intercept and serve the attestation for the whole TEE on. + +In return (2), the Proxy replies with an attestation value, along with information about the current version of the application running inside the TEE. + +The client then has the non-trivial task of verifying that attestation value (3). In particular, the user has to: +* Confirm that the attestation comes from a genuine TEE, by verifying the signatures in the attestation data against Intel's or AMD's certificate +* Confirm that the hashes in the attestation match the well-known hashes of an Apocryph enclave. +* Confirm that the attestation contains a certificate hash, and that it is the same as the certificate hash used in the current HTTPS connection. +* Confirm that the application details, i.e. OCI image hashes, in the returned attestation match the expected app version details. (Application detils can optionally include sigstore signatures, in which case, checking the signature and the key could replace checking an image hash.) + +All of that can be automated through either a browser plugin or a lightweight library, which incorporates the above steps. + +Finally (4), the client is ready to issue their actual request. At that stage, all they need to do is reuse the TLS connection (or, if establishing a new one, ensure that the new one is using the same attested-to TLS certificate) to send a subsequent HTTPS request, this time to the actual application-specific path they want to access. To ensure that they are connecting to the same application version that was previously attested, the user includes an application version in the `X-Apocryph-Expected` header. + +The Proxy running inside the Apocryph node would then forward the request to the currently-running application instance with that specific version (5,6), and proxy the result back, unmodified (7,8). diff --git a/test/e2e/common/manifests/manifest-nginx.yaml b/test/e2e/common/manifests/manifest-nginx.yaml index f54524e..b41a300 100644 --- a/test/e2e/common/manifests/manifest-nginx.yaml +++ b/test/e2e/common/manifests/manifest-nginx.yaml @@ -1,7 +1,7 @@ containers: - name: test-container image: - url: docker.io/nginxdemos/nginx-hello:latest + url: docker.io/nginxdemos/nginx-hello@sha256:f8a31a47683f7c27497063e6061168290b211f421dfe2bd2b230fac0f3557618 ports: - containerPort: '8080' hostHttpHost: example.localhost @@ -14,3 +14,5 @@ containers: replicas: min: 0 max: 1 +verificationSettings: + publicVerifiability: true