This repository has been archived by the owner on Nov 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support loading an enclave application at runtime.
This PR makes it possible to load the enclave application at runtime. When enabled, nitriding maintains an in-memory transparency log that allows users to verify the evolution of enclave applications. This fixes brave/nitriding-daemon#37.
- Loading branch information
1 parent
ed7746b
commit 6684a03
Showing
10 changed files
with
329 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"net/http" | ||
"os/exec" | ||
"time" | ||
) | ||
|
||
const ( | ||
appPath = "/tmp/enclave-application" // Where to store the enclave application. | ||
) | ||
|
||
// appRetriever implements an interface that retrieves an enclave application at | ||
// runtime. This allows us to retrieve the application via various mechanisms, | ||
// like a Web API or a Docker registry. | ||
type appRetriever interface { | ||
retrieve(*http.Server, chan []byte) error | ||
} | ||
|
||
// appLoader implements a mechanism that can retrieve and execute an enclave | ||
// application at runtime. | ||
type appLoader struct { | ||
srv *http.Server | ||
log transparencyLog | ||
app chan []byte | ||
appExited chan struct{} | ||
appRetriever | ||
} | ||
|
||
// newAppLoader returns a new appLoader object. | ||
func newAppLoader(srv *http.Server, r appRetriever) *appLoader { | ||
return &appLoader{ | ||
srv: srv, | ||
log: new(memLog), | ||
app: make(chan []byte), | ||
appExited: make(chan struct{}), | ||
appRetriever: r, | ||
} | ||
} | ||
|
||
// runCmd runs the enclave application. The function blocks for as long as the | ||
// application is running. | ||
func (l *appLoader) runCmd() { | ||
cmd := exec.Command(appPath) | ||
err := cmd.Run() | ||
elog.Printf("Enclave application exited: %v", err) | ||
l.appExited <- struct{}{} | ||
} | ||
|
||
// appendToLog appends the given digest to our append-only log. | ||
func (l *appLoader) appendToLog(app []byte) { | ||
l.log.append(newSHA256LogRecord(app)) | ||
} | ||
|
||
// start executes the enclave application. | ||
func (l *appLoader) start(stop chan struct{}) { | ||
var ( | ||
err error | ||
) | ||
elog.Println("Starting app loader event loop.") | ||
defer elog.Println("Stopping app loader event loop.") | ||
|
||
go l.retrieve(l.srv, l.app) | ||
go func() { | ||
for { | ||
select { | ||
case <-stop: | ||
return | ||
case <-l.appExited: | ||
time.Sleep(time.Second) | ||
elog.Println(l.log) | ||
go l.runCmd() | ||
|
||
case app := <-l.app: | ||
if err = writeToDisk(app); err != nil { | ||
log.Fatalf("Error writing enclave application to disk: %v", err) | ||
} | ||
l.appendToLog(app) | ||
go l.runCmd() | ||
} | ||
} | ||
}() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package main | ||
|
||
import ( | ||
"net/http" | ||
"testing" | ||
) | ||
|
||
type appRetrieverDummy struct{} | ||
|
||
func (d *appRetrieverDummy) retrieve(srv *http.Server, appChan chan []byte) error { | ||
//var once sync.Once | ||
go func(appChan chan []byte) { | ||
// once.Do() | ||
// sync.Once() | ||
appChan <- []byte("") // Dummy application. | ||
}(appChan) | ||
return nil | ||
} | ||
|
||
func TestStartStop(t *testing.T) { | ||
var ( | ||
loader = newAppLoader(nil, new(appRetrieverDummy)) | ||
stop = make(chan struct{}) | ||
) | ||
defer close(stop) | ||
loader.start(stop) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package main | ||
|
||
import ( | ||
"io" | ||
"net/http" | ||
|
||
"github.com/go-chi/chi/v5" | ||
) | ||
|
||
// appRetrieverViaWeb installs a PUT endpoint that is used to upload the enclave | ||
// application at runtime. | ||
type appRetrieverViaWeb struct{} | ||
|
||
func newAppRetrieverViaWeb() *appRetrieverViaWeb { | ||
return new(appRetrieverViaWeb) | ||
} | ||
|
||
func (l *appRetrieverViaWeb) retrieve(srv *http.Server, appChan chan []byte) error { | ||
srv.Handler.(*chi.Mux).Put(pathApp, func(w http.ResponseWriter, r *http.Request) { | ||
const maxAppLen = 1024 * 1024 * 50 // 50 MiB. | ||
|
||
app, err := io.ReadAll(newLimitReader(r.Body, maxAppLen)) | ||
if err != nil { | ||
http.Error(w, errFailedReqBody.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
elog.Printf("Received %d-byte enclave application.", len(app)) | ||
appChan <- app | ||
w.WriteHeader(http.StatusOK) | ||
}) | ||
elog.Printf("Installed HTTP handler %s to receive enclave application.", pathApp) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/sha256" | ||
"fmt" | ||
"sync" | ||
"time" | ||
) | ||
|
||
// transparencyLog implements an interface for an append-only data structure | ||
// that serves as a transparency log for enclave image IDs. | ||
type transparencyLog interface { | ||
append(*logRecord) error | ||
String() string // human-readable representation | ||
} | ||
|
||
// logRecord represents a single record in our transparency log. | ||
type logRecord struct { | ||
digestType byte | ||
digestSize byte | ||
digest []byte | ||
time time.Time | ||
} | ||
|
||
// newSHA256LogRecord creates a new logRecord that uses SHA-2-256 for the given | ||
// byte blob. | ||
func newSHA256LogRecord(blob []byte) *logRecord { | ||
digest := sha256.Sum256(blob) | ||
return &logRecord{ | ||
digestType: 0x12, // SHA-2-256. | ||
digestSize: 0x20, // 32 bytes, in the "variable integer" multiformat. | ||
digest: digest[:], | ||
time: time.Now(), | ||
} | ||
} | ||
|
||
// String returns a string representation of the log record. | ||
func (r *logRecord) String() string { | ||
return fmt.Sprintf("%s: %x (type=%x)\n", r.time.Format(time.RFC3339), r.digest, r.digestType) | ||
} | ||
|
||
// memLog implements a transparencyLog in memory. | ||
type memLog struct { | ||
sync.Mutex | ||
log []*logRecord | ||
} | ||
|
||
// append appends the given logRecord to the memory log. | ||
func (m *memLog) append(r *logRecord) error { | ||
m.Lock() | ||
defer m.Unlock() | ||
|
||
m.log = append(m.log, r) | ||
elog.Printf("Appended %s to transparency log of new size %d.", r, len(m.log)) | ||
return nil | ||
} | ||
|
||
// size returns the memory log's size. | ||
func (m *memLog) size() int { | ||
m.Lock() | ||
defer m.Unlock() | ||
|
||
return len(m.log) | ||
} | ||
|
||
// String returns a string representation of the memory log. | ||
func (m *memLog) String() string { | ||
m.Lock() | ||
defer m.Unlock() | ||
|
||
var s string | ||
for _, r := range m.log { | ||
s += r.String() | ||
} | ||
return s | ||
} |
Oops, something went wrong.