From c270a92a2998a25415c56203446e0bcb6e867980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Wei=C3=9Fe?= <66256922+daniel-weisse@users.noreply.github.com> Date: Wed, 27 Oct 2021 12:58:26 +0200 Subject: [PATCH] AB#1317 Update Graphene to Gramine (#268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rename Graphene to Gramine * Update gramine-hello sample * Update gramine-nginx sample * Update gramine-redis sample * Adjust cli to new Gramine manifest format Signed-off-by: Daniel Weiße --- README.md | 10 +- ROADMAP.md | 2 +- .../{graphenePrepare.go => graminePrepare.go} | 140 ++++++++++---- ...Prepare_test.go => graminePrepare_test.go} | 17 +- cli/cmd/packageInfo.go | 18 +- cli/cmd/root.go | 2 +- cmd/premain-libos/main.go | 31 +-- marble/premain/{graphene.go => gramine.go} | 10 +- .../Makefile | 10 +- samples/gramine-hello/README.md | 37 ++++ .../{graphene-hello => gramine-hello}/hello.c | 0 samples/gramine-hello/hello.manifest.template | 57 ++++++ .../manifest.json | 0 .../Makefile | 75 ++++---- samples/gramine-nginx/README.md | 37 ++++ .../manifest.json | 4 +- .../nginx-gramine.conf.template} | 2 +- samples/gramine-nginx/nginx.manifest.template | 84 ++++++++ .../.gitignore | 0 samples/gramine-redis/Dockerfile | 43 +++++ .../README.md | 23 +-- .../kubernetes/Chart.yaml | 2 +- .../kubernetes/templates/redis.yaml | 10 +- .../kubernetes/values.yaml | 2 +- .../manifest.json} | 4 +- .../redis-server.manifest.template | 178 +++++++++++++++++ samples/graphene-hello/README.md | 43 ----- .../graphene-hello/hello.manifest.template | 48 ----- samples/graphene-nginx/README.md | 38 ---- .../graphene-nginx/nginx.manifest.template | 116 ------------ samples/graphene-redis/Dockerfile | 40 ---- .../redis-server.manifest.template | 179 ------------------ 32 files changed, 643 insertions(+), 619 deletions(-) rename cli/cmd/{graphenePrepare.go => graminePrepare.go} (65%) rename cli/cmd/{graphenePrepare_test.go => graminePrepare_test.go} (86%) rename marble/premain/{graphene.go => gramine.go} (81%) rename samples/{graphene-hello => gramine-hello}/Makefile (56%) create mode 100644 samples/gramine-hello/README.md rename samples/{graphene-hello => gramine-hello}/hello.c (100%) create mode 100644 samples/gramine-hello/hello.manifest.template rename samples/{graphene-hello => gramine-hello}/manifest.json (100%) rename samples/{graphene-nginx => gramine-nginx}/Makefile (65%) create mode 100644 samples/gramine-nginx/README.md rename samples/{graphene-nginx => gramine-nginx}/manifest.json (95%) rename samples/{graphene-nginx/nginx-graphene.conf.template => gramine-nginx/nginx-gramine.conf.template} (95%) create mode 100644 samples/gramine-nginx/nginx.manifest.template rename samples/{graphene-redis => gramine-redis}/.gitignore (100%) create mode 100644 samples/gramine-redis/Dockerfile rename samples/{graphene-redis => gramine-redis}/README.md (78%) rename samples/{graphene-redis => gramine-redis}/kubernetes/Chart.yaml (60%) rename samples/{graphene-redis => gramine-redis}/kubernetes/templates/redis.yaml (89%) rename samples/{graphene-redis => gramine-redis}/kubernetes/values.yaml (56%) rename samples/{graphene-redis/redis-manifest.json => gramine-redis/manifest.json} (96%) create mode 100644 samples/gramine-redis/redis-server.manifest.template delete mode 100644 samples/graphene-hello/README.md delete mode 100644 samples/graphene-hello/hello.manifest.template delete mode 100644 samples/graphene-nginx/README.md delete mode 100644 samples/graphene-nginx/nginx.manifest.template delete mode 100644 samples/graphene-redis/Dockerfile delete mode 100644 samples/graphene-redis/redis-server.manifest.template diff --git a/README.md b/README.md index b91a4963..748e7acd 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ To keep things simple, MarbleRun issues one concise remote attestation statement ### Supported runtimes MarbleRun supports services built with one of the following frameworks: * [EGo][ego] -* [Graphene][graphene] +* [Gramine][gramine] * [Edgeless RT][edgelessrt] More are coming soon. @@ -66,15 +66,15 @@ We provide basic examples on how to build confidential apps with MarbleRun: * See [helloworld](samples/helloworld) for an example in Go * See [helloc++](samples/helloc++) for an example in C++ -* See [graphene-hello](samples/graphene-hello) for an example using Graphene +* See [gramine-hello](samples/gramine-hello) for an example using Gramine * See [occlum-hello](samples/occlum-hello) for an example using Occlum ### Advanced In case you want to see how you can integrate popular existing solutions with MarbleRun, we provide more advanced examples: -* See [graphene-nginx](samples/graphene-nginx) for an example of converting an existing Graphene application to a Marble -* See [graphene-redis](samples/graphene-redis) for a distributed Redis example using Graphene +* See [gramine-nginx](samples/gramine-nginx) for an example of converting an existing Gramine application to a Marble +* See [gramine-redis](samples/gramine-redis) for a distributed Redis example using Gramine ### Confidential emoji voting @@ -92,7 +92,7 @@ The popular [Linkerd][linkerd] service mesh uses the simple and scalable *emojiv [go-pkg-badge]: https://pkg.go.dev/badge/github.com/edgelesssys/marblerun [go-report-card]: https://goreportcard.com/report/github.com/edgelesssys/marblerun [go-report-card-badge]: https://goreportcard.com/badge/github.com/edgelesssys/marblerun -[graphene]: https://github.com/oscarlab/graphene +[gramine]: https://github.com/gramineproject/gramine [license-badge]: https://img.shields.io/github/license/edgelesssys/marblerun [linkerd]: https://linkerd.io [marblerunsh]: https://marblerun.sh diff --git a/ROADMAP.md b/ROADMAP.md index 263ba3af..cab526fa 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -15,7 +15,7 @@ * Trusted time via Roughtime * EdgelessDB storage-backend plugin * Microsoft Azure Attestation integration -* Graphene TTLS support +* Gramine TTLS support ## Long-term diff --git a/cli/cmd/graphenePrepare.go b/cli/cmd/graminePrepare.go similarity index 65% rename from cli/cmd/graphenePrepare.go rename to cli/cmd/graminePrepare.go index 7ec348a2..b5035183 100644 --- a/cli/cmd/graphenePrepare.go +++ b/cli/cmd/graminePrepare.go @@ -24,35 +24,38 @@ const premainName = "premain-libos" // uuidName is the file name of a Marble's uuid const uuidName = "uuid" -// commentMarbleRunAdditions holds the marker which is appended to the Graphene manifest before the performed additions +// commentMarbleRunAdditions holds the marker which is appended to the Gramine manifest before the performed additions const commentMarbleRunAdditions = "\n# MARBLERUN -- auto generated configuration entries \n" // longDescription is the help text shown for this command -const longDescription = `Modifies a Graphene manifest for use with MarbleRun. +const longDescription = `Modifies a Gramine manifest for use with MarbleRun. -This command tries to automatically adjust the required parameters in an already existing Graphene manifest template, simplifying the migration of your existing Graphene application to MarbleRun. +This command tries to automatically adjust the required parameters in an already existing Gramine manifest template, simplifying the migration of your existing Gramine application to MarbleRun. Please note that you still need to manually create a MarbleRun manifest. -For more information about the requirements and changes performed, consult the documentation: https://edglss.cc/doc-mr-graphene +For more information about the requirements and changes performed, consult the documentation: https://edglss.cc/doc-mr-gramine -The parameter of this command is the path of the Graphene manifest template you want to modify. +The parameter of this command is the path of the Gramine manifest template you want to modify. ` type diff struct { - manifestEntry string alreadyExists bool + // type of the entry, one of {'string', 'array'} + entryType string + // content of the entry + manifestEntry string } -func newGraphenePrepareCmd() *cobra.Command { +func newGraminePrepareCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "graphene-prepare", - Short: "Modifies a Graphene manifest for use with MarbleRun", + Use: "gramine-prepare", + Short: "Modifies a Gramine manifest for use with MarbleRun", Long: longDescription, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { fileName := args[0] - return addToGrapheneManifest(fileName) + return addToGramineManifest(fileName) }, SilenceUsage: true, } @@ -60,9 +63,21 @@ func newGraphenePrepareCmd() *cobra.Command { return cmd } -func addToGrapheneManifest(fileName string) error { - // Read Graphene manifest and populate TOML tree +func addToGramineManifest(fileName string) error { + // Read Gramine manifest and populate TOML tree fmt.Println("Reading file:", fileName) + + file, err := ioutil.ReadFile(fileName) + if err != nil { + return err + } + if strings.Contains(string(file), premainName) || strings.Contains(string(file), "EDG_MARBLE_COORDINATOR_ADDR") || + strings.Contains(string(file), "EDG_MARBLE_TYPE") || strings.Contains(string(file), "EDG_MARBLE_UUID_FILE") || + strings.Contains(string(file), "EDG_MARBLE_DNS_NAMES") { + color.Yellow("The supplied manifest already contains changes for MarbleRun. Have you selected the correct file?") + return errors.New("manifest already contains MarbleRun changes") + } + tree, err := toml.LoadFile(fileName) if os.IsNotExist(err) { return fmt.Errorf("file does not exist: %v", fileName) @@ -93,37 +108,51 @@ func parseTreeForChanges(tree *toml.Tree) (map[string]interface{}, map[string]in original["sgx.remote_attestation"] = tree.Get("sgx.remote_attestation") original["sgx.enclave_size"] = tree.Get("sgx.enclave_size") original["sgx.thread_num"] = tree.Get("sgx.thread_num") - original["sgx.trusted_files.marblerun_premain"] = tree.Get("sgx.trusted_files.marblerun_premain") - original["sgx.allowed_files.marblerun_uuid"] = tree.Get("sgx.allowed_files.marblerun_uuid") + original["loader.env.EDG_MARBLE_COORDINATOR_ADDR"] = tree.Get("loader.env.EDG_MARBLE_COORDINATOR_ADDR") + original["loader.env.EDG_MARBLE_TYPE"] = tree.Get("loader.env.EDG_MARBLE_TYPE") + original["loader.env.EDG_MARBLE_UUID_FILE"] = tree.Get("loader.env.EDG_MARBLE_UUID_FILE") + original["loader.env.EDG_MARBLE_DNS_NAMES"] = tree.Get("loader.env.EDG_MARBLE_DNS_NAMES") - // Abort, if we cannot find an endpoint + // Abort, if we cannot find an entrypoint if original["libos.entrypoint"] == nil { return nil, nil, errors.New("cannot find libos.entrypoint") } - // If MarbleRun already touched the manifest, abort. - if original["libos.entrypoint"].(string) == premainName || original["sgx.trusted_files.marblerun_premain"] != nil || original["sgx.allowed_files.marblerun_uuid"] != nil { - color.Yellow("The supplied manifest already contains changes for MarbleRun. Have you selected the correct file?") - return nil, nil, errors.New("manifest already contains MarbleRun changes") + // add premain and uuid files + if err := insertFile(original, changes, "trusted_files", premainName, tree); err != nil { + return nil, nil, err + } + if err := insertFile(original, changes, "allowed_files", uuidName, tree); err != nil { + return nil, nil, err } // Add premain-libos executable as trusted file & entry point changes["libos.entrypoint"] = premainName - changes["sgx.trusted_files.marblerun_premain"] = "file:" + premainName - // Set original endpoint as argv0. If one exists, keep the old one + // Set original entrypoint as argv0. If one exists, keep the old one if original["loader.argv0_override"] == nil { changes["loader.argv0_override"] = original["libos.entrypoint"].(string) } - // Enable use "insecure" host env (which delegates the "secure" handling to MarbleRun) - if original["loader.insecure__use_host_env"] == nil || original["loader.insecure__use_host_env"].(int64) == 0 { - changes["loader.insecure__use_host_env"] = 1 + // If insecure host environment is disabled (which hopefully it is), specify the required passthrough variables + if original["loader.insecure__use_host_env"] == nil || !original["loader.insecure__use_host_env"].(bool) { + if original["loader.env.EDG_MARBLE_COORDINATOR_ADDR"] == nil { + changes["loader.env.EDG_MARBLE_COORDINATOR_ADDR"] = "{ passthrough = true }" + } + if original["loader.env.EDG_MARBLE_TYPE"] == nil { + changes["loader.env.EDG_MARBLE_TYPE"] = "{ passthrough = true }" + } + if original["loader.env.EDG_MARBLE_UUID_FILE"] == nil { + changes["loader.env.EDG_MARBLE_UUID_FILE"] = "{ passthrough = true }" + } + if original["loader.env.EDG_MARBLE_DNS_NAMES"] == nil { + changes["loader.env.EDG_MARBLE_DNS_NAMES"] = "{ passthrough = true }" + } } // Enable remote attestation - if original["sgx.remote_attestation"] == nil || original["sgx.remote_attestation"].(int64) == 0 { - changes["sgx.remote_attestation"] = 1 + if original["sgx.remote_attestation"] == nil || !original["sgx.remote_attestation"].(bool) { + changes["sgx.remote_attestation"] = true } // Ensure at least 1024 MB of enclave memory for the premain Go runtime @@ -140,9 +169,6 @@ func parseTreeForChanges(tree *toml.Tree) (map[string]interface{}, map[string]in changes["sgx.thread_num"] = 16 } - // Add Marble UUID to allowed files - changes["sgx.allowed_files.marblerun_uuid"] = "file:" + uuidName - return original, changes, nil } @@ -159,8 +185,17 @@ func calculateChanges(original map[string]interface{}, updates map[string]interf // Add quotation marks for strings, direct value if not switch v := changedValue.(type) { case string: + newDiff.entryType = "string" newDiff.manifestEntry = fmt.Sprintf("%s = \"%v\"", index, v) + case []interface{}: + newDiff.entryType = "array" + newEntry := fmt.Sprintf("%s = [\n", index) + for _, val := range v { + newEntry = fmt.Sprintf("%s \"%v\",\n", newEntry, val) + } + newDiff.manifestEntry = fmt.Sprintf("%s]", newEntry) default: + newDiff.entryType = "string" newDiff.manifestEntry = fmt.Sprintf("%s = %v", index, v) } changeDiffs = append(changeDiffs, newDiff) @@ -177,7 +212,7 @@ func calculateChanges(original map[string]interface{}, updates map[string]interf // performChanges displays the suggested changes to the user and tries to automatically perform them func performChanges(changeDiffs []diff, fileName string) error { - fmt.Println("\nMarbleRun suggests the following changes to your Graphene manifest:") + fmt.Println("\nMarbleRun suggests the following changes to your Gramine manifest:") for _, entry := range changeDiffs { if entry.alreadyExists { color.Yellow(entry.manifestEntry) @@ -197,7 +232,7 @@ func performChanges(changeDiffs []diff, fileName string) error { directory := filepath.Dir(fileName) - // Read Graphene manifest as normal text file + // Read Gramine manifest as normal text file manifestContentOriginal, err := ioutil.ReadFile(fileName) if err != nil { return err @@ -225,7 +260,7 @@ func performChanges(changeDiffs []diff, fileName string) error { } fmt.Println("Downloading MarbleRun premain from GitHub...") - // Download MarbleRun premain for Graphene from GitHub + // Download MarbleRun premain for Gramine from GitHub if err := downloadPremain(directory); err != nil { color.Red("ERROR: Cannot download '%s' from GitHub. Please add the file manually.", premainName) } @@ -268,7 +303,7 @@ func downloadPremain(directory string) error { For existing entries: Run a RegEx search, replace the line. For new entries: Append to the end of the file. NOTE: This only works for flat-mapped TOML configs. - These seem to be usually used for Graphene manifests. + These seem to be usually used for Gramine manifests. However, TOML is quite flexible, and there are no TOML parsers out there which are style & comments preserving So, if we do not have a flat-mapped config, this will fail at some point. */ @@ -281,13 +316,23 @@ func appendAndReplace(changeDiffs []diff, manifestContent []byte) ([]byte, error // If a value was previously existing, we replace the existing entry key := strings.Split(value.manifestEntry, " =") regexKey := strings.ReplaceAll(key[0], ".", "\\.") - regex := regexp.MustCompile("(?m)^" + regexKey + "\\s?=.*$") + var regex *regexp.Regexp + + switch value.entryType { + case "string": + regex = regexp.MustCompile("(?m)^" + regexKey + "\\s?=.*$") + case "array": + regex = regexp.MustCompile("(?m)^" + regexKey + "\\s?=([^\\]]*)\\]$") + default: + return nil, fmt.Errorf("unkown manifest entry type: %v", value.entryType) + } + // Check if we actually found the entry we searched for. If not, we might be dealing with a TOML file we cannot handle correctly without a full parser. regexMatches := regex.FindAll(newManifestContent, -1) if regexMatches == nil { - color.Red("ERROR: Cannot find specified entry. Your Graphene config might not be flat-mapped.") + color.Red("ERROR: Cannot find specified entry. Your Gramine config might not be flat-mapped.") color.Red("MarbleRun can only automatically modify manifests using a flat hierarchy, as otherwise we would lose all styling & comments.") - color.Red("To continue, please manually perform the changes printed above in your Graphene manifest.") + color.Red("To continue, please manually perform the changes printed above in your Gramine manifest.") return nil, errors.New("failed to detect position of config entry") } else if len(regexMatches) > 1 { color.Red("ERROR: Found multiple potential matches for automatic value substitution.") @@ -310,3 +355,26 @@ func appendAndReplace(changeDiffs []diff, manifestContent []byte) ([]byte, error return newManifestContent, nil } + +// insertFile checks what trusted/allowed file declaration is used in the manifest and inserts files accordingly +// trusted/allowed files are either present in legacy 'sgx.trusted_files.identifier = "file:/path/file"' format +// or in TOML-array format +func insertFile(original, changes map[string]interface{}, fileType, fileName string, tree *toml.Tree) error { + fileTree := tree.Get("sgx." + fileType) + switch fileTree.(type) { + case nil: + // No files are defined in the original manifest + changes["sgx."+fileType] = []interface{}{"file:" + fileName} + return nil + case *toml.Tree: + // legacy format + changes["sgx."+fileType+".marblerun_"+fileName] = "file:" + fileName + case []interface{}: + // TOML-array format, append file to the array + original["sgx."+fileType] = tree.Get("sgx." + fileType) + changes["sgx."+fileType] = append(original["sgx."+fileType].([]interface{}), "file:"+fileName) + default: + return errors.New("could not read files from Gramine manifest") + } + return nil +} diff --git a/cli/cmd/graphenePrepare_test.go b/cli/cmd/graminePrepare_test.go similarity index 86% rename from cli/cmd/graphenePrepare_test.go rename to cli/cmd/graminePrepare_test.go index 5d3ea53d..63beb7ee 100644 --- a/cli/cmd/graphenePrepare_test.go +++ b/cli/cmd/graminePrepare_test.go @@ -16,11 +16,16 @@ import ( const someManifest = ` libos.entrypoint = "myapplication" -sgx.remote_attestation = 0 +sgx.remote_attestation = false # Some comment here in between # This should not match: sgx.enclave_size - 2 # This should also not match: sgx.enclave_size = 24M sgx.enclave_size = "128M" +sgx.trusted_files = [ + "file:/usr/favorite.file", + "file:/usr/lib/important.so" +] +sgx.allowed_files.unimportant = "file:/usr/not_that_important.txt" ` func TestCalculateChanges(t *testing.T) { @@ -75,6 +80,7 @@ func TestParseTreeForChanges(t *testing.T) { assert.GreaterOrEqual(changes["sgx.thread_num"], 16) require.NoError(v.UnmarshalText([]byte(changes["sgx.enclave_size"].(string)))) assert.GreaterOrEqual(v.GBytes(), 1.00) + assert.Equal([]interface{}{"file:/usr/favorite.file", "file:/usr/lib/important.so", "file:premain-libos"}, changes["sgx.trusted_files"]) } func TestAppendAndReplace(t *testing.T) { @@ -91,11 +97,14 @@ func TestAppendAndReplace(t *testing.T) { original["sgx.remote_attestation"] = tomlTree.Get("sgx.remote_attestation") original["sgx.enclave_size"] = tomlTree.Get("sgx.enclave_size") original["sgx.thread_num"] = tomlTree.Get("sgx.thread_num") + original["sgx.trusted_files"] = tomlTree.Get("sgx.trusted_files") // Set some changes we want to perform - changes["sgx.remote_attestation"] = 1 + changes["sgx.remote_attestation"] = true changes["sgx.enclave_size"] = "1024M" changes["sgx.thread_num"] = 16 + changedFiles := []interface{}{"file:/usr/favorite.file", "file:/usr/lib/important.so", "file:premain-libos"} + changes["sgx.trusted_files"] = changedFiles // Calculate the differences diffs := calculateChanges(original, changes) @@ -109,11 +118,13 @@ func TestAppendAndReplace(t *testing.T) { newTomlTree, err := toml.Load(string(someNewManifest)) assert.NoError(err) newRemoteAttestation := newTomlTree.Get("sgx.remote_attestation") - assert.EqualValues(1, newRemoteAttestation.(int64)) + assert.EqualValues(true, newRemoteAttestation.(bool)) newEnclaveSize := newTomlTree.Get("sgx.enclave_size") assert.EqualValues("1024M", newEnclaveSize.(string)) newThreadNum := newTomlTree.Get("sgx.thread_num") assert.EqualValues(16, newThreadNum.(int64)) + newTrustedFiles := newTomlTree.Get("sgx.trusted_files") + assert.EqualValues(changedFiles, newTrustedFiles) } func TestDownloadPremain(t *testing.T) { diff --git a/cli/cmd/packageInfo.go b/cli/cmd/packageInfo.go index cf7b68e2..070fd685 100644 --- a/cli/cmd/packageInfo.go +++ b/cli/cmd/packageInfo.go @@ -42,8 +42,8 @@ func newPackageInfoCmd() *cobra.Command { } // In every other case, try to guess if it's a directory, or expect a specific file to be pointed to - errGraphene := decodeGrapheneSigStruct(path, isDirectory) - if errGraphene == nil { + errGramine := decodeGramineSigStruct(path, isDirectory) + if errGramine == nil { return nil } errOcclum := decodeSGXSDKSigStruct(path, isDirectory) // Either Occlum or SGX SDK @@ -53,8 +53,8 @@ func newPackageInfoCmd() *cobra.Command { color.Red("ERROR: Failed to automatically determine SGX package signature properties detection.") if isDirectory { - color.Red("A directory was supplied, but it appears not to be a Graphene or Occlum instance.") - color.Red("Please either specify the .sig file (Graphene) or SGX enclave binary (Occlum / SGX SDK) directly, or the root directory of an Graphene or Occlum instance.") + color.Red("A directory was supplied, but it appears not to be a Gramine or Occlum instance.") + color.Red("Please either specify the .sig file (Gramine) or SGX enclave binary (Occlum / SGX SDK) directly, or the root directory of an Gramine or Occlum instance.") } fmt.Printf("\n") @@ -62,7 +62,7 @@ func newPackageInfoCmd() *cobra.Command { if errOpenEnclave != nil { color.Red("Open Enclave detection error: %v\n", errOpenEnclave) } - fmt.Printf("Error - Graphene: %v\n", errGraphene) + fmt.Printf("Error - Gramine: %v\n", errGramine) fmt.Printf("Error - Occlum / SGX SDK: %v\n", errOcclum) return errors.New("unable to determine enclave properties") }, @@ -162,7 +162,7 @@ func parseSigStruct(sgxMetaData []byte) ([]byte, []byte, []byte, []byte, error) return mrenclave, mrsigner[:], isvprodid, isvsvn, nil } -func decodeGrapheneSigStruct(path string, isDirectory bool) error { +func decodeGramineSigStruct(path string, isDirectory bool) error { // Check if directory contains a file ending in .sig var sigFile string if isDirectory { @@ -178,11 +178,11 @@ func decodeGrapheneSigStruct(path string, isDirectory bool) error { } foundSigFile = true sigFile = entry.Name() - color.Green("Detected Graphene instance.") + color.Green("Detected Gramine instance.") } } if !foundSigFile { - return errors.New("did not find Graphene .sig file in directory") + return errors.New("did not find Gramine .sig file in directory") } sigFile = filepath.Join(path, sigFile) } else { @@ -201,7 +201,7 @@ func decodeGrapheneSigStruct(path string, isDirectory bool) error { } if isDirectory { - color.Cyan("PackageProperties for Graphene instance at '%s':\n", path) + color.Cyan("PackageProperties for Gramine instance at '%s':\n", path) } else { color.Cyan("PackageProperties for '%s':\n", path) } diff --git a/cli/cmd/root.go b/cli/cmd/root.go index eaeb8de0..923e8b8d 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -27,7 +27,7 @@ func init() { rootCmd.AddCommand(newCertificateCmd()) rootCmd.AddCommand(newCheckCmd()) rootCmd.AddCommand(newCompletionCmd()) - rootCmd.AddCommand(newGraphenePrepareCmd()) + rootCmd.AddCommand(newGraminePrepareCmd()) rootCmd.AddCommand(newInstallCmd()) rootCmd.AddCommand(newManifestCmd()) rootCmd.AddCommand(newPrecheckCmd()) diff --git a/cmd/premain-libos/main.go b/cmd/premain-libos/main.go index 69403d67..ecdeae11 100644 --- a/cmd/premain-libos/main.go +++ b/cmd/premain-libos/main.go @@ -22,7 +22,7 @@ import ( // libOS constants for specific checks. // Use 1000 as a starting point for distinction const ( - graphene = iota + 1000 + gramine = iota + 1000 occlum ) @@ -41,11 +41,11 @@ func main() { var service string // Use different execution flows depending on libOS switch libOS { - case graphene: - log.Println("detected libOS: Graphene") + case gramine: + log.Println("detected libOS: Gramine") - // Graphene: Get service to launch before MarbleRun's premain - service, err = prepareGraphene(hostfs) + // Gramine: Get service to launch before MarbleRun's premain + service, err = prepareGramine(hostfs) if err != nil { panic(err) } @@ -85,33 +85,22 @@ func detectLibOS() (int, error) { return occlum, nil } - // Graphene detection + // Gramine detection // This looks like a general Linux kernel name, making it harder to detect... But it's unlikely someone is running SGX code on Linux 3.10.0. - // Taken from: https://github.com/oscarlab/graphene/blob/master/LibOS/shim/src/sys/shim_uname.c + // Taken from: https://github.com/gramineproject/gramine/blob/master/LibOS/shim/src/sys/shim_uname.c if sysname == "Linux" && nodename == "localhost" && release == "3.10.0" && version == "1" && machine == "x86_64" { - return graphene, nil + return gramine, nil } return 0, errors.New("cannot detect libOS") } -func prepareGraphene(hostfs afero.Fs) (string, error) { - // Filter env vars - // TODO: INSECURE! This is known, but for a proper solution we have to wait for environment variable filtering on the level of graphene. - // See: https://github.com/edgelesssys/marblerun/issues/158 & https://github.com/oscarlab/graphene/issues/2356 - for _, env := range os.Environ() { - if !strings.HasPrefix(env, "EDG_") && !strings.HasPrefix(env, "LD_LIBRARY_PATH=") { - if err := os.Unsetenv(strings.SplitN(env, "=", 2)[0]); err != nil { - return "", err - } - } - } - +func prepareGramine(hostfs afero.Fs) (string, error) { // Save the passed argument which is our service to spawn service := os.Args[0] // Run MarbleRun premain - if err := marblePremain.PreMainEx(marblePremain.GrapheneQuoteIssuer{}, marblePremain.GrapheneActivate, hostfs, hostfs); err != nil { + if err := marblePremain.PreMainEx(marblePremain.GramineQuoteIssuer{}, marblePremain.GramineActivate, hostfs, hostfs); err != nil { return "", err } diff --git a/marble/premain/graphene.go b/marble/premain/gramine.go similarity index 81% rename from marble/premain/graphene.go rename to marble/premain/gramine.go index 35e77ba9..963ec230 100644 --- a/marble/premain/graphene.go +++ b/marble/premain/gramine.go @@ -16,8 +16,8 @@ import ( "google.golang.org/grpc/credentials" ) -// GrapheneActivate sends an activation request to the Coordinator and initializes protected files. -func GrapheneActivate(req *rpc.ActivationReq, coordAddr string, tlsCredentials credentials.TransportCredentials) (*rpc.Parameters, error) { +// GramineActivate sends an activation request to the Coordinator and initializes protected files. +func GramineActivate(req *rpc.ActivationReq, coordAddr string, tlsCredentials credentials.TransportCredentials) (*rpc.Parameters, error) { // call the actual Activate function params, err := ActivateRPC(req, coordAddr, tlsCredentials) if err != nil { @@ -36,11 +36,11 @@ func GrapheneActivate(req *rpc.ActivationReq, coordAddr string, tlsCredentials c return params, nil } -// GrapheneQuoteIssuer issues quotes -type GrapheneQuoteIssuer struct{} +// GramineQuoteIssuer issues quotes +type GramineQuoteIssuer struct{} // Issue issues a quote for remote attestation for a given message (usually a certificate) -func (GrapheneQuoteIssuer) Issue(cert []byte) ([]byte, error) { +func (GramineQuoteIssuer) Issue(cert []byte) ([]byte, error) { hash := sha256.Sum256(cert) f, err := os.OpenFile("/dev/attestation/user_report_data", os.O_WRONLY, 0) diff --git a/samples/graphene-hello/Makefile b/samples/gramine-hello/Makefile similarity index 56% rename from samples/graphene-hello/Makefile rename to samples/gramine-hello/Makefile index 13f9a10e..dc09ef04 100644 --- a/samples/graphene-hello/Makefile +++ b/samples/gramine-hello/Makefile @@ -1,12 +1,10 @@ -GRAPHENEDIR?=$(HOME)/graphene - all: sign .PHONY: clean all sign: hello.manifest hello premain-libos - graphene-sgx-sign -output hello.manifest.sgx --manifest hello.manifest --key $(GRAPHENEDIR)/Pal/src/host/Linux-SGX/signer/enclave-key.pem - graphene-sgx-get-token -sig hello.sig -output hello.token + gramine-sgx-sign --output hello.manifest.sgx --manifest hello.manifest --key enclave-key.pem + gramine-sgx-get-token --sig hello.sig --output hello.token clean: @@ -18,7 +16,7 @@ hello: hello.c hello.manifest: hello.manifest.template - graphene-manifest $< > $@ + gramine-manifest $< > $@ premain-libos: @@ -26,4 +24,4 @@ premain-libos: chmod u+x premain-libos run: - graphene-sgx hello + gramine-sgx hello diff --git a/samples/gramine-hello/README.md b/samples/gramine-hello/README.md new file mode 100644 index 00000000..3a7dc8da --- /dev/null +++ b/samples/gramine-hello/README.md @@ -0,0 +1,37 @@ +# Gramine "Hello World!" example +This example shows how to run a [Gramine](https://github.com/gramineproject/gramine) application in MarbleRun. In essence, you have to add the `premain` process to the Gramine manifest. `premain` will contact the Coordinator, set up the environment, and run the actual application. See the commented [hello.manifest.template](hello.manifest.template) for details. + +## Requirements +First, install Gramine on [release v1.0](https://github.com/gramineproject/gramine/releases/tag/v1.0). You will need hardware with Intel SGX support. + +Then, before you can run the example, make sure you got the prerequisites for ECDSA remote attestation installed on your system. You can collectively install them with the following command: +```sh +sudo apt install libsgx-quote-ex-dev +``` + +## Build +You can build the example as follows: +```sh +openssl genrsa -3 -out enclave-key.pem 3072 +make +``` +Then get `mr_enclave` from the build output and set it as `UniqueID` in `manifest.json`. + +## Run +Next, use the `erthost` command to start the Coordinator in a local enclave: +```sh +erthost ../../build/coordinator-enclave.signed +``` + +The Coordinator exposes two APIs, a client REST API (port 4433) and a mesh API (port 2001). While the Coordinator and your Marble communicate via the mesh API, you can administrate the Coordinator via the REST API. + +Once the Coordinator instance is running, you can upload the manifest to the Coordinator's client API: +``` +curl -k --data-binary @manifest.json https://localhost:4433/manifest +``` + +The type of the Marble is defined in the `manifest.json`. In this example, the manifest defines a single Marble, which is called "hello". To run the application, you need to set the `EDG_MARBLE_TYPE` environement variable to that name. + +```sh +EDG_MARBLE_TYPE=hello make run +``` diff --git a/samples/graphene-hello/hello.c b/samples/gramine-hello/hello.c similarity index 100% rename from samples/graphene-hello/hello.c rename to samples/gramine-hello/hello.c diff --git a/samples/gramine-hello/hello.manifest.template b/samples/gramine-hello/hello.manifest.template new file mode 100644 index 00000000..135d8626 --- /dev/null +++ b/samples/gramine-hello/hello.manifest.template @@ -0,0 +1,57 @@ +loader.preload = "file:{{ gramine.libos }}" +loader.env.LD_LIBRARY_PATH = "/lib" + +# entrypoint must be premain-libos +libos.entrypoint = "premain-libos" + +# argv0 must be the path to the actual application +loader.argv0_override = "hello" + +# Forward EDG environment variables, used by MarbleRun +loader.env.EDG_MARBLE_TYPE = { passthrough = true } +loader.env.EDG_MARBLE_COORDINATOR_ADDR = { passthrough = true } +loader.env.EDG_MARBLE_UUID_FILE = { passthrough = true } +loader.env.EDG_MARBLE_DNS_NAMES = { passthrough = true } + +# FS mount points +fs.mount.lib.type = "chroot" +fs.mount.lib.path = "/lib" +fs.mount.lib.uri = "file:{{ gramine.runtimedir() }}" + +# the following is only required if you need DNS resolution +fs.mount.etc.type = "chroot" +fs.mount.etc.path = "/etc" +fs.mount.etc.uri = "file:/etc" + +# trusted files +sgx.trusted_files = [ + "file:{{ gramine.runtimedir() }}/libnss_dns.so.2", + "file:{{ gramine.runtimedir() }}/libnss_files.so.2", + "file:{{ gramine.runtimedir() }}/libresolv.so.2", + "file:{{ gramine.runtimedir() }}/ld-linux-x86-64.so.2", + "file:{{ gramine.runtimedir() }}/libc.so.6", + "file:{{ gramine.runtimedir() }}/libpthread.so.0", + "file:premain-libos", + "file:hello" +] + +# allowed files +sgx.allowed_files = [ + "file:/etc/hosts", + "file:/etc/host.conf", + "file:/etc/gai.conf", + "file:/etc/resolv.conf", + "file:/etc/localtime", + "file:/etc/nsswitch.conf", + "file:uuid" +] + +# enable DCAP +sgx.remote_attestation = true + +# enclave must have enough memory and threads +sgx.enclave_size = "1024M" +sgx.thread_num = 16 + +# create a debug enclave by default +sgx.debug = true \ No newline at end of file diff --git a/samples/graphene-hello/manifest.json b/samples/gramine-hello/manifest.json similarity index 100% rename from samples/graphene-hello/manifest.json rename to samples/gramine-hello/manifest.json diff --git a/samples/graphene-nginx/Makefile b/samples/gramine-nginx/Makefile similarity index 65% rename from samples/graphene-nginx/Makefile rename to samples/gramine-nginx/Makefile index 7a0e1708..c17c2a87 100644 --- a/samples/graphene-nginx/Makefile +++ b/samples/gramine-nginx/Makefile @@ -1,39 +1,38 @@ -# Building the manifest for Nginx: +# Makefile for Nginx: # -# - make Building for Linux -# - make DEBUG=1 Building for Linux, with Graphene debug output -# - make SGX=1 Building for SGX -# - make SGX=1 DEBUG=1 Building for SGX, with Graphene debug output +# - make Build for plain Linux +# - make DEBUG=1 Build for plain Linux, with Gramine debug output +# - make SGX=1 Build for SGX +# - make SGX=1 DEBUG=1 Build for SGX, with Gramine debug output # -# Use `make clean` to remove Graphene-generated files. +# Use `make clean` to remove Gramine-generated files. # # Use `make distclean` to further remove the Nginx tarball, source code, # and installation. THIS_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine) +GRAMINEDIR ?= $(HOME)/gramine + INSTALL_DIR ?= $(THIS_DIR)install NGINX_SRC ?= $(THIS_DIR)nginx-1.16.1 NGINX_SHA256 ?= f11c2a6dd1d3515736f0324857957db2de98be862461b5a542a3ac6188dbe32b -# Mirrors for downloading the Nginx source code NGINX_MIRRORS ?= \ http://nginx.org/download \ - https://packages.grapheneproject.io/distfiles + https://packages.gramineproject.io/distfiles -# Address and port for the Nginx server to listen LISTEN_HOST ?= 127.0.0.1 LISTEN_PORT ?= 8002 LISTEN_SSL_PORT ?= 8444 -# Relative path to Graphene root -GRAPHENEDIR ?= $(THIS_DIR)../.. -SGX_SIGNER_KEY ?= $(GRAPHENEDIR)/Pal/src/host/Linux-SGX/signer/enclave-key.pem +SGX_SIGNER_KEY ?= enclave-key.pem ifeq ($(DEBUG),1) -GRAPHENE_LOG_LEVEL = debug +GRAMINE_LOG_LEVEL = debug else -GRAPHENE_LOG_LEVEL = error +GRAMINE_LOG_LEVEL = error endif .PHONY: all @@ -42,15 +41,13 @@ ifeq ($(SGX),1) all: nginx.manifest.sgx nginx.sig nginx.token endif -include $(GRAPHENEDIR)/Scripts/Makefile.configs +include $(GRAMINEDIR)/Scripts/Makefile.configs -# The make targets for downloading and compiling the Nginx source code, and -# installing the binaries. -# Note that Graphene doesn't support eventfd() and PR_SET_DUMPABLE, so we manually +# Note that Gramine doesn't support eventfd() and PR_SET_DUMPABLE, so we manually # overwrite these macros in the autogenerated configuration header of Nginx. $(INSTALL_DIR)/sbin/nginx: $(NGINX_SRC)/configure cd $(NGINX_SRC) && ./configure --prefix=$(abspath $(INSTALL_DIR)) \ - --without-http_rewrite_module --with-http_ssl_module + --without-http_rewrite_module --with-http_ssl_module sed -e "s|#define NGX_HAVE_EVENTFD[[:space:]]\+1|#define NGX_HAVE_EVENTFD 0|g" \ -e "s|#define NGX_HAVE_SYS_EVENTFD_H[[:space:]]\+1|#define NGX_HAVE_SYS_EVENTFD_H 0|g" \ -e "s|#define NGX_HAVE_PR_SET_DUMPABLE[[:space:]]\+1|#define NGX_HAVE_PR_SET_DUMPABLE 0|g" \ @@ -62,12 +59,12 @@ $(NGINX_SRC)/configure: $(NGINX_SRC).tar.gz tar --touch -xzf $< $(NGINX_SRC).tar.gz: - $(GRAPHENEDIR)/Scripts/download --output $@ --sha256 $(NGINX_SHA256) \ + $(GRAMINEDIR)/CI-Examples/common_tools/download --output $@ --sha256 $(NGINX_SHA256) \ $(foreach mirror,$(NGINX_MIRRORS),--url $(mirror)/$(NGINX_SRC).tar.gz) nginx.manifest: nginx.manifest.template - graphene-manifest \ - -Dlog_level=$(GRAPHENE_LOG_LEVEL) \ + gramine-manifest \ + -Dlog_level=$(GRAMINE_LOG_LEVEL) \ -Darch_libdir=$(ARCH_LIBDIR) \ -Dinstall_dir=$(INSTALL_DIR) \ -Dinstall_dir_abspath=$(abspath $(INSTALL_DIR)) \ @@ -77,14 +74,14 @@ premain-libos: wget https://github.com/edgelesssys/marblerun/releases/latest/download/premain-libos chmod u+x premain-libos -# Generating the SGX-specific manifest (nginx.manifest.sgx), the enclave signature, -# and the token for enclave initialization. nginx.manifest.sgx: nginx.manifest $(INSTALL_DIR)/sbin/nginx \ - $(INSTALL_DIR)/conf/nginx-graphene.conf \ + $(INSTALL_DIR)/conf/nginx-gramine.conf \ $(TEST_DATA) \ $(INSTALL_DIR)/conf/server.crt \ premain-libos - graphene-sgx-sign \ + @test -s $(SGX_SIGNER_KEY) || \ + { echo "SGX signer private key was not found, please specify SGX_SIGNER_KEY!"; exit 1; } + gramine-sgx-sign \ --key $(SGX_SIGNER_KEY) \ --manifest $< \ --output $@ @@ -92,22 +89,19 @@ nginx.manifest.sgx: nginx.manifest $(INSTALL_DIR)/sbin/nginx \ nginx.sig: nginx.manifest.sgx nginx.token: nginx.sig - graphene-sgx-get-token --output $@ --sig $< + gramine-sgx-get-token --output $@ --sig $< # Nginx configuration and test data -# .PHONY: config -config: $(INSTALL_DIR)/conf/nginx-graphene.conf +config: $(INSTALL_DIR)/conf/nginx-gramine.conf -$(INSTALL_DIR)/conf/nginx-graphene.conf: nginx-graphene.conf.template $(INSTALL_DIR)/sbin/nginx +$(INSTALL_DIR)/conf/nginx-gramine.conf: nginx-gramine.conf.template $(INSTALL_DIR)/sbin/nginx sed -e 's|$$(LISTEN_PORT)|'"$(LISTEN_PORT)"'|g' \ -e 's|$$(LISTEN_SSL_PORT)|'"$(LISTEN_SSL_PORT)"'|g' \ -e 's|$$(LISTEN_HOST)|'"$(LISTEN_HOST)"'|g' \ $< > $@ -# HTTP docs: -# Generating random HTML files in $(INSTALL_DIR)/html/random - +# HTTP docs: Generating random HTML files in $(INSTALL_DIR)/html/random RANDOM_DIR = $(INSTALL_DIR)/html/random RANDOM_FILES = \ $(foreach n,1 2 3 4 5 6 7 8 9 10,2K.$n.html) \ @@ -129,28 +123,25 @@ $(RANDOM_DIR)/%.html: $(INSTALL_DIR)/sbin/nginx testdata: $(TEST_DATA) # SSL data: key and x.509 self-signed certificate (to test SSL/TLS) - $(INSTALL_DIR)/conf/server.crt: $(INSTALL_DIR)/sbin/nginx # MARBLERUN: removed because certificate is provisioned by the Coordinator .PHONY: ssldata ssldata: $(INSTALL_DIR)/conf/server.crt -# Targets to run Nginx - .PHONY: start-native-server start-native-server: all - $(INSTALL_DIR)/sbin/nginx -c conf/nginx-graphene.conf + $(INSTALL_DIR)/sbin/nginx -c conf/nginx-gramine.conf ifeq ($(SGX),) -GRAPHENE = graphene-direct +GRAMINE = gramine-direct else -GRAPHENE = graphene-sgx +GRAMINE = gramine-sgx endif -.PHONY: start-graphene-server -start-graphene-server: all - $(GRAPHENE) ./nginx -c conf/nginx-graphene.conf +.PHONY: start-gramine-server +start-gramine-server: all + $(GRAMINE) ./nginx -c conf/nginx-gramine.conf .PHONY: clean clean: diff --git a/samples/gramine-nginx/README.md b/samples/gramine-nginx/README.md new file mode 100644 index 00000000..c19d317b --- /dev/null +++ b/samples/gramine-nginx/README.md @@ -0,0 +1,37 @@ +# Gramine nginx example +This example is a slightly modified variant of the [Gramine nginx example](https://github.com/gramineproject/gramine/tree/master/CI-Examples/nginx). These changes are required to run it with MarbleRun. + +*Prerequisite*: Gramine is installed on [release v1.0](https://github.com/gramineproject/gramine/releases/tag/v1.0) and the original nginx example is working. You will need hardware with Intel SGX support, and the Coordinator must not run in simulation mode. + +To marbleize the example we edited [nginx.manifest.template](nginx.manifest.template). See comments starting with `MARBLERUN` for explanations of the required changes. + +We also removed certificate generation from the Makefile because it will be provisioned by the Coordinator. See [manifest.json](manifest.json) on how this is specified. + +We now build the example as follows: +```sh +openssl genrsa -3 -out enclave-key.pem 3072 +make SGX=1 +``` + +Start the Coordinator in a SGX enclave: +```sh +erthost ../../build/coordinator-enclave.signed +``` + +The Coordinator exposes two APIs, a client REST API (port 4433) and a mesh API (port 2001). While the Coordinator and your Marble communicate via the mesh API, you can administrate the Coordinator via the REST API. + +Once the Coordinator instance is running, you can upload the manifest to the Coordinator's client API: +``` +curl -k --data-binary @manifest.json https://localhost:4433/manifest +``` + +The type of the Marble is defined in the `manifest.json`. In this example, the manifest defines a single Marble, which is called "frontend". To run the application, you need to set the `EDG_MARBLE_TYPE` environement variable to that name. + +```sh +EDG_MARBLE_TYPE=frontend gramine-sgx nginx +``` + +From a new terminal, check if nginx is running properly: +```sh +curl -k https://localhost:8443 +``` diff --git a/samples/graphene-nginx/manifest.json b/samples/gramine-nginx/manifest.json similarity index 95% rename from samples/graphene-nginx/manifest.json rename to samples/gramine-nginx/manifest.json index 9ece6767..93ab2639 100644 --- a/samples/graphene-nginx/manifest.json +++ b/samples/gramine-nginx/manifest.json @@ -11,7 +11,7 @@ "Argv": [ "./nginx", "-c", - "conf/nginx-graphene.conf" + "conf/nginx-gramine.conf" ], "Files": { "/dev/attestation/protected_files_key": "{{ hex .Secrets.ProtectedFilesKey }}", @@ -39,4 +39,4 @@ "Size": 128 } } -} +} \ No newline at end of file diff --git a/samples/graphene-nginx/nginx-graphene.conf.template b/samples/gramine-nginx/nginx-gramine.conf.template similarity index 95% rename from samples/graphene-nginx/nginx-graphene.conf.template rename to samples/gramine-nginx/nginx-gramine.conf.template index f74ca632..5716542a 100644 --- a/samples/graphene-nginx/nginx-graphene.conf.template +++ b/samples/gramine-nginx/nginx-gramine.conf.template @@ -3,7 +3,7 @@ # The following changes are made: # - Number of worker processes in increased from 1 to 4 # - Number of worker connections is decrease from 1024 to 768 (because Linux by default -# limits FDs to 1024, and Graphene uses ~100 FDs for its own purposes, so we are left with +# limits FDs to 1024, and Gramine uses ~100 FDs for its own purposes, so we are left with # about 900 FDs available for Nginx application itself) # - Listening port is changed from 80 to LISTEN_PORT # - Listening host is changed from localhost to LISTEN_HOST diff --git a/samples/gramine-nginx/nginx.manifest.template b/samples/gramine-nginx/nginx.manifest.template new file mode 100644 index 00000000..79292e2e --- /dev/null +++ b/samples/gramine-nginx/nginx.manifest.template @@ -0,0 +1,84 @@ +# Nginx manifest example + +# MARBLERUN: entrypoint must be premain-libos +libos.entrypoint = "premain-libos" + +loader.preload = "file:{{ gramine.libos }}" +loader.log_level = "{{ log_level }}" +loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/local/lib:/usr/lib:/usr/{{ arch_libdir }}" + +sys.enable_sigterm_injection = true + +fs.mount.lib.type = "chroot" +fs.mount.lib.path = "/lib" +fs.mount.lib.uri = "file:{{ gramine.runtimedir() }}" + +fs.mount.lib2.type = "chroot" +fs.mount.lib2.path = "{{ arch_libdir }}" +fs.mount.lib2.uri = "file:{{ arch_libdir }}" + +fs.mount.lib3.type = "chroot" +fs.mount.lib3.path = "/usr{{ arch_libdir }}" +fs.mount.lib3.uri = "file:/usr{{ arch_libdir }}" + +fs.mount.lib4.type = "chroot" +fs.mount.lib4.path = "/usr/local/lib" +fs.mount.lib4.uri = "file:/usr/local/lib" + +fs.mount.etc.type = "chroot" +fs.mount.etc.path = "/etc" +fs.mount.etc.uri = "file:/etc" + +fs.mount.cwd.type = "chroot" +fs.mount.cwd.path = "{{ install_dir_abspath }}" +fs.mount.cwd.uri = "file:{{ install_dir }}" + +sgx.debug = true +sgx.nonpie_binary = true +# MARBLERUN: enclave must have enough memory for Go runtime of premain +sgx.enclave_size = "1024M" +# MARBLERUN: enclave must have enough threads for Go runtime of premain +sgx.thread_num = 16 + +# Nginx benefits from Exitless. Uncomment the below line to use it. +#sgx.rpc_thread_num = 4 + +sgx.trusted_files = [ + "file:{{ install_dir }}/sbin/nginx", + "file:{{ install_dir }}/conf/", + "file:{{ install_dir }}/html/", + "file:{{ gramine.runtimedir() }}/", + "file:{{ arch_libdir }}/", + "file:/usr/{{ arch_libdir }}/", + "file:premain-libos" +] + +sgx.allowed_files = [ + "file:{{ install_dir }}/logs", + "file:/etc/nsswitch.conf", + "file:/etc/ethers", + "file:/etc/host.conf", + "file:/etc/hosts", + "file:/etc/group", + "file:/etc/passwd", + "file:/etc/resolv.conf", + "file:/etc/localtime", + "file:uuid" +] + +sgx.protected_files = [ + "file:install/conf/server.crt", + "file:install/conf/server.key", +] + +# MARBLERUN: argv0 must be the path to the actual application +loader.argv0_override = "{{ install_dir }}/sbin/nginx" + +# MARBLERUN: Forward EDG environment variables, used by MarbleRun +loader.env.EDG_MARBLE_TYPE = { passthrough = true } +loader.env.EDG_MARBLE_COORDINATOR_ADDR = { passthrough = true } +loader.env.EDG_MARBLE_UUID_FILE = { passthrough = true } +loader.env.EDG_MARBLE_DNS_NAMES = { passthrough = true } + +# MARBLERUN: enable DCAP +sgx.remote_attestation = true diff --git a/samples/graphene-redis/.gitignore b/samples/gramine-redis/.gitignore similarity index 100% rename from samples/graphene-redis/.gitignore rename to samples/gramine-redis/.gitignore diff --git a/samples/gramine-redis/Dockerfile b/samples/gramine-redis/Dockerfile new file mode 100644 index 00000000..806e26b7 --- /dev/null +++ b/samples/gramine-redis/Dockerfile @@ -0,0 +1,43 @@ +# syntax=docker/dockerfile:experimental + +FROM alpine/git:latest AS pull_marblerun +RUN git clone https://github.com/edgelesssys/marblerun.git /marblerun + +FROM alpine/git:latest AS pull_gramine +RUN git clone --branch v1.0 https://github.com/gramineproject/gramine /gramine + +FROM ghcr.io/edgelesssys/edgelessrt-dev AS build-premain +COPY --from=pull_marblerun /marblerun /premain +WORKDIR /premain/build +RUN cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. +RUN make premain-libos + +FROM ubuntu:20.04 +RUN apt update && \ + apt install -y libssl-dev gnupg software-properties-common + +RUN apt-key adv --fetch-keys https://packages.microsoft.com/keys/microsoft.asc && \ + apt-add-repository 'https://packages.microsoft.com/ubuntu/20.04/prod main' && \ + apt-key adv --fetch-keys https://packages.gramineproject.io/gramine.asc && \ + add-apt-repository 'deb [arch=amd64] https://packages.gramineproject.io/ stable main' && \ + apt-key adv --fetch-keys https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key && \ + add-apt-repository 'https://download.01.org/intel-sgx/sgx_repo/ubuntu main' + +RUN apt-get update && apt-get install -y \ + az-dcap-client \ + wget \ + libsgx-quote-ex-dev \ + libsgx-aesm-launch-plugin \ + build-essential \ + libprotobuf-c-dev \ + gramine-dcap && \ + apt-get clean -y && apt-get autoclean -y && apt-get autoremove -y + +COPY --from=pull_gramine /gramine /gramine +COPY --from=build-premain /premain/build/premain-libos /gramine/CI-Examples/redis/ +COPY --from=pull_marblerun /marblerun/samples/gramine-redis/redis-server.manifest.template /gramine/CI-Examples/redis/ +WORKDIR /gramine/CI-Examples/redis/ +ENV BUILD_TLS yes +RUN --mount=type=secret,id=signingkey,dst=/gramine/Pal/src/host/Linux-SGX/signer/enclave-key.pem,required=true \ + make clean && make SGX=1 +ENTRYPOINT ["gramine-sgx", "/gramine/CI-Examples/redis/redis-server" ] diff --git a/samples/graphene-redis/README.md b/samples/gramine-redis/README.md similarity index 78% rename from samples/graphene-redis/README.md rename to samples/gramine-redis/README.md index f585033e..6dda5d4d 100644 --- a/samples/graphene-redis/README.md +++ b/samples/gramine-redis/README.md @@ -1,10 +1,8 @@ -# Graphene Redis example +# Gramine Redis example -This example is a slightly modified variant of the [Graphene Redis example](https://github.com/oscarlab/graphene/tree/master/Examples/redis). +This example is a slightly modified variant of the [Gramine Redis example](https://github.com/gramineproject/gramine/tree/master/CI-Examples/redis). Instead of running a single [Redis](https://redis.io/) server instance, MarbleRun unleashes the full potential of Redis and takes care of distributing the Redis server in *replication* mode. -**Warning**: This sample enables `loader.insecure__use_host_env` in [redis-server.manifest.template](redis-server.manifest.template). Don't use this on production until [secure forwarding of host environment variables](https://github.com/oscarlab/graphene/issues/2356) will be available. - *Prerequisite:* * Ensure you have access to a Kubernetes cluster with SGX-enabled nodes and kubectl installed and configured. Probably the easiest way to get started is to run Kubernetes on an [Azure Kubernetes Service (AKS)](https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-nodes-aks-get-started), which offers SGX-enabled nodes. * Ensure you have the [MarbleRun CLI](https://docs.edgeless.systems/marblerun/#/reference/cli) installed. @@ -42,25 +40,18 @@ First, we are installing MarbleRun on your cluster. marblerun status $MARBLERUN ``` -* Set the [manifest](redis-manifest.json) +* Set the [manifest](manifest.json) ```bash - marblerun manifest set redis-manifest.json $MARBLERUN + marblerun manifest set manifest.json $MARBLERUN ``` ### Step 2: Deploying Redis -* Create and add the `redis` namespace to MarbleRun - - ```bash - kubectl create namespace redis - marblerun namespace add redis - ``` - * Deploy Redis using helm ```bash - helm install -f ./kubernetes/values.yaml redis ./kubernetes -n redis + helm install -f ./kubernetes/values.yaml redis ./kubernetes --create-namespace -n redis ``` * Wait for the Redis server to start, this might take a moment. The output shoud look like this: @@ -93,7 +84,7 @@ You can now securely connect to the Redis server using the `redis-cli` and the M * Obtain the Coordinator's CA certificate ```bash - marblerun certificate root $MARBLERUN -o marblerun.crt + marblerun certificate chain $MARBLERUN -o marblerun.crt ``` * Connect via the Redis-CLI @@ -114,5 +105,5 @@ To marbleize the example we edited [redis-server.manifest.template](redis-server Build the Docker image: ```bash -docker buildx build --secret id=signingkey,src= --tag ghcr.io/edgelesssys/redis-graphene-marble -f ./Dockerfile . +docker buildx build --secret id=signingkey,src= --tag ghcr.io/edgelesssys/redis-gramine-marble -f ./Dockerfile . ``` diff --git a/samples/graphene-redis/kubernetes/Chart.yaml b/samples/gramine-redis/kubernetes/Chart.yaml similarity index 60% rename from samples/graphene-redis/kubernetes/Chart.yaml rename to samples/gramine-redis/kubernetes/Chart.yaml index a638a251..bf885279 100644 --- a/samples/graphene-redis/kubernetes/Chart.yaml +++ b/samples/gramine-redis/kubernetes/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: redis -description: redis running as a Graphene SGX Marble +description: redis running as a Gramine SGX Marble type: application version: 0.1.0 diff --git a/samples/graphene-redis/kubernetes/templates/redis.yaml b/samples/gramine-redis/kubernetes/templates/redis.yaml similarity index 89% rename from samples/graphene-redis/kubernetes/templates/redis.yaml rename to samples/gramine-redis/kubernetes/templates/redis.yaml index ef29e538..d77e84a0 100644 --- a/samples/graphene-redis/kubernetes/templates/redis.yaml +++ b/samples/gramine-redis/kubernetes/templates/redis.yaml @@ -31,13 +31,15 @@ spec: spec: serviceAccountName: redis containers: - - image: ghcr.io/edgelesssys/redis-graphene-marble:latest + - image: ghcr.io/edgelesssys/redis-gramine-marble:latest imagePullPolicy: {{ .Values.imagePullPolicy }} env: - name: SGX_AESM_ADDR value: "1" - name: EDG_MARBLE_UUID_FILE value: "uuid" + - name: EDG_MARBLE_TYPE + value: "redis-main" name: redis securityContext: privileged: true @@ -80,13 +82,15 @@ spec: spec: serviceAccountName: redis containers: - - image: ghcr.io/edgelesssys/redis-graphene-marble:latest + - image: ghcr.io/edgelesssys/redis-gramine-marble:latest imagePullPolicy: {{ .Values.imagePullPolicy }} env: - name: SGX_AESM_ADDR value: "1" - name: EDG_MARBLE_UUID_FILE value: "uuid" + - name: EDG_MARBLE_TYPE + value: "redis-replica" name: redis securityContext: privileged: true @@ -105,7 +109,7 @@ spec: apiVersion: v1 kind: Service metadata: - name: redis + name: redis-main namespace: redis spec: type: ClusterIP diff --git a/samples/graphene-redis/kubernetes/values.yaml b/samples/gramine-redis/kubernetes/values.yaml similarity index 56% rename from samples/graphene-redis/kubernetes/values.yaml rename to samples/gramine-redis/kubernetes/values.yaml index c058ed68..eeb338b3 100644 --- a/samples/graphene-redis/kubernetes/values.yaml +++ b/samples/gramine-redis/kubernetes/values.yaml @@ -1,4 +1,4 @@ imagePullSecrets: - name: regcred -imagePullPolicy: Always \ No newline at end of file +imagePullPolicy: IfNotPresent \ No newline at end of file diff --git a/samples/graphene-redis/redis-manifest.json b/samples/gramine-redis/manifest.json similarity index 96% rename from samples/graphene-redis/redis-manifest.json rename to samples/gramine-redis/manifest.json index 444f86b8..b980542b 100644 --- a/samples/graphene-redis/redis-manifest.json +++ b/samples/gramine-redis/manifest.json @@ -33,7 +33,7 @@ ], "Files": { "/dev/attestation/protected_files_key": "{{ hex .Secrets.ProtectedFilesKey }}", - "redis.conf": "bind 0.0.0.0\nprotected-mode no\nport 0\ntls-port 6379\ntls-cert-file redis.crt\ntls-key-file redis.key\ntls-ca-cert-file ca.crt\ntls-auth-clients no\ntls-replication yes\ntls-cluster yes\nsave ''\nreplicaof redis-main-0.redis 6379", + "redis.conf": "bind 0.0.0.0\nprotected-mode no\nport 0\ntls-port 6379\ntls-cert-file redis.crt\ntls-key-file redis.key\ntls-ca-cert-file ca.crt\ntls-auth-clients no\ntls-replication yes\ntls-cluster yes\nsave ''\nreplicaof redis-main.redis 6379", "redis.crt": "{{ pem .MarbleRun.MarbleCert.Cert }}", "redis.key": "{{ pem .MarbleRun.MarbleCert.Private }}", "ca.crt": "{{ pem .MarbleRun.RootCA.Cert }}" @@ -47,4 +47,4 @@ "Size": 128 } } -} +} \ No newline at end of file diff --git a/samples/gramine-redis/redis-server.manifest.template b/samples/gramine-redis/redis-server.manifest.template new file mode 100644 index 00000000..577845a6 --- /dev/null +++ b/samples/gramine-redis/redis-server.manifest.template @@ -0,0 +1,178 @@ +# Redis manifest file example + +################################## GRAMINE ################################### + +# LibOS layer library of Gramine. There is currently only one implementation, +# so it is always set to libsysdb.so. +loader.preload = "file:{{ gramine.libos }}" + +# MARBLERUN: entrypoint must be premain-libos +libos.entrypoint = "premain-libos" + +# Verbosity of Gramine debug log (none/error/warning/debug/trace/all). Note +# that GRAMINE_LOG_LEVEL macro is expanded in the Makefile as part of the +# building process: the default is "error" for non-debug builds, and "debug" +# for debug builds. +loader.log_level = "{{ log_level }}" + +################################# ARGUMENTS ################################### + +# MARBLERUN: argv0 must be the path to the actual application +loader.argv0_override = "redis-server" + +################################# ENV VARS #################################### + +# Specify paths to search for libraries. The usual LD_LIBRARY_PATH syntax +# applies. Paths must be in-Gramine visible paths, not host-OS paths (i.e., +# paths must be taken from fs.mount.xxx.path, not fs.mount.xxx.uri). +# +# In case of Redis: +# - /lib is searched for Glibc libraries (ld, libc, libpthread) +# - {{ arch_libdir }} is searched for Name Service Switch (NSS) libraries +loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}" + +# MARBLERUN: Forward EDG environment variables, used by MarbleRun +loader.env.EDG_MARBLE_TYPE = { passthrough = true } +loader.env.EDG_MARBLE_COORDINATOR_ADDR = { passthrough = true } +loader.env.EDG_MARBLE_UUID_FILE = { passthrough = true } +loader.env.EDG_MARBLE_DNS_NAMES = { passthrough = true } + +################################## SIGNALS #################################### + +# Allow for injecting SIGTERM signal from the host. +sys.enable_sigterm_injection = true + +################################# MOUNT FS ################################### + +# General notes: +# - There is only one supported type of mount points: 'chroot'. +# - Directory names are (somewhat confusingly) prepended by 'file:'. +# - Names of mount entries (lib, lib2, lib3) are irrelevant but must be unique. +# - In-Gramine visible path names may be arbitrary but we reuse host-OS URIs +# for simplicity (except for the first 'lib' case). + +# Mount host-OS directory to Gramine glibc/runtime libraries (in 'uri') into +# in-Gramine visible directory /lib (in 'path'). +fs.mount.lib.type = "chroot" +fs.mount.lib.path = "/lib" +fs.mount.lib.uri = "file:{{ gramine.runtimedir() }}" + +# Mount host-OS directory to Name Service Switch (NSS) libraries (in 'uri') +# into in-Gramine visible directory /lib/x86_64-linux-gnu (in 'path'). +fs.mount.lib2.type = "chroot" +fs.mount.lib2.path = "{{ arch_libdir }}" +fs.mount.lib2.uri = "file:{{ arch_libdir }}" + +fs.mount.lib3.type = "chroot" +fs.mount.lib3.path = "/usr{{ arch_libdir }}" +fs.mount.lib3.uri = "file:/usr{{ arch_libdir }}" + +# Mount host-OS directory to NSS files required by Glibc + NSS libs (in 'uri') +# into in-Gramine visible directory /etc (in 'path'). +fs.mount.etc.type = "chroot" +fs.mount.etc.path = "/etc" +fs.mount.etc.uri = "file:/etc" + +############################### SGX: GENERAL ################################## + +# Create a debug SGX enclave (with SIGSTRUCT.ATTRIBUTES.DEBUG bit set to 1). +# This allows to debug Gramine with the application using GDB, read perf +# counters and enable SGX statistics. Note that this option is *insecure*! +sgx.debug = true + +# Set enclave size (somewhat arbitrarily) to 1024MB. Recall that SGX v1 requires +# to specify enclave size at enclave creation time. If Redis exhausts these +# 1024MB then it will start failing with random errors. Greater enclave sizes +# result in longer startup times, smaller enclave sizes are not enough for +# typical Redis workloads. +sgx.enclave_size = "1024M" + +# Set maximum number of in-enclave threads (somewhat arbitrarily) to 8. Recall +# that SGX v1 requires to specify the maximum number of simulteneous threads at +# enclave creation time. +# +# Note that internally Gramine may spawn two additional threads, one for IPC +# and one for asynchronous events/alarms. Redis is technically single-threaded +# but spawns couple additional threads to do background bookkeeping. Therefore, +# specifying '8' allows to run a maximum of 6 Redis threads which is enough. +# MARBLERUN: enclave must have enough threads for Go runtime of premain +sgx.thread_num = 16 + +# Redis executable is typically a PIE (Position Independent Executable) on most +# modern OS distros (e.g., Ubuntu 18.04). However, on some OS distros (notably, +# CentOS), Redis executable is built as non-PIE. We mark Redis as a non-PIE +# binary for the SGX PAL unconditionally -- this makes it work on CentOS and +# doesn't hurt on Ubuntu. (Note that the Linux PAL correctly distinguishes +# between PIE and non-PIE binaries, but for SGX we need to prearrange enclave +# memory layout, hence the below option.) +sgx.nonpie_binary = true + +############################# SGX: TRUSTED FILES ############################### + +# Specify all files used by Redis and its dependencies (including all libraries +# which can be loaded at runtime via dlopen), as well as other static read-only +# files (like configuration files). +# +# The paths to files are host-OS paths. These files will be searched for in +# in-Gramine visible paths according to mount points above. +# +# As part of the build process, Gramine-SGX script (`gramine-sgx-sign`) finds +# each specified file, measures its hash, and adds it to the manifest entry for +# that file (converting each entry to a table with "uri" and "sha256" keys). +# Note that this happens on the developer machine or a build server. +# +# At runtime, during loading of each "trusted file", Gramine-SGX measures its +# hash and compares with the "sha256" value in the corresponding manifest entry. +# If hashes match, this file is trusted and allowed to be loaded and used. Note +# that this happens on the client machine. + +# MARBLERUN: must trust premain-libos +sgx.trusted_files = [ + "file:redis-server", + "file:{{ gramine.runtimedir() }}/", + "file:{{ arch_libdir }}/", + "file:/usr/{{ arch_libdir }}/", + "file:premain-libos" +] + +############################# SGX: PROTECTED FILES ############################### + +# MARBLERUN: these are provisioned by the Coordinator +sgx.protected_files = [ + "file:redis.conf", + "file:redis.crt", + "file:redis.key", + "file:ca.crt" +] + +############################# SGX: ALLOWED FILES ############################### + +# Specify all non-static files used by app. These files may be accessed by +# Gramine-SGX but their integrity is not verified (Gramine-SGX does not +# measure their hashes). This may pose a security risk! + +sgx.allowed_files = [ + # Name Service Switch (NSS) files. Glibc reads these files as part of name- + # service information gathering. For more info, see 'man nsswitch.conf'. + "file:/etc/nsswitch.conf", + "file:/etc/ethers", + "file:/etc/hosts", + "file:/etc/group", + "file:/etc/passwd", + "file:/etc/localtime", + "file:/etc/resolv.conf", + "file:/etc/host.conf", + + # getaddrinfo(3) configuration file. Glibc reads this file to correctly find + # network addresses. For more info, see 'man gai.conf'. + "file:/etc/gai.conf", + + # MARBLERUN: allow the marble's uuid file + "file:uuid" +] + +############################# SGX: Attestation ############################### + +sgx.remote_attestation = true +sgx.isvprodid = 13 +sgx.isvsvn = 1 diff --git a/samples/graphene-hello/README.md b/samples/graphene-hello/README.md deleted file mode 100644 index 93b5597d..00000000 --- a/samples/graphene-hello/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Graphene "Hello World!" example -This example shows how to run a [Graphene](https://github.com/oscarlab/graphene) application in MarbleRun. In essence, you have to add the `premain` process to the Graphene manifest. `premain` will contact the Coordinator, set up the environment, and run the actual application. See the commented [hello.manifest.template](hello.manifest.template) for details. - -## Requirements -First, install Graphene on [commit b37ac75](https://github.com/oscarlab/graphene/tree/b37ac75efec0c1183fd42340ce2d3e04dcfb3388). You can use either the [Building](https://graphene.readthedocs.io/en/latest/building.html) or [Cloud Deployment](https://graphene.readthedocs.io/en/latest/cloud-deployment.html) guide to build and initially setup Graphene. You will need hardware with Intel SGX support. - -Then, before you can run the example, make sure you got the prerequisites for ECDSA remote attestation installed on your system. You can collectively install them with the following command: -```sh -sudo apt install libsgx-quote-ex-dev -``` - -**Warning**: This sample enables `loader.insecure__use_host_env` in [hello.manifest.template](hello.manifest.template). For production consider hardcoding the Marble environment variables (see [Run](#run)) until [secure forwarding of host environment variables](https://github.com/oscarlab/graphene/issues/2356) will be available. - -## Build -You can build the example as follows: -```sh -export GRAPHENEDIR=[PATH To Your Graphene Folder] -make -``` -Then get `mr_enclave` from the build output and set it as `UniqueID` in `manifest.json`. - -## Run -Next, use the `erthost` command to start the Coordinator in a local enclave: -```sh -erthost ../../build/coordinator-enclave.signed -``` - -The Coordinator exposes two APIs, a client REST API (port 4433) and a mesh API (port 2001). While the Coordinator and your Marble communicate via the mesh API, you can administrate the Coordinator via the REST API. - -Once the Coordinator instance is running, you can upload the manifest to the Coordinator's client API: -``` -curl -k --data-binary @manifest.json https://localhost:4433/manifest -``` - -To run the application, you need to set some environment variables. The type of the Marble is defined in the `manifest.json`. In this example, the manifest defines a single Marble, which is called "hello". The Marble's DNS name and the Coordinator's address are used to establish a connection between the Coordinator's mesh API and the Marble. Further, the UUID file stores a unique ID that enables a restart of the application. - -```sh -EDG_MARBLE_TYPE=hello \ -EDG_MARBLE_COORDINATOR_ADDR=localhost:2001 \ -EDG_MARBLE_UUID_FILE=uuid \ -EDG_MARBLE_DNS_NAMES=localhost \ -make run -``` diff --git a/samples/graphene-hello/hello.manifest.template b/samples/graphene-hello/hello.manifest.template deleted file mode 100644 index dafe0623..00000000 --- a/samples/graphene-hello/hello.manifest.template +++ /dev/null @@ -1,48 +0,0 @@ -loader.preload = "file:{{ graphene.libos }}" -loader.env.LD_LIBRARY_PATH = "/lib" - -# entrypoint must be premain-libos -libos.entrypoint = "premain-libos" -sgx.trusted_files.premain = "file:premain-libos" - -# argv0 must be the path to the actual application -loader.argv0_override = "hello" - -# Forward environment variables from the host. Don't use this on production! -loader.insecure__use_host_env = 1 - -fs.mount.lib.type = "chroot" -fs.mount.lib.path = "/lib" -fs.mount.lib.uri = "file:{{ graphene.runtimedir() }}" - -sgx.trusted_files.ld = "file:{{ graphene.runtimedir() }}/ld-linux-x86-64.so.2" -sgx.trusted_files.libc = "file:{{ graphene.runtimedir() }}/libc.so.6" -sgx.trusted_files.pthread = "file:{{ graphene.runtimedir() }}/libpthread.so.0" -sgx.trusted_files.hello = "file:hello" - -# allow the marble's uuid file -sgx.allowed_files.uuid = "file:uuid" - -# enable DCAP -sgx.remote_attestation = 1 - -# enclave must have enough memory and threads -sgx.enclave_size = "1024M" -sgx.thread_num = 16 - -# the following is only required if you need DNS resolution -fs.mount.etc.type = "chroot" -fs.mount.etc.path = "/etc" -fs.mount.etc.uri = "file:/etc" -sgx.trusted_files.nss = "file:{{ graphene.runtimedir() }}/libnss_dns.so.2" -sgx.trusted_files.nss_files = "file:{{ graphene.runtimedir() }}/libnss_files.so.2" -sgx.trusted_files.resolv = "file:{{ graphene.runtimedir() }}/libresolv.so.2" -sgx.allowed_files.hosts = "file:/etc/hosts" -sgx.allowed_files.host_conf = "file:/etc/host.conf" -sgx.allowed_files.gai_conf = "file:/etc/gai.conf" -sgx.allowed_files.resolv_conf = "file:/etc/resolv.conf" -sgx.allowed_files.localtime = "file:/etc/localtime" - - -# Name Service Switch (NSS) files, see 'man nsswitch.conf' -sgx.allowed_files.nsswitch = "file:/etc/nsswitch.conf" diff --git a/samples/graphene-nginx/README.md b/samples/graphene-nginx/README.md deleted file mode 100644 index cdc2f6d1..00000000 --- a/samples/graphene-nginx/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Graphene nginx example -This example is a slightly modified variant of the [Graphene nginx example](https://github.com/oscarlab/graphene/tree/master/Examples/nginx). These changes are required to run it in MarbleRun. - -*Prerequisite*: Graphene is set up on [commit b37ac75](https://github.com/oscarlab/graphene/tree/b37ac75efec0c1183fd42340ce2d3e04dcfb3388) and the original nginx example is working. You will need hardware with Intel SGX support. - -To marbleize the example we edited [nginx.manifest.template](nginx.manifest.template). See comments starting with `MARBLERUN` for explanations of the required changes. - -**Warning**: This sample enables `loader.insecure__use_host_env` in [nginx.manifest.template](nginx.manifest.template). For production consider hardcoding the Marble environment variables (see below) until [secure forwarding of host environment variables](https://github.com/oscarlab/graphene/issues/2356) will be available. - -We also removed certificate generation from the Makefile because it will be provisioned by the Coordinator. See [manifest.json](manifest.json) on how this is specified. - -We now build the example as follows: -```sh -export GRAPHENEDIR=[PATH To Your Graphene Folder] -make SGX=1 -``` - -Start the Coordinator in a SGX enclave: -```sh -erthost ../../build/coordinator-enclave.signed -``` - -The Coordinator exposes two APIs, a client REST API (port 4433) and a mesh API (port 2001). While the Coordinator and your Marble communicate via the mesh API, you can administrate the Coordinator via the REST API. - -Once the Coordinator instance is running, you can upload the manifest to the Coordinator's client API: -``` -curl -k --data-binary @manifest.json https://localhost:4433/manifest -``` - -To run the application, you need to set some environment variables. The type of the Marble is defined in the `manifest.json`. In this example, the manifest defines a single Marble, which is called "frontend". The Marble's DNS name and the Coordinator's address are used to establish a connection between the Coordinator's mesh API and the Marble. Further, the UUID file stores a unique ID that enables a restart of the application. - -```sh -EDG_MARBLE_TYPE=frontend \ -EDG_MARBLE_COORDINATOR_ADDR=localhost:2001 \ -EDG_MARBLE_UUID_FILE=uuid \ -EDG_MARBLE_DNS_NAMES=localhost \ -graphene-sgx nginx -``` diff --git a/samples/graphene-nginx/nginx.manifest.template b/samples/graphene-nginx/nginx.manifest.template deleted file mode 100644 index 364f7a49..00000000 --- a/samples/graphene-nginx/nginx.manifest.template +++ /dev/null @@ -1,116 +0,0 @@ -# Nginx manifest example -# -# This manifest was prepared and tested on Ubuntu 18.04. - -# MARBLERUN: entrypoint must be premain-libos -libos.entrypoint = "premain-libos" - -# Path to the library OS -loader.preload = "file:{{ graphene.libos }}" - -# Graphene log level -loader.log_level = "{{ log_level }}" - -# Read application arguments directly from the command line. Don't use this on production! -loader.insecure__use_cmdline_argv = true - -# Environment variables -loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/local/lib:/usr/lib:/usr/{{ arch_libdir }}" - -# Allow for injecting SIGTERM signal from the host. -sys.enable_sigterm_injection = true - -# Mounted FSes. The following "chroot" FSes mount a part of the host FS into the -# guest. Other parts of the host FS will not be available in the guest. - -# Default glibc files, mounted from the Runtime directory in GRAPHENEDIR. -fs.mount.lib.type = "chroot" -fs.mount.lib.path = "/lib" -fs.mount.lib.uri = "file:{{ graphene.runtimedir() }}" - -# Host-level libraries (e.g., /lib/x86_64-linux-gnu) required by Nginx. -fs.mount.lib2.type = "chroot" -fs.mount.lib2.path = "{{ arch_libdir }}" -fs.mount.lib2.uri = "file:{{ arch_libdir }}" - -# Host-level libraries (e.g., /usr/lib/x86_64-linux-gnu) required by Nginx with SSL. -fs.mount.lib3.type = "chroot" -fs.mount.lib3.path = "/usr{{ arch_libdir }}" -fs.mount.lib3.uri = "file:/usr{{ arch_libdir }}" - -# Host-level libraries required by Nginx with SSL. -fs.mount.lib4.type = "chroot" -fs.mount.lib4.path = "/usr/local/lib" -fs.mount.lib4.uri = "file:/usr/local/lib" - -# Host-level directory to NSS files required by Glibc + NSS libs. -fs.mount.etc.type = "chroot" -fs.mount.etc.path = "/etc" -fs.mount.etc.uri = "file:/etc" - -# Mount the current working directory -fs.mount.cwd.type = "chroot" -fs.mount.cwd.path = "{{ install_dir_abspath }}" -fs.mount.cwd.uri = "file:{{ install_dir }}" - -# SGX general options - -# Set the virtual memory size of the SGX enclave. For SGX v1, the enclave -# size must be specified during signing. If Nginx needs more virtual memory -# than the enclave size, Graphene will not be able to allocate it. -# MARBLERUN: enclave must have enough memory for Go runtime of premain -sgx.enclave_size = "1024M" - -sgx.nonpie_binary = true - -# Set the maximum number of enclave threads. For SGX v1, the number of enclave -# TCSes must be specified during signing, so the application cannot use more -# threads than the number of TCSes. Note that Graphene also creates an internal -# thread for handling inter-process communication (IPC), and potentially another -# thread for asynchronous events. Therefore, the actual number of threads that -# the application can create is (sgx.thread_num - 2). -# -# We (somewhat arbitrarily) specify 4 threads since Nginx is single-threaded. -# MARBLERUN: enclave must have enough threads for Go runtime of premain -sgx.thread_num = 16 - -# Nginx benefits from Exitless. Uncomment the below line to use it. -#sgx.rpc_thread_num = 4 - - -# SGX trusted files -sgx.trusted_files.nginx = "file:{{ install_dir }}/sbin/nginx" -sgx.trusted_files.conf_dir = "file:{{ install_dir }}/conf/" -sgx.trusted_files.html_dir = "file:{{ install_dir }}/html/" -# MARBLERUN: these are provisioned by the Coordinator -sgx.protected_files.cert = "file:{{ install_dir }}/conf/server.crt" -sgx.protected_files.privkey = "file:{{ install_dir }}/conf/server.key" - -sgx.trusted_files.runtime = "file:{{ graphene.runtimedir() }}/" -sgx.trusted_files.arch_libdir = "file:{{ arch_libdir }}/" -sgx.trusted_files.usr_arch_libdir = "file:/usr/{{ arch_libdir }}/" - -# Nginx logs directory (untrusted and allowed, since log files are not security-critical) -sgx.allowed_files.logs = "file:{{ install_dir }}/logs" - -# Name Service Switch (NSS) files (Glibc reads these files) -sgx.allowed_files.nsswitch = "file:/etc/nsswitch.conf" -sgx.allowed_files.ethers = "file:/etc/ethers" -sgx.allowed_files.hosts = "file:/etc/hosts" -sgx.allowed_files.group = "file:/etc/group" -sgx.allowed_files.passwd = "file:/etc/passwd" - -# MARBLERUN: must trust premain-libos -sgx.trusted_files.premain = "file:premain-libos" - -# MARBLERUN: argv0 must be the path to the actual application -loader.argv0_override = "{{ install_dir }}/sbin/nginx" - -# MARBLERUN: Forward environment variables from the host. Don't use this on production! -loader.insecure__use_host_env = 1 - -# MARBLERUN: allow the marble's uuid file -sgx.allowed_files.uuid = "file:uuid" - -# MARBLERUN: enable DCAP -sgx.remote_attestation = 1 diff --git a/samples/graphene-redis/Dockerfile b/samples/graphene-redis/Dockerfile deleted file mode 100644 index 48693d9f..00000000 --- a/samples/graphene-redis/Dockerfile +++ /dev/null @@ -1,40 +0,0 @@ -# syntax=docker/dockerfile:experimental - -FROM alpine/git:latest AS pull -RUN git clone https://github.com/edgelesssys/marblerun.git /premain - -FROM ghcr.io/edgelesssys/edgelessrt-dev AS build-premain -COPY --from=pull /premain /premain -WORKDIR /premain/build -RUN cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. -RUN make premain-libos - -FROM ghcr.io/edgelesssys/edgelessrt-deploy:latest AS release -RUN apt-get update && apt-get install -y git meson build-essential autoconf gawk bison wget python3 libcurl4-openssl-dev \ - python3-protobuf libprotobuf-c-dev protobuf-c-compiler python3-pip software-properties-common python3-click python3-jinja2 -RUN wget -qO- https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | apt-key add -RUN add-apt-repository 'deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu bionic main' -RUN apt-get install -y libsgx-quote-ex-dev libsgx-aesm-launch-plugin -RUN python3 -m pip install "toml>=0.10" - -RUN git clone https://github.com/intel/SGXDataCenterAttestationPrimitives.git /SGXDriver -WORKDIR /SGXDriver -RUN git reset --hard a93785f7d66527aa3bd331ba77b7993f3f9c729b - - - -RUN git clone https://github.com/oscarlab/graphene.git /graphene -WORKDIR /graphene -RUN git reset --hard b37ac75efec0c1183fd42340ce2d3e04dcfb3388 -RUN make ISGX_DRIVER_PATH=/SGXDriver/driver/linux/ SGX=1 -RUN meson build -Ddirect=disabled -Dsgx=enabled -RUN ninja -C build -RUN ninja -C build install - -COPY --from=build-premain /premain/build/premain-libos /graphene/Examples/redis/ -COPY redis-server.manifest.template /graphene/Examples/redis/ -WORKDIR /graphene/Examples/redis -ENV BUILD_TLS yes -RUN --mount=type=secret,id=signingkey,dst=/graphene/Pal/src/host/Linux-SGX/signer/enclave-key.pem,required=true \ - make clean && make SGX=1 -ENTRYPOINT ["graphene-sgx", "/graphene/Examples/redis/redis-server" ] diff --git a/samples/graphene-redis/redis-server.manifest.template b/samples/graphene-redis/redis-server.manifest.template deleted file mode 100644 index 76b5e0d8..00000000 --- a/samples/graphene-redis/redis-server.manifest.template +++ /dev/null @@ -1,179 +0,0 @@ -# Redis manifest file example -# -# This manifest was prepared and tested on Ubuntu 16.04. - -################################## GRAPHENE ################################### - -# LibOS layer library of Graphene. There is currently only one implementation, -# so it is always set to libsysdb.so. Note that GRAPHENEDIR macro is expanded -# to relative path to Graphene repository in the Makefile as part of the -# build process. -loader.preload = "file:{{ graphene.libos }}" -# MARBLERUN: entrypoint must be premain-libos -libos.entrypoint = "premain-libos" - -# Verbosity of Graphene debug log (none/error/warning/debug/trace/all). Note -# that GRAPHENE_LOG_LEVEL macro is expanded in the Makefile as part of the -# building process: the default is "error" for non-debug builds, and "debug" -# for debug builds. -loader.log_level = "{{ log_level }}" - -################################# ARGUMENTS ################################### - -# Read application arguments directly from the command line. Don't use this on production! -loader.insecure__use_cmdline_argv = true -# MARBLERUN: argv0 must be the path to the actual application -loader.argv0_override = "redis-server" -# MARBLERUN: Forward environment variables from the host. Don't use this on production! -loader.insecure__use_host_env = 1 - -################################# ENV VARS #################################### - -# Specify paths to search for libraries. The usual LD_LIBRARY_PATH syntax -# applies. Paths must be in-Graphene visible paths, not host-OS paths (i.e., -# paths must be taken from fs.mount.xxx.path, not fs.mount.xxx.uri). -# -# In case of Redis: -# - /lib is searched for Glibc libraries (ld, libc, libpthread) -# - {{ arch_libdir }} is searched for Name Service Switch (NSS) libraries -loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}" - -################################## SIGNALS #################################### - -# Allow for injecting SIGTERM signal from the host. -sys.enable_sigterm_injection = true - -################################# MOUNT FS ################################### - -# General notes: -# - There is only one supported type of mount points: 'chroot'. -# - Directory names are (somewhat confusingly) prepended by 'file:'. -# - Names of mount entries (lib, lib2, lib3) are irrelevant but must be unique. -# - In-Graphene visible path names may be arbitrary but we reuse host-OS URIs -# for simplicity (except for the first 'lib' case). - -# Mount host-OS directory to Graphene glibc/runtime libraries (in 'uri') into -# in-Graphene visible directory /lib (in 'path'). Note that GRAPHENEDIR macro -# is expanded to relative path to Graphene repository in the Makefile as part -# of the build process. -fs.mount.lib.type = "chroot" -fs.mount.lib.path = "/lib" -fs.mount.lib.uri = "file:{{ graphene.runtimedir() }}" - -# Mount host-OS directory to Name Service Switch (NSS) libraries (in 'uri') -# into in-Graphene visible directory /lib/x86_64-linux-gnu (in 'path'). -fs.mount.lib2.type = "chroot" -fs.mount.lib2.path = "{{ arch_libdir }}" -fs.mount.lib2.uri = "file:{{ arch_libdir }}" - -fs.mount.lib3.type = "chroot" -fs.mount.lib3.path = "/usr/{{ arch_libdir }}" -fs.mount.lib3.uri = "file:/usr/{{ arch_libdir }}" - -# Mount host-OS directory to NSS files required by Glibc + NSS libs (in 'uri') -# into in-Graphene visible directory /etc (in 'path'). -fs.mount.etc.type = "chroot" -fs.mount.etc.path = "/etc" -fs.mount.etc.uri = "file:/etc" - -############################### SGX: GENERAL ################################## - -# Set enclave size (somewhat arbitrarily) to 1024MB. Recall that SGX v1 requires -# to specify enclave size at enclave creation time. If Redis exhausts these -# 1024MB then it will start failing with random errors. Greater enclave sizes -# result in longer startup times, smaller enclave sizes are not enough for -# typical Redis workloads. -# MARBLERUN: enclave must have enough memory for Go runtime of premain -sgx.enclave_size = "1024M" - -sgx.nonpie_binary = true - -# MARBLERUN: enable DCAP -sgx.remote_attestation = 1 - -# Set maximum number of in-enclave threads (somewhat arbitrarily) to 8. Recall -# that SGX v1 requires to specify the maximum number of simulteneous threads at -# enclave creation time. -# -# Note that internally Graphene may spawn two additional threads, one for IPC -# and one for asynchronous events/alarms. Redis is technically single-threaded -# but spawns couple additional threads to do background bookkeeping. Therefore, -# specifying '8' allows to run a maximum of 6 Redis threads which is enough. -# MARBLERUN: enclave must have enough threads for Go runtime of premain -sgx.thread_num = 16 - -############################# SGX: TRUSTED FILES ############################### - -# Specify all files used by Redis and its dependencies (including all -# libraries which can be loaded at runtime via dlopen). The paths to files -# are host-OS paths. These files will be searched for in in-Graphene visible -# paths according to mount points above. -# -# As part of the build process, Graphene-SGX script (`graphene-sgx-sign`) finds each -# specified file, measures its hash, and outputs the hash in auto-generated -# entry 'sgx.trusted_checksum.xxx' in auto-generated redis-server.manifest.sgx. -# Note that this happens on the developer machine or a build server. -# -# At runtime, during loading of each "trusted file", Graphene-SGX measures its hash -# and compares with the one specified in 'sgx.trusted_checksum.xxx'. If hashes -# match, this file is trusted and allowed to be loaded and used. Note that -# this happens on the client machine. - -sgx.trusted_files.redis-server = "file:redis-server" - -sgx.trusted_files.runtime = "file:{{ graphene.runtimedir() }}/" -sgx.trusted_files.arch_libdir = "file:{{ arch_libdir }}/" -sgx.trusted_files.usr_arch_libdir = "file:/usr/{{ arch_libdir }}/" - -# MARBLERUN: must trust premain-libos -sgx.trusted_files.premain = "file:premain-libos" - -# Trusted no-library files include configuration files, read-only files, and -# other static files. It is useful to specify such files here to make sure -# they are not maliciously modified (modifications will be detected as hash -# mismatch by Graphene-SGX). -# -# Redis by default does not use configuration files, so this section is empty. -# sgx.trusted_files.config = "file:" - -############################# SGX: PROTECTED FILES ############################### - -# MARBLERUN: these are provisioned by the Coordinator -sgx.protected_files.redis-conf = "file:redis.conf" -sgx.protected_files.redis-crt = "file:redis.crt" -sgx.protected_files.redis-key = "file:redis.key" -sgx.protected_files.redis-ca = "file:ca.crt" - - -############################# SGX: ALLOWED FILES ############################### - -# Specify all non-static files used by app. These files may be accessed by -# Graphene-SGX but their integrity is not verified (Graphene-SGX does not -# measure their hashes). This may pose a security risk! - -# MARBLERUN: allow the marble's uuid file -sgx.allowed_files.injected-uuid = "file:/redis-uid/uuid-file" -sgx.allowed_files.uuid = "file:uuid" - -# Name Service Switch (NSS) files. Glibc reads these files as part of name- -# service information gathering. For more info, see 'man nsswitch.conf'. -sgx.allowed_files.nsswitch = "file:/etc/nsswitch.conf" -sgx.allowed_files.ethers = "file:/etc/ethers" -sgx.allowed_files.hosts = "file:/etc/hosts" -sgx.allowed_files.group = "file:/etc/group" -sgx.allowed_files.passwd = "file:/etc/passwd" -# MARBLERUN: needed for DNS -sgx.allowed_files.host = "file:/etc/host.conf" -sgx.allowed_files.resolve = "file:/etc/resolv.conf" -sgx.allowed_files.time = "file:/etc/localtime" - -# MARBLERUN: allow aesm socket -sgx.allowed_files.aesm_socket = "file:/var/run/aesmd/aesm.socket" - -# getaddrinfo(3) configuration file. Glibc reads this file to correctly find -# network addresses. For more info, see 'man gai.conf'. -sgx.allowed_files.gaiconf = "file:/etc/gai.conf" - -############################# SGX: Attestation ############################### -sgx.isvprodid = 13 -sgx.isvsvn = 1