Skip to content

SCONE Online Boutique example

This demo showcases how the sconify-image tool can be used to turn existing microservices into confidential containers, with encrypted filesystems and remote attestation. For that purpose, we convert the Google Online Boutique demo, a well-known microservices application composed by 10 services in different languages, to run on SCONE.

Demo

Requirements

This demo is executed in two stages:

Stage 1: One-step image transformation (sconify)

On a trusted machine, sconify-image will transform all standard images into confidential images that run on top of SCONE, create security policies for remote attestation and generate Kubernetes manifests or Helm charts. Check the sconify documentation to learn more about this process.

Requirements:

  • Docker.
  • sconify-image.

Stage 2: Deployment

Using the generated deployment resources from the previous step, deploy the confidential services to a Kubernetes cluster with Intel SGX support. You don't need to trust or control the cluster nodes or the deployment process.

Requirements:

  • Kubernetes cluster with Intel SGX support.
  • SCONE LAS running on the cluster. Learn more.

TL;DR

git clone https://github.com/scontain/microservices-demo -b sconify
cd microservices-demo

# Specify the registry to which the confidential images will be pushed.
# You must be logged in and have push permission.
export IMAGE_REPOSITORY="docker.io/myrepo/online-boutique"
# Configure Kubernetes namespace (required here so that the in-cluster
# DNS names of the microservices are part of the security policies).
export K8S_NAMESPACE="default"
./sconify-all.sh

# Configure an imagePullSecret for Kubernetes, if necessary.
export K8S_IMAGE_PULL_SECRET="sconeapps"
# Configure which SGX device plugin you want to use ("scone", "azure" or "disabled")
export K8S_DEVICE_PLUGIN="azure"
./deploy-all.sh

One-step image transformation

SCONE is a lift-and-shift framework for confidential containers. With that in mind, we support a one-step transformation process for standard containers (i.e., not confidential) into confidential containers. We call the process sconify (learn more). This means that existing code and workflows (e.g., CI/CD pipelines) can benefit from SCONE guarantees with little-to-no effort.

Go services support

SCONE requires Go services to be built with gccgo (learn more). For this reason, we use alternative Dockerfiles to build the standard services (gcc.Dockerfile). Note that the original source code is untouched.

C# services support

SCONE support for C#. For this reason, sconify-image does not support C# yet, and we provide a pre-built image for the C# service of the demo, cartservice, with SCONE. Check src/cartservice/sconify.sh and policies/cartservice.yaml for more information.

Compared to the original repository, the following directories are modified. Note that no source code changes are performed.

policies/       # Code to create a CAS namespace and creating policies for services that are not sconified (redis, cartservice)
release/charts/ # Helm charts for services that not sconified (redis, cartservice). Generated Helm charts are saved here too
src/            # Original source code, sconify.sh scripts and alternative Dockerfiles (if necessary)

In this example, we have an online boutique composed of the following services (the description was taken from the original repository):

Service Language Description
frontend Go Exposes an HTTP server to serve the website. Does not require signup/login and generates session IDs for all users automatically.
cartservice C# Stores the items in the user's shopping cart in Redis and retrieves it.
productcatalogservice Go Provides the list of products from a JSON file and ability to search products and get individual products.
currencyservice Node.js Converts one money amount to another currency. Uses real values fetched from European Central Bank. It's the highest QPS service.
paymentservice Node.js Charges the given credit card info (mock) with the given amount and returns a transaction ID.
shippingservice Go Gives shipping cost estimates based on the shopping cart. Ships items to the given address (mock)
emailservice Python Sends users an order confirmation email (mock).
checkoutservice Go Retrieves user cart, prepares order and orchestrates the payment, shipping and the email notification.
recommendationservice Python Recommends other products based on what's given in the cart.
adservice Java Provides text ads based on given context words.

We perform the one-step transformation of all the services above. Each service has its own sconify.sh script with the transformation parameters (e.g., src/emailservice/sconify.sh). sconify-image ships with sensible defaults so it's easy to get started, but it also supports a fair amount of configuration parameters, should you require more customization or additional integrations (e.g., integration to Azure services).

The flow is as follows:

  1. Build standard image (using the original source code).
  2. Transform it into a confidential image. Push the generated image to a container registry.
  3. Generate a security policy. Push it to a SCONE CAS.
  4. Generate Helm charts (or Kubernetes manifests).

You can trigger the sconification of a single service by running their own sconify.sh. To transform all services at once, run sconify-all.sh, which simply loops through all services and calls their sconify.sh script.

# Specify the registry to which the confidential images will be pushed.
# You must be logged in and have push permission.
export IMAGE_REPOSITORY="docker.io/myrepo/online-boutique"
# Configure Kubernetes namespace (required here so that the in-cluster
# DNS names of the microservices are part of the security policies).
export K8S_NAMESPACE="default"
./sconify-all.sh

This will transform all images, and now the generated Helm charts will be in release/charts.

sconify-all.sh supports the following environment variables:

Environment variable Description Default value
IMAGE_REPOSITORY Container image repository for the generated confidential images
SCONE_CAS_ADDR Address of SCONE CAS that will receive the generated security policies "5-4-0.scone-cas.cf"
CAS_MRENCLAVE The MrEnclave of the chosen SCONE CAS (for attestation and trust establishment) "9f82c11af7ed3c3212483a5ad33b59923ca1462d1814943ff2586932c21fa51b"
K8S_NAMESPACE The Kubernetes namespace for deploying the services, since the microservices talk to each other using their in-cluster DNS names "default"

This example also uses redis as the storage backend for cartservice. There is no need to sconify redis, as we already provide it as one of our curated images.

The following service is part of the demo but does not run on SCONE (as it is just a benchmark):

Service Language Description
loadgenerator Python/Locust Continuously sends requests imitating realistic user shopping flows to the frontend.

Deployment

After the Helm charts are generated, one can deploy them to a Kubernetes cluster. Similar to sconify-all.sh, we provide a deploy-all.sh script, which loops through all Helm charts in release/charts and install them. The following environment variables are accepted by deploy-all.sh:

Environment variable Description Default value
K8S_IMAGE_PULL_SECRET You need a Secret to access private container registries. Learn more "sconeapps"
K8S_DEVICE_PLUGIN Configure which device plugin to use: "scone" for SCONE SGX device plugin, "azure" for the Azure SGX device plugin or "disabled" to access the SGX devices via hostPath volumes "scone"
K8S_FRONTEND_SERVICE_TYPE Configure Service type of "frontend", i.e., how to expose it (LoadBalancer or NodePort) "NodePort"

To deploy the services to an AKS cluster, for example, run:

# Configure an imagePullSecret for Kubernetes, if necessary.
export K8S_IMAGE_PULL_SECRET="sconeapps"
# Configure which SGX device plugin you want to use ("scone", "azure" or "disabled")
export K8S_DEVICE_PLUGIN="azure"
# Configure Service type of "frontend", i.e., how to expose it (LoadBalancer or NodePort)
export K8S_FRONTEND_SERVICE_TYPE="LoadBalancer"
./deploy-all.sh

After all charts are deployed, you can see their pods running:

$ kubectl get pods
NAME                                                              READY   STATUS                       RESTARTS   AGE
adservice-sconify-adservice-5c5d79cd5-hmpmv                       1/1     Running                      0          7m44s
cartservice-sconify-cartservice-557767665f-79ltl                  1/1     Running                      0          7m39s
checkoutservice-sconify-checkoutservice-69cbcc9cf6-rsbpv          1/1     Running                      0          7m33s
currencyservice-sconify-currencyservice-d7cb458db-vtwqq           1/1     Running                      0          7m28s
emailservice-sconify-emailservice-84fb77d787-6gbhh                1/1     Running                      0          7m22s
frontend-sconify-frontend-77fdf9d665-mb2wh                        1/1     Running                      0          6m54s
las-49fzx                                                         1/1     Running                      0          8h
las-gjgr8                                                         1/1     Running                      0          8h
paymentservice-sconify-paymentservice-79cd585dc9-nzqk5            1/1     Running                      0          7m17s
productcatalogservice-sconify-productcatalogservice-75bfc9pxbtg   1/1     Running                      0          7m11s
recommendationservice-sconify-recommendationservice-5c9bcbpghkm   1/1     Running                      0          7m5s
redis-cart-69b6f44897-nn45s                                       1/1     Running                      0          7m50s
shippingservice-sconify-shippingservice-68d544fcdc-v4h5g          1/1     Running                      0          7m

You can now access the online boutique by fetching the frontend service address. If you set K8S_FRONTEND_SERVICE_TYPE to LoadBalancer, you should see the EXTERNAL-IP field populated for the frontendservice.