This is a simple tutorial on how to convert a very basic cloud-native application into a confidential cloud-native application. This example runs on different CPUs:
- Intel SGX,
- Intel TDX, and
- AMD SEV SNP
and on different cloud providers:
- Microsoft Azure,
- Google Cloud,
- AWS, and
- Telekom OSC.
We use a simple Java program to demonstrate the steps. We include slide decks to explain the different steps and screencasts to demonstrate the execution of the different steps. To run the code yourself, you need to get access to the production container images on registry.scontain.com.
The Java program:
- Prints CLI arguments.
- Prints environment variables.
- Prints the content of /config/configs.yaml and /config/secrets.
- Deploy the Java application.
- Mount a ConfigMap to /config/configs.yaml.
- Mount a Secret to /config/secrets.
For more details, see the following slide deck:
Make sure you have installed all necessary software on your development machine and your Kubernetes cluster by clonging repository https://github.com/scontain/scone.git and following the README.md. Basically, execute in repo scone
./scripts/prerequisite_check.shto install all necessary commands,./scripts/reconcile_scone_operator.shcheck that your Kubernetes cluster is properly configured, and./scripts/install_cas.shto ensure that CAS instancecasexists in Kubernetes namespacedefault.
The following scrips that are found in the scripts directory:
build.sh: build the container image and the manifests. Runbuild.sh --helpto learn how to customize this build step.deploy.sh: deploys the manifests in the default namespace.show_logs.sh: show the log of the podcleanup.sh: removes all resources
For more details, see the following slide deck:
Run the following script to build the container image and generate the Kubernetes manifests:
# example: ./scripts/build.sh --repo registry.scontain.com/workshop/java-cli-env-reader
./scripts/build.sh --repo your-registry-url/your-repo/java-cli-env-readerYou can customize the build process by using command-line options. To see all available options:
./scripts/build.sh --helpExample: By default, we use image tag
latest. You can set the tag using option--tag <tag>
To produce smaller images, you can create the application an Alpine instead:
./scripts/build.sh --alpineNote that CVMs on some cloud providers are sensitive to pulling large images from remote registries. Hence, using small images helps to reduce the startup times and avoid timeouts because of long image pull times.
Screencast of building the native application:
You can deploy the native application with the help of script deploy.sh:
In a first approximation,
- we cannot trust any person that has access to the computing infrastructure, and
- we cannot trust hardware and software components unless we securely measured the components and the measured values match our expected values.
For more details, see the following slide deck:
We transform previously built Docker image and generated Kubernetes manifests using the SCONE TD Build Service.
For more details, see the following slide deck:
When you run the CAS in the same cluster, you can just execute
# Use --help to explore additional options and usage details
./scripts/td-build.shIn case there are any vulnerabilities in the cluster, attestation will fail. Please add flag --permissive to build the application again.
# Use --help to explore additional options and usage details
./scripts/td-build.sh --permissiveScreencast of transforming the native application into a confidential application:
Deploy the confidential application by executing
kubectl apply -f generated/manifest.sanitized.yamlScreencast of deploying the confidential application:
We talk about a split deployment when you run the application in one Kubernetes cluster and the SCONE CAS in a different cluster. The cluster could even run in different clouds.
To use CAS in a different cluster, we assume that you have two Kubernetes contexts:
- context
cvmruns the CVMs, and - context
casruns the SCONE CAS.
We check that these two clusters exists as follows:
if kubectl config get-contexts -o name | grep -q '^cas$'; then
echo "Context 'cas' exists"
else
echo "ERROR: Context 'cas' not exist"
exit 1
fi
if kubectl config get-contexts -o name | grep -q '^cvm$'; then
echo "Context 'cvm' exists"
else
echo "ERROR: Context 'cvm' not exist"
exit 1
fiYou can merge two configs as follows:
CVM_KONTEXT=$(realpath ~/.kube/cvm-context)
CAS_KONTEXT=$(realpath ~/.kube/cas-context)
mv ~/.kube/config-cvm-cas ~/.kube/config-cvm-cas.bak || true
KUBECONFIG="$CVM_KONTEXT" C_CVM_KONTEXT=$(kubectl config current-context)
KUBECONFIG="$CAS_KONTEXT" C_CAS_KONTEXT=$(kubectl config current-context)
echo "C_CVM_KONTEXT=$C_CVM_KONTEXT C_CAS_KONTEXT=$C_CAS_KONTEXT"
KUBECONFIG="$CVM_KONTEXT:$CAS_KONTEXT" kubectl config view --flatten > ~/.kube/config-cvm-cas
export KUBECONFIG=$(realpath ~/.kube/config-cvm-cas)
kubectl config rename-context $C_CVM_KONTEXT cvm
kubectl config rename-context $C_CAS_KONTEXT cas
kubectl config viewkubectl config set-context cas
# Use --help to explore additional options and usage details
./scripts/td-build.sh --cas-addr cas-ext.default --cluster-addr cas-ext.sconecloud.de --kbs-addr kbs.sconecloud.de --cvm --split --permissiveNote: Before running this script, make sure your
kubeconfigis configured to point to the target SGX-enabled Kubernetes cluster. Also, make sure to set the--cluster-addraccording to your environment, using the correctname.namespaceformat that matches your CAS deployment.
Apply the generated encrypted policies (in the SGX-enabled cluster):
kubectl --context=cas apply -f generated/manifest.sanitized.enc.yamlThen deploy the workload using:
kubectl --context=cvm apply -f generated/manifest.sanitized.yaml
watch kubectl --context=cvm get podsNote: If your container images are stored in a private registry, you must create a Kubernetes secret to allow image pulling:
# Replace <REGISTRY>, <USER> and <TOKEN> with your actual registry credentials
kubectl create secret docker-registry sconeapps \
--docker-server=<REGISTRY> \
--docker-username=<USER> \
--docker-password=<TOKEN>Streams logs from the pod created by the Kubernetes Job: java-cli-env-reader.
./scripts/show-logs.shNote: If you have used the
--namespaceflag atbuild.sh, use same namespace here.
To stop the application, you can delete its deployment:
kubectl delete deployment java-cli-env-readerTo delete all Kubernetes resources created from generated manifests and the local files, execute:
# Use --help to explore additional options and usage details
./scripts/cleanup.shWe can decode the Kubernetes secret with the help of script decode_secret.sh.
One can monitor the memory consumption inside of an enclave by setting within the enclave environment
- { name: SCONE_METRICS, value: "prometheus_port:9999" }This will activate a metric port for Prometheus to collect memory metrics of the enclave.
For Prometheus to be able to collect this metric, we need to make this metric port available as an Kubernetes endpoint. We can achieve this by executing
kubectl apply -f monitoring/metrics_service.yamlNext, we need to ensure that Prometheus discovers this endpoint. We create a ServiceMonitor for our application:
kubectl apply -f monitoring/metrics_service.yaml



