diff --git a/demos/grpc/grpc_tls/Makefile.patch b/demos/grpc/grpc_tls/Makefile.patch new file mode 100644 index 00000000..e482ff3c --- /dev/null +++ b/demos/grpc/grpc_tls/Makefile.patch @@ -0,0 +1,64 @@ +--- Makefile-orig 2022-01-04 08:37:31.975521982 +0000 ++++ Makefile 2022-01-04 08:37:58.709192999 +0000 +@@ -16,29 +16,32 @@ + + HOST_SYSTEM = $(shell uname | cut -f 1 -d_) + SYSTEM ?= $(HOST_SYSTEM) +-CXX = g++ +-CPPFLAGS += `pkg-config --cflags protobuf grpc` ++CXX = occlum-g++ ++CPPFLAGS += -I/usr/local/occlum/x86_64-linux-musl/include + CXXFLAGS += -std=c++11 + ifeq ($(SYSTEM),Darwin) + LDFLAGS += -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc`\ + -pthread\ + -lgrpc++_reflection\ +- -ldl ++ -ldl \ ++ -lssl -lcrypto + else +-LDFLAGS += -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc`\ ++LDFLAGS += -L/usr/local/occlum/x86_64-linux-musl/lib -lgrpc++ -lgrpc -lprotobuf -lgpr \ ++ -lcares -lz -laddress_sorting -pie\ + -pthread\ + -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed\ +- -ldl ++ -ldl \ ++ -lssl -lcrypto + endif + PROTOC = protoc + GRPC_CPP_PLUGIN = grpc_cpp_plugin + GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)` + +-PROTOS_PATH = ../../protos ++PROTOS_PATH = . + + vpath %.proto $(PROTOS_PATH) + +-all: system-check greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server ++all: system-check greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server greeter_secure_client greeter_secure_server + + greeter_client: helloworld.pb.o helloworld.grpc.pb.o greeter_client.o + $(CXX) $^ $(LDFLAGS) -o $@ +@@ -55,6 +58,12 @@ greeter_async_client2: helloworld.pb.o h + greeter_async_server: helloworld.pb.o helloworld.grpc.pb.o greeter_async_server.o + $(CXX) $^ $(LDFLAGS) -o $@ + ++greeter_secure_client: helloworld.pb.o helloworld.grpc.pb.o greeter_secure_client.o ++ $(CXX) $^ $(LDFLAGS) -o $@ ++ ++greeter_secure_server: helloworld.pb.o helloworld.grpc.pb.o greeter_secure_server.o ++ $(CXX) $^ $(LDFLAGS) -o $@ ++ + .PRECIOUS: %.grpc.pb.cc + %.grpc.pb.cc: %.proto + $(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $< +@@ -64,7 +73,7 @@ greeter_async_server: helloworld.pb.o he + $(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $< + + clean: +- rm -f *.o *.pb.cc *.pb.h greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server ++ rm -f *.o *.pb.cc *.pb.h greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server greeter_secure_server greeter_secure_client + + + # The following is to test your system and ensure a smoother experience. diff --git a/demos/grpc/grpc_tls/README.md b/demos/grpc/grpc_tls/README.md new file mode 100644 index 00000000..41768c60 --- /dev/null +++ b/demos/grpc/grpc_tls/README.md @@ -0,0 +1,35 @@ +# Run gRPC TLS C++ Client/Server on Occlum + +## Step 1: +Downlaod, build and install openssl into `/usr/local/occlum/x86_64-linux-musl/lib`: +``` +./download_and_install_openssl.sh +``` + +## Step 2: +Download, build and install cares, protobuf and finally gRPC into `/usr/local/occlum/x86_64-linux-musl/lib`: +``` +./download_and_install_grpc.sh +``` + +## Step 3: +Prepare the gRPC TLS C++ Hello World demo Occlum instance, which consists of a client and server: +``` +./prepare_occlum_instance.sh +``` +Then you can see two occlum instance created for server(`occlum_server`) and client(`occlum_client`). + +## Step 4: +Start `tls server` which will listen on port `50051` on occlum: +``` +cd occlum_server +occlum run /bin/greeter_secure_server +``` + +Then you can invoke gRPC service by running `tls client` in a different terminal on occlum: +``` +cd occlum_client +occlum run /bin/greeter_secure_client +``` + +And you will see the "Greeter received: Hello world" in the client side output. diff --git a/demos/grpc/grpc_tls/download_and_install_grpc.sh b/demos/grpc/grpc_tls/download_and_install_grpc.sh new file mode 100755 index 00000000..69e3582d --- /dev/null +++ b/demos/grpc/grpc_tls/download_and_install_grpc.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -e + +INSTALL_DIR=/usr/local/occlum/x86_64-linux-musl +GRPC_VER=1.24.3 +GRPC_SRC_DIR=$PWD/grpc-src +CARES_VER=1_15_0 +RPOTOBUF_VER=3.10.0 + +export PATH=$PATH:$INSTALL_DIR/bin +# Tell CMake to search for packages in Occlum toolchain's directory only +export PKG_CONFIG_LIBDIR=$INSTALL_DIR/lib + +# Download grpc +wget https://github.com/grpc/grpc/archive/refs/tags/v${GRPC_VER}.tar.gz +rm -rf ${GRPC_SRC_DIR} && mkdir ${GRPC_SRC_DIR} +tar zxvf v${GRPC_VER}.tar.gz -C ${GRPC_SRC_DIR} --strip-components 1 + +# Download and Install c-ares +wget https://github.com/c-ares/c-ares/archive/refs/tags/cares-${CARES_VER}.tar.gz +tar zxvf cares-${CARES_VER}.tar.gz -C ${GRPC_SRC_DIR}/third_party/cares/cares/ --strip-components 1 +cd $GRPC_SRC_DIR/third_party/cares/cares +mkdir -p build +cd build +cmake ../ \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=occlum-gcc \ + -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR + +make -j$(nproc) +make install + +cd $PWD + +# Download and Install protobuf +wget https://github.com/protocolbuffers/protobuf/archive/refs/tags/v${RPOTOBUF_VER}.tar.gz +tar zxvf v${RPOTOBUF_VER}.tar.gz -C $GRPC_SRC_DIR/third_party/protobuf/ --strip-components 1 +cd $GRPC_SRC_DIR/third_party/protobuf +cd cmake +mkdir -p build +cd build +cmake ../ \ + -Dprotobuf_BUILD_TESTS=OFF -DBUILD_SHARED_LIBS=TRUE \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=occlum-gcc \ + -DCMAKE_CXX_COMPILER=occlum-g++ -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR \ + -DCMAKE_NO_SYSTEM_FROM_IMPORTED=TRUE + +make -j$(nproc) +make install + +cp $INSTALL_DIR/bin/protoc /usr/bin + +# Install gRPC +cd $GRPC_SRC_DIR/cmake +mkdir -p build +cd build +cmake ../.. \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=occlum-gcc \ + -DCMAKE_CXX_COMPILER=occlum-g++ -DgRPC_INSTALL=ON -DgRPC_PROTOBUF_PROVIDER=package \ + -DgRPC_ZLIB_PROVIDER=package -DgRPC_CARES_PROVIDER=package \ + -DgRPC_SSL_PROVIDER=package -DCMAKE_PREFIX_PATH=$INSTALL_DIR \ + -DCMAKE_NO_SYSTEM_FROM_IMPORTED=TRUE -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR + +make -j$(nproc) +make install +echo "gRPC build success" diff --git a/demos/grpc/grpc_tls/download_and_install_openssl.sh b/demos/grpc/grpc_tls/download_and_install_openssl.sh new file mode 100755 index 00000000..289002dd --- /dev/null +++ b/demos/grpc/grpc_tls/download_and_install_openssl.sh @@ -0,0 +1,17 @@ +#!/bin/bash +#copyright@antfinancial:adopted from a script written by geding +set -e + +git clone http://github.com/openssl/openssl +cd openssl +git checkout tags/OpenSSL_1_1_1 +CC=occlum-gcc ./config \ + --prefix=/usr/local/occlum/x86_64-linux-musl \ + --openssldir=/usr/local/occlum/x86_64-linux-musl/ssl \ + --with-rand-seed=rdcpu \ + no-async no-zlib + +make -j$(nproc) +make install + +echo "build and install openssl success!" diff --git a/demos/grpc/grpc_tls/gen-cert.sh b/demos/grpc/grpc_tls/gen-cert.sh new file mode 100755 index 00000000..26b88400 --- /dev/null +++ b/demos/grpc/grpc_tls/gen-cert.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +pushd ~ +openssl rand -writerand .rnd +popd + +# Generate valid CA +openssl genrsa -out ca.key 4096 +openssl req -new -x509 -days 365 -key ca.key -out ca.crt -subj "/OU=Test/CN=Root CA" + +# Generate valid Server Key/Cert +openssl genrsa -out server.key 4096 +openssl req -new -key server.key -out server.csr -subj "/OU=Server/CN=localhost" +openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt + +# Remove passphrase from the Server Key +openssl rsa -in server.key -out server.key + +# Generate valid Client Key/Cert +openssl genrsa -out client.key 4096 +openssl req -new -key client.key -out client.csr -subj "/OU=Client/CN=localhost" +openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt + +# Remove passphrase from Client Key +openssl rsa -in client.key -out client.key + diff --git a/demos/grpc/grpc_tls/greeter_secure_client.cc b/demos/grpc/grpc_tls/greeter_secure_client.cc new file mode 100644 index 00000000..85f97a2d --- /dev/null +++ b/demos/grpc/grpc_tls/greeter_secure_client.cc @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include + +#include "helloworld.grpc.pb.h" + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; + +using helloworld::HelloRequest; +using helloworld::HelloReply; +using helloworld::Greeter; + +class GreeterClient +{ +public: + GreeterClient ( const std::string& cert, + const std::string& key, + const std::string& root, + const std::string& server ) + { + grpc::SslCredentialsOptions opts = + { + root, + key, + cert + }; + + stub_ = Greeter::NewStub ( grpc::CreateChannel ( + server, grpc::SslCredentials ( opts ) ) ); + } + + std::string + SayHello ( const std::string& user ) + { + HelloRequest request; + request.set_name(user); + + HelloReply reply; + + ClientContext context; + + Status status = stub_->SayHello ( &context, request, &reply ); + + if ( status.ok () ) + { + return reply.message (); + } + else + { + std::cout << status.error_code () << ": " + << status.error_message () << std::endl; + return "RPC failed"; + } + } + +private: + std::unique_ptr stub_; +}; + +void +read ( const std::string& filename, std::string& data ) +{ + std::ifstream file ( filename.c_str (), std::ios::in ); + + if ( file.is_open () ) + { + std::stringstream ss; + ss << file.rdbuf (); + + file.close (); + + data = ss.str (); + } + + return; +} + +int +main ( int argc, char** argv ) +{ + std::string cert; + std::string key; + std::string root; + std::string server { "localhost:50051" }; + + read ( "client.crt", cert ); + read ( "client.key", key ); + read ( "ca.crt", root ); + + GreeterClient greeter ( cert, key, root, server ); + + std::string user ( "world" ); + std::string reply = greeter.SayHello ( user ); + + std::cout << "Greeter received: " << reply << std::endl; + + return 0; +} + diff --git a/demos/grpc/grpc_tls/greeter_secure_server.cc b/demos/grpc/grpc_tls/greeter_secure_server.cc new file mode 100644 index 00000000..e4bd4ff7 --- /dev/null +++ b/demos/grpc/grpc_tls/greeter_secure_server.cc @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include + +#include "helloworld.grpc.pb.h" + +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::Status; + +using helloworld::HelloRequest; +using helloworld::HelloReply; +using helloworld::Greeter; + +class GreeterServiceImpl final : public Greeter::Service +{ + Status SayHello ( ServerContext* context, + const HelloRequest* request, + HelloReply* reply ) override + { + std::string prefix ( "Hello " ); + + reply->set_message ( prefix + request->name () ); + + return Status::OK; + } +}; + +void +read ( const std::string& filename, std::string& data ) +{ + std::ifstream file ( filename.c_str (), std::ios::in ); + + if ( file.is_open () ) + { + std::stringstream ss; + ss << file.rdbuf (); + + file.close (); + + data = ss.str (); + } + + return; +} + + +void +runServer() +{ + /** + * [!] Be carefull here using one cert with the CN != localhost. [!] + **/ + std::string server_address ( "localhost:50051" ); + + std::string key; + std::string cert; + std::string root; + + read ( "server.crt", cert ); + read ( "server.key", key ); + read ( "ca.crt", root ); + + ServerBuilder builder; + + grpc::SslServerCredentialsOptions::PemKeyCertPair keycert = + { + key, + cert + }; + + grpc::SslServerCredentialsOptions sslOps; + sslOps.pem_root_certs = root; + sslOps.pem_key_cert_pairs.push_back ( keycert ); + + builder.AddListeningPort(server_address, grpc::SslServerCredentials( sslOps )); + + GreeterServiceImpl service; + builder.RegisterService(&service); + + std::unique_ptr < Server > server ( builder.BuildAndStart () ); + std::cout << "Server listening on " << server_address << std::endl; + + server->Wait (); +} + +int +main ( int argc, char** argv ) +{ + runServer(); + + return 0; +} + diff --git a/demos/grpc/grpc_tls/grpc_secure_client.yaml b/demos/grpc/grpc_tls/grpc_secure_client.yaml new file mode 100644 index 00000000..83224538 --- /dev/null +++ b/demos/grpc/grpc_tls/grpc_secure_client.yaml @@ -0,0 +1,13 @@ +includes: + - base.yaml +targets: + - target: /bin/ + copy: + - files: + - ../build/greeter_secure_client + - target: / + copy: + - files: + - ../client.crt + - ../client.key + - ../ca.crt diff --git a/demos/grpc/grpc_tls/grpc_secure_server.yaml b/demos/grpc/grpc_tls/grpc_secure_server.yaml new file mode 100644 index 00000000..819f4d04 --- /dev/null +++ b/demos/grpc/grpc_tls/grpc_secure_server.yaml @@ -0,0 +1,13 @@ +includes: + - base.yaml +targets: + - target: /bin/ + copy: + - files: + - ../build/greeter_secure_server + - target: / + copy: + - files: + - ../server.crt + - ../server.key + - ../ca.crt diff --git a/demos/grpc/grpc_tls/prepare_occlum_instance.sh b/demos/grpc/grpc_tls/prepare_occlum_instance.sh new file mode 100755 index 00000000..1b9e4e35 --- /dev/null +++ b/demos/grpc/grpc_tls/prepare_occlum_instance.sh @@ -0,0 +1,55 @@ +#!/bin/bash +set -e + +INSTALL_DIR=/usr/local/occlum/x86_64-linux-musl +export PATH=$PATH:$INSTALL_DIR/bin +GRPC_SRC_DIR=$PWD/grpc-src +WORK_DIR=$PWD + +rm -rf build && mkdir build +pushd build + +cp -R ${GRPC_SRC_DIR}/examples/cpp/helloworld/. . +patch Makefile -i ${WORK_DIR}/Makefile.patch + +cp ${GRPC_SRC_DIR}/examples/protos/helloworld.proto . +cp ${WORK_DIR}/*.cc . + +make -j$(nproc) + +popd + +# Generate demo ca/csr/crt +./gen-cert.sh + +# Build server occlum instance +rm -rf occlum_server +occlum new occlum_server +cd occlum_server + +rm -rf image && \ +copy_bom -f ../grpc_secure_server.yaml --root image --include-dir /opt/occlum/etc/template && \ +occlum build + +if [ $? -ne 0 ] +then + echo "occlum build failed" + exit 1 +fi + +# Build client occlum instance +cd $WORK_DIR +rm -rf occlum_client +occlum new occlum_client +cd occlum_client + +rm -rf image && \ +copy_bom -f ../grpc_secure_client.yaml --root image --include-dir /opt/occlum/etc/template && \ +occlum build + +if [ $? -ne 0 ] +then + echo "occlum build failed" + exit 1 +fi +