All Products
Search
Document Center

Container Service for Kubernetes:Use TEE SDK to develop and build Intel SGX 2.0 applications

Last Updated:Apr 26, 2024

This topic describes how to use trusted execution environment (TEE) SDK to develop, build, and deploy Intel Software Guard Extensions (SGX) 2.0 applications. In this topic, an application named helloworld is used as an example. This application generates messages in an enclave on a regular basis and sends the messages to an untrusted buffer. Then, the messages are sent to the terminal.

Prerequisites

How Intel SGX works

image

An Intel SGX 2.0 application consists of two components: an untrusted component and a trusted component.

  • Untrusted component: The untrusted component is an unencrypted part of the memory. If you store the code and data of an application in this part, the main() entry function of the application must also be placed in the untrusted component. In the preceding figure, the main() and bar() functions are placed in the untrusted component.

  • Trusted component: The trusted component or enclave is an encrypted part of the memory. This component is created by the CPU, and the data and code in this component can be accessed only by the CPU. In the preceding figure, the helloworld() and foo() functions are placed in the enclave.

To call a function in the enclave from the untrusted component, the application must perform an enclave call (ECALL). To call a function in the untrusted component from the enclave, the application must perform an outside call (OCALL). ECALLs and OCALLs are declared in Enclave Definition Language (EDL) files.

Sample code and directory tree

In this example, an Intel SGX 2.0 application named helloworld is deployed. For more information about the source code, visit GitHub. The source code includes the code for application compilation, image building, and application deployment. The following sample code shows the directory tree.

App.hsgx-device-plugin/samples/hello_world/
                                    ├── Dockerfile
                                    ├── Makefile
                                    ├── README.md
                                    └── src
                                         ├── App
                                         │ ├── App.cpp
                                         │ └── App.h
                                         ├── Enclave
                                         │ ├── Enclave.config.xml
                                         │ ├── Enclave.cpp
                                         │ ├── Enclave.edl
                                         │ ├── Enclave.h
                                         │ ├── Enclave.lds
                                         │ └── Enclave_private.pem
                                         └── Makefile

The following table describes the src directory and related files.

Directory

Description

File

Description

App

The App directory contains untrusted code, such as the main() function (the entry function) and code of OCALL functions.

App.cpp

The code in the untrusted component.

App.h

The header file.

Enclave

The code in the trusted component, including the code of the ECALL function.

Enclave.edl

The EDL file.

Enclave.lds

The enclave linker script.

Enclave_private.pem

The private key that is used to sign the enclave.so file.

Enclave.config.xml

The enclave configuration file that specifies parameters, such as the stack size and whether to enable debugging.

Enclave.h and Enclave.cpp.

The code that implements the trusted component.

Step 1: Compile hello_world

  1. Run the following command to install Git:

    sudo yum install git
  2. Run the following command to compile the hello_world application:

    git clone https://github.com/AliyunContainerService/sgx-device-plugin
    cd sgx-device-plugin/samples/hello_world
    SGX_SDK=/opt/alibaba/teesdk/intel/sgxsdk make build

    A binary file named hello_world is generated in the root directory of the project.

    View the content of the binary file

    GEN  =>  App/Enclave_u.h
    CC   <=  App/Enclave_u.c
    CXX  <=  App/App.cpp
    LINK =>  app
    GEN  =>  Enclave/Enclave_t.h
    CC   <=  Enclave/Enclave_t.c
    CXX  <=  Enclave/Enclave.cpp
    LINK =>  enclave.so
    <EnclaveConfiguration>
        <ProdID>0</ProdID>
        <ISVSVN>0</ISVSVN>
        <StackMaxSize>0x40000</StackMaxSize>
        <HeapMaxSize>0x100000</HeapMaxSize>
        <TCSNum>10</TCSNum>
        <TCSPolicy>1</TCSPolicy>
        <!-- Recommend changing 'DisableDebug' to 1 to make the enclave undebuggable for enclave release -->
        <DisableDebug>0</DisableDebug>
        <MiscSelect>0</MiscSelect>
        <MiscMask>0xFFFFFFFF</MiscMask>
    </EnclaveConfiguration>
    tcs_num 10, tcs_max_num 10, tcs_min_pool 1
    The required memory is 3960832B.
    The required memory is 0x3c7000, 3868 KB.
    Succeed.
    SIGN =>  enclave.signed.so
    The project has been built in debug hardware mode

  3. Run the ./hello_world command on an instance that supports SGX to run the hello_world application.

    cd src/
    ./hello_world

    Expected output:

    Wed May  6 06:53:33 2020
    Hello world From SGX Enclave!
    Wed May  6 06:53:34 2020
    Hello world From SGX Enclave!
    ...

The following content describes how to compile the application and shows the directory tree of the compiled code:

View the compiling procedure

  1. Use the sgx_edger8r tool and the sgx_ecall ECALL function to generate untrusted code (Enclave_u.c and Enclave_u.h) in the App directory.

  2. Compile untrusted binary files in the App directory.

  3. Use the sgx_edger8r tool to generate trusted code (Enclave_t.c and Enclave_t.h) in the Enclave directory.

  4. Compile the enclave.so file. It is a trusted dynamic-link library.

  5. Use the sgx_sign tool to sign the trusted dynamic-link library. The name of the trusted dynamic-link library changes to enclave.signed.so.

  6. The application is compiled.

View the directory tree of the complied code

sgx-device-plugin/samples/hello_world/src/
                                         ├── hello_world      #[generated]
                                         ├── App
                                         │ ├── App.cpp
                                         │ ├── App.h
                                         │ ├── App.o        #[generated]
                                         │ ├── Enclave_u.c  #[generated] 
                                         │ ├── Enclave_u.h  #[generated] 
                                         │ └── Enclave_u.o  #[generated]
                                         ├── Enclave
                                         │ ├── Enclave.config.xml
                                         │ ├── Enclave.cpp
                                         │ ├── Enclave.edl
                                         │ ├── Enclave.h
                                         │ ├── Enclave.lds
                                         │ ├── Enclave.o     #[generated]
                                         │ ├── Enclave_private.pem
                                         │ ├── Enclave_t.c   #[generated]
                                         │ ├── Enclave_t.h   #[generated]
                                         │ └── Enclave_t.o   #[generated]
                                         ├── enclave.signed.so #[generated]
                                         ├── enclave.so        #[generated]
                                         └── Makefile

File path

Description

Sample code

Encalve/Enclave.edl

The EDL file that declares a public ECALL function. At least one of the following functions must be declared in the EDL file of an SGX application:

  • An ECALL function of the type.

  • trusted {...} declares an ECALL function.

  • untrusted {...} declares an OCALL function.

In this example, the application does not need to invoke an OCALL function. Therefore, only one ECALL function (ecall_hello_from_enclave) is declared. This ECALL function is used to create a buffer in the enclave and deploy the helloworld application. Then, information in the buffer is copied to an untrusted buffer in the untrusted component. The application invokes printf in the untrusted component to print the information.

enclave {
    trusted {
        public void ecall_hello_from_enclave([out, size=len] char* buf, size_t len);
    };
};

Enclave/Enclave.lds

N/A

enclave.so
{
    global:
        g_global_data_sim;
        g_global_data;
        enclave_entry;
        g_peak_heap_used;
    local:
        *;
};

Enclave/Enclave.config.xml

N/A

<EnclaveConfiguration>
  <ProdID>0</ProdID>
  <ISVSVN>0</ISVSVN>
  <StackMaxSize>0x40000</StackMaxSize>
  <HeapMaxSize>0x100000</HeapMaxSize>
  <TCSNum>10</TCSNum>
  <TCSPolicy>1</TCSPolicy>
  <!-- Recommend changing 'DisableDebug' to 1 to make the enclave undebuggable for enclave release -->
  <DisableDebug>0</DisableDebug>
  <MiscSelect>0</MiscSelect>
  <MiscMask>0xFFFFFFFF</MiscMask>
</EnclaveConfiguration>

Enclave/Enclave.h

In most cases, this header file is empty.

#ifndef _ENCLAVE_H_
#define _ENCLAVE_H_
#endif

Enclave/Enclave.cpp

N/A

#include "Enclave.h"
#include "Enclave_t.h" /* print_string */
#include <string.h>

void ecall_hello_from_enclave(char *buf, size_t len)
{
    const char *hello = "Hello world";

    size_t size = len;
    if(strlen(hello) < len)
    {
        size = strlen(hello) + 1;
    }

    memcpy(buf, hello, size - 1);
    buf[size-1] = '\0';
}

Enclave/Enclave_private.pem

The private key that is used to sign the enclave.so file

openssl genrsa -out Enclave/Enclave_private.pem -3 3072

App/App.h

N/A

#ifndef _APP_H_
#define _APP_H_

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include "sgx_error.h"       /* sgx_status_t */
#include "sgx_eid.h"     /* sgx_enclave_id_t */

#ifndef TRUE
# define TRUE 1
#endif

#ifndef FALSE
# define FALSE 0
#endif

# define TOKEN_FILENAME   "enclave.token"
# define ENCLAVE_FILENAME "enclave.signed.so"

extern sgx_enclave_id_t global_eid;    /* global enclave id */

#if defined(__cplusplus)
extern "C" {
#endif


#if defined(__cplusplus)
}
#endif

#endif /* !_APP_H_ */

App/App.cpp

N/A

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include <time.h>
#include <ctime>

# include <unistd.h>
# include <pwd.h>
# define MAX_PATH FILENAME_MAX

#include "sgx_urts.h"
#include "App.h"
#include "Enclave_u.h"

/* Global EID shared by multiple threads */
sgx_enclave_id_t global_eid = 0;

int initialize_enclave(void)
{
    sgx_status_t ret = SGX_ERROR_UNEXPECTED;

    char enclavefile[256];
    getcwd(enclavefile, sizeof(enclavefile));
    strcat(enclavefile, "/enclave.signed.so");

    /* Call sgx_create_enclave to initialize an enclave instance */
    /* Debug Support: set 2nd parameter to 1 */
    ret = sgx_create_enclave(enclavefile, SGX_DEBUG_FLAG, NULL, NULL, &global_eid, NULL);
    if (ret != SGX_SUCCESS) {
        printf("Failed to create enclave, ret code: %d, enclave file: %s\n", ret, enclavefile);
        return -1;
    }

    return 0;
}

tm* get_time() {
    time_t rawtime;
    struct tm * timeinfo;
    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
    return timeinfo;
}

/* Application entry */
int SGX_CDECL main(int argc, char *argv[])
{
    (void)(argc);
    (void)(argv);

    const size_t max_buf_len = 100;
    char buffer[max_buf_len] = {0};


    /* Initialize the enclave */
    if(initialize_enclave() < 0){
        printf("Enter a character before exit ...\n");
        getchar();
        return -1;
    }

    /* Enclave calls */
    while(1) {
        ecall_hello_from_enclave(global_eid, buffer, max_buf_len);

        printf("%s%s\n", asctime(get_time()), buffer);
        fflush(stdout);

        sleep(1);
    }

    /* Destroy the enclave */
    sgx_destroy_enclave(global_eid);

    printf("Info: SampleEnclave successfully returned.\n");

    printf("Enter a character before exit ...\n");
    getchar();
    return 0;
}

Step 2: Build and deploy the helloworld application

We recommend that you build an Alibaba Cloud Linux image, install the latest SGX SDK version, and periodically update the SDK to mitigate potential risks.

Sample Dockerfile

The following sample Dockerfile uses Alibaba Cloud Linux 3:

FROM registry.cn-hangzhou.aliyuncs.com/alinux/alinux3

ARG REGION_ID=cn-hangzhou

RUN yum install -y curl && \
	repo_url=https://enclave-${REGION_ID}.oss-${REGION_ID}.aliyuncs.com/repo/alinux/enclave-expr.repo && \
	yum install -y yum-utils && \
	yum-config-manager --add-repo ${repo_url} && \
	yum install -y libsgx-urts libsgx-uae-service # Add more SGX runtime dependencies on demand.

WORKDIR /src
COPY src/hello_world src/enclave.signed.so /src
ENTRYPOINT ["/src/hello_world"]

Procedure

  1. Install Docker.

    For more information, see Install and use Docker on a Linux instance.

  2. Run the following command to compile and build a test image.

    Replace ${IMAGE_URL} with the address of the test image.

    cd sgx-device-plugin/samples/hello_world
    TARGET_IMAGE=${IMAGE_URL} SGX_SDK=/opt/alibaba/teesdk/intel/sgxsdk make image
    docker push ${IMAGE_URL}
  3. Run the following command to deploy the helloworld application.

    Replace ${IMAGE_URL} with the address of the test image that you built in the preceding step.

    cat <<EOF | kubectl create -f -
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: helloworld
      namespace: default
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: helloworld
      template:
        metadata:
          labels:
            app: helloworld
        spec:
          containers:
          - image: ${IMAGE_URL}
            imagePullPolicy: Always
            name: helloworld
            resources:
              limits:
                cpu: 250m
                memory: 512Mi
                alibabacloud.com/sgx_epc_MiB: 2
    EOF

References