Browse Source

Graphene-SGX Secure Container: Seamless Integration of Docker Container and Graphene-SGX (#177)

* Adding scripts for building and running GSC (Graphene-SGX Secure Container)
* Describing GSC usage in Tools/README
Li Lei 4 years ago
parent
commit
2a98391d7b
3 changed files with 266 additions and 0 deletions
  1. 24 0
      Tools/README
  2. 109 0
      Tools/gen_manifest
  3. 133 0
      Tools/gsce

+ 24 - 0
Tools/README

@@ -0,0 +1,24 @@
+Graphene-SGX Secure Container
+--------------------------------
+Graphene-SGX Secure Container (GSC) is a container system where the containerized application can be protected by Graphene-SGX while it is running in a container environment. The GSC system includes two parts: (1) a Docker container instance where the application is running inside Graphene-SGX and both of them are running inside the container instance; (2) a front-end named GSCE (GSC Engine) which takes a legacy Docker container image and automatically launches the contained application inside a GSC container instance. 
+
+Launching a GSC container instance includes following steps: 
+
+(1) Make sure there is a Docker container image of your application in the local or remote image repository. 
+
+(2) Download and Compile Graphene-SGX;
+
+(2) Go to graphene/Tools
+
+(3) Run a GSC container via the following command: 
+
+   ./gsce run [All the arguments used for launching a normal Docker container] [docker Image Name:Tag].
+
+Let's take redis, a key-value, in-memory database as an example. Assume the user runs a normal redis from its docker image as follows.
+
+docker run -i -t -p 6379:6379 redis:latest
+
+To launch a GSC container running redis, simply change docker to "./gsce", i.e., the user runs the command as follows.
+
+./gsce run -i -t -p 6379:6379 redis:latest
+

+ 109 - 0
Tools/gen_manifest

@@ -0,0 +1,109 @@
+#!/usr/bin/env python2
+
+import os
+import sys
+import subprocess
+import re
+from shutil import copyfile
+runtime_libs = ['libc', 
+                'libdl',
+                'libm',
+                'libpthread', 
+                'libutil',
+                'libnss_dns', 
+                'libresolv', 
+                'librt']
+  
+def parse_libs (bin_path) :
+  print (bin_path)
+  ldd_out = subprocess.check_output(['ldd', bin_path])
+  lib_list = []
+  for line in ldd_out.splitlines():
+    match = re.match(r'\t(.*) => (.*) \(0x', line)
+    if match and match.group(1) and match.group(2):
+      full_lib_name = match.group(1)
+      name_match = re.match(r'([\w\d]*)(\.*)(.*)', full_lib_name)
+      if name_match:
+        lib_name = name_match.group(1)
+        lib_path = match.group(2)
+        if lib_name not in runtime_libs : 
+          lib_list.append((name_match.group(1), match.group(2)))
+  return lib_list
+
+def make_exec(path) :
+  mode = os.stat(path).st_mode
+  mode |= (mode & 0o444) >> 2    # copy R bits to X
+  os.chmod(path, mode)
+
+def gen_manifest(app_name, bin_name, g_path) :
+  m_path = g_path + '/LibOS/shim/test/apps/' + app_name + "/" + app_name +"." + "manifest"
+  print (m_path)
+  mf = open(m_path, "w")
+  make_exec(m_path)
+  mf.write("#!" + g_path + "/Runtime/pal_loader \n")
+  mf.write("loader.preload = file:../../../../../Runtime/libsysdb.so \n")
+  
+  # Get Path of Binary
+  bin_path = subprocess.check_output(['which', bin_name]).strip()
+  mf.write('loader.exec = file:' + bin_path + '\n')
+  mf.write('loader.execname = ' + bin_name + '\n')
+  mf.write('loader.env.LD_LIBRARY_PATH = /graphene:/graphene/resolv:/host:/usr/local/lib:/usr/lib:/usr/lib/x86_64-linux-gnu \n')
+  mf.write('loader.env.PATH = /usr/local/bin:/usr/bin:/bin \n' + 
+ 	   'loader.env.USERNAME = \n' + 
+           'loader.env.PWD = \n' +
+           'loader.debug_type = none \n')
+  mf.write('\n')
+
+  # File system setting
+  mf.write('fs.mount.lib1.type = chroot \n' +
+           'fs.mount.lib1.path = /graphene \n' +
+           'fs.mount.lib1.uri = file:../../../../../Runtime \n \n')
+
+  mf.write('fs.mount.lib2.type = chroot \n' +
+           'fs.mount.lib2.path = /host \n' +
+	   'fs.mount.lib2.uri = file:/lib/x86_64-linux-gnu \n \n')
+  
+  mf.write('fs.mount.bin.type = chroot \n' +
+	   'fs.mount.bin.path = /bin \n' +
+	   'fs.mount.bin.uri = file:/bin \n \n')
+
+  mf.write('fs.mount.usr.type = chroot \n' +
+	   'fs.mount.usr.path = /usr \n' +
+	   'fs.mount.usr.uri = file:/usr \n \n')
+
+  mf.write('fs.mount.etc.type = chroot \n' +
+	   'fs.mount.etc.path = /etc \n' +
+	   'fs.mount.etc.uri = file: \n \n')
+  
+  # Set Dependent Libraries
+  
+  mf.write('sgx.trusted_files.ld = file:../../../../../Runtime/ld-linux-x86-64.so.2 \n' +
+           'sgx.trusted_files.libc = file:../../../../../Runtime/libc.so.6 \n' +
+           'sgx.trusted_files.libdl = file:../../../../../Runtime/libdl.so.2 \n' +
+           'sgx.trusted_files.libm = file:../../../../../Runtime/libm.so.6 \n' +
+           'sgx.trusted_files.libpthread = file:../../../../../Runtime/libpthread.so.0 \n' + 
+           'sgx.trusted_files.libutil = file:../../../../../Runtime/libutil.so.1 \n' + 
+           'sgx.trusted_files.libnss3 = file:../../../../../Runtime/libnss_dns.so.2 \n' +
+           'sgx.trusted_files.libresolv = file:../../../../../Runtime/libresolv.so.2 \n')
+  
+  lib_list = parse_libs(bin_path)
+  for lib_name, lib_path in lib_list :
+    print ("lib name: " + lib_name)
+    print ("lib path: " + lib_path)
+    mf.write('sgx.trusted_files.' + lib_name + ' = file:' + lib_path + '\n')
+
+  mf.write('\n')
+
+  #	   'sgx.allowed_files.usr = file:/usr \n')
+
+  mf.close()
+   
+if __name__ == "__main__":
+  if len(sys.argv) != 4: 
+   print ("Usage: gen_manifest [App Name] [bin_name] [Graphene Path]")
+   exit()
+  app_name = sys.argv[1]
+  bin_name = sys.argv[2]
+  g_path = sys.argv[3]
+
+  gen_manifest(app_name, bin_name, g_path)

+ 133 - 0
Tools/gsce

@@ -0,0 +1,133 @@
+#!/usr/bin/env python2
+import sys,os
+import subprocess
+import re
+
+
+def gen_dockerfile( image_name, app_name, bin_name, proj_dir) :
+  if not os.path.exists(proj_dir + "/Tools/build") :
+    os.makedirs(proj_dir + "/Tools/build")
+  df =open(proj_dir + "/Tools/build/Dockerfile" + '.' + app_name, 'w')
+  df.write('# This file is auto-generated, any edits will be overwritten\n')
+
+  df.write('\n')
+  # Choose the base image from the user input
+  df.write('FROM '+ image_name + '\n')
+  df.write('\n')
+
+  # SWITCH to ROOT
+  df.write('# SWITCH to root \n')
+  df.write('USER root \n')
+  df.write('\n')
+ 
+  # DOWNLOAD dependencies
+  df.write('# Download dependencies\n')
+  df.write('RUN apt-get update && \ \n')
+  df.write('    apt-get install -y openssl libjemalloc-dev python python-pip python-dev \n')
+  df.write('RUN pip install protobuf && \ \n')
+  df.write('    pip install pycrypto\n')
+
+  df.write('# Temporal fixes for Dependencies Issue #1: libcrypto.so.1.0.0 and libssl.so.1.0.0 have different locations \n')
+  df.write('RUN ln -s /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 && \ \n')
+  df.write('    ln -s /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 /lib/x86_64-linux-gnu/libssl.so.1.0.0 \n')
+  
+  # SETUP Directory Structure
+  print "cwd: "+ proj_dir
+  df.write('# Setup Directory Structure \n')
+ # df.write('RUN mkdir -p ' + proj_dir + '\n')
+  df.write('RUN mkdir -p ' + proj_dir + '/LibOS/shim/test/apps/' + app_name + '\n')
+  df.write('RUN mkdir -p ' + proj_dir + '/Pal/src/host/Linux-SGX/signer \n')
+  df.write('RUN mkdir -p ' + proj_dir + '/Runtime \n')
+  df.write('RUN mkdir /gbin \n')
+
+  # COPY system files
+  df.write('# Copy system files \n')
+  df.write('COPY Runtime/* ' + proj_dir + '/Runtime/ \n')
+  df.write('COPY Pal/src/Makefile.Host ' + proj_dir + '/Pal/src/Makefile.Host \n')
+  df.write('COPY Pal/src/host/Linux-SGX/signer/* ' + proj_dir + '/Pal/src/host/Linux-SGX/signer/ \n')
+
+  # COPY tools for building app instance
+  df.write('# Copy tools for building app instance\n')
+  df.write('COPY Tools/build/tools/* /gbin/ \n')
+  df.write('COPY Tools/gen_manifest /gbin/  \n')
+
+  # Generating manifest file for target app
+  df.write('# Generating manifest for target app \n')
+  df.write('RUN /gbin/gen_manifest ' + app_name + ' ' + bin_name + ' ' + proj_dir + '\n')
+
+  # Sign Enclave
+  df.write('# Signing Enclave \n')
+  df.write('RUN cd ' + proj_dir + '/LibOS/shim/test/apps/' + app_name + ' && \ \n'
+           '    '+ proj_dir + '/Pal/src/host/Linux-SGX/signer/pal-sgx-sign -libpal ' + proj_dir + 
+           '/Pal/src/host/Linux-SGX/../../../../Runtime/libpal-Linux-SGX.so -key ' + proj_dir + 
+           '/Pal/src/host/Linux-SGX/signer/enclave-key.pem -output ' + app_name + '.manifest.sgx ' + 
+           '-manifest ' + app_name + '.manifest \n')
+  # Remove signing key
+  df.write('# Removing key after signing \n')
+  # TODO
+  
+  # Overwrite Entry Point
+  df.write('ENTRYPOINT  ["/bin/bash", "/gbin/app_exec"] \n') 
+  df.close()
+
+def make_exec(path) :
+  mode = os.stat(path).st_mode
+  mode |= (mode & 0o444) >> 2    # copy R bits to X
+  os.chmod(path, mode)
+
+def gen_app_executor(app_name, bin_cmd, proj_dir) :
+  if not os.path.exists(proj_dir + "/Tools/build/tools") :
+    os.makedirs(proj_dir + "/Tools/build/tools")
+
+  e_path = proj_dir + "/Tools/build/tools/app_exec"
+  print "e_path: " + e_path
+  ef = open(e_path, "w")
+  make_exec(e_path)
+  ef.write('#!/bin/bash \n \n')
+  ef.write('cd ' + proj_dir + '/LibOS/shim/test/apps/' + app_name +'\n')
+  ef.write('# Generate EINITOKEN \n')
+  ef.write(proj_dir + '/Pal/src/host/Linux-SGX/signer/pal-sgx-get-token -output ' 
+           + app_name + '.token -sig ' + app_name + '.sig \n')
+  ef.write('# Run the application \n')
+  ef.write('SGX=1 ./' + app_name + '.manifest.sgx ' + bin_cmd + '\n')
+  
+  ef.close()
+  
+if __name__ == "__main__":
+  if len(sys.argv) < 3:
+    print "Usage: gsce run [Image name] "
+    exit()
+
+  image_name = sys.argv[-1]
+  image_match = re.match(r'([^:]*)(:*)(.*)', image_name)
+  if image_match :
+    app_name = image_match.group(1)
+  print "app_name: " + app_name
+  inspect_cmd = "sudo docker inspect --format '{{.Config.Cmd}}' " + image_name
+  res = subprocess.check_output(inspect_cmd, shell=True).strip()
+  print res
+  match = re.match(r'\[([^\s]*)\s*(.*)\]', res)
+  bin_name = match.group(1)
+  if match.group(2) :
+    bin_cmd = match.group(2)
+  else :
+    bin_cmd = ""
+  print "bin_name: " + bin_name + " bin_cmd: " + bin_cmd
+  # Store the rest arguments as docker run arguments
+  docker_str = " " + " ".join(sys.argv[2:-1])
+  
+  # print image_cmd
+  proj_dir = os.path.abspath(os.getcwd() + "/../")
+
+  # STEP 1: Generating Dockerfile
+  gen_dockerfile(image_name, app_name, bin_name, proj_dir)
+  
+  # STEP 2: Generating entry point execute script
+  gen_app_executor(app_name, bin_cmd, proj_dir)
+
+  # STEP 3: Building new docker image with generated Dockerfile
+  os.chdir('..')
+  os.system("sudo docker build -f Tools/build/Dockerfile." + app_name + " -t gsc_" + app_name + " .\n") 
+
+  # STEP 4: Run GSC with the target app
+  os.system("sudo docker run -i -t" + docker_str +" --device=/dev/gsgx --device=/dev/isgx -v /var/run/aesmd/aesm.socket:/var/run/aesmd/aesm.socket gsc_"+app_name+"\n")