Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vsock sample using Go #18

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions vsock_sample/go/Dockerfile.client
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM golang:1.19-alpine AS builder

WORKDIR /app

COPY go.mod ./
COPY go.sum ./
RUN go mod download

COPY *.go ./

RUN CGO_ENABLED=0 GOOS=linux go build -o vsock-sample .

FROM scratch

COPY --from=builder /app/vsock-sample .

ENTRYPOINT ["/vsock-sample", "client"]
17 changes: 17 additions & 0 deletions vsock_sample/go/Dockerfile.server
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM golang:1.19-alpine AS builder

WORKDIR /app

COPY go.mod ./
COPY go.sum ./
RUN go mod download

COPY *.go ./

RUN CGO_ENABLED=0 GOOS=linux go build -o vsock-sample .

FROM scratch

COPY --from=builder /app/vsock-sample .

CMD ["/vsock-sample", "server", "-port", "5005"]
114 changes: 114 additions & 0 deletions vsock_sample/go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Vsock Communication Sample

A hello-world example for Nitro Enclaves vsock server and client communication.

## Prerequisites

1. The `vsock-sample` is a Nitro Enclaves application that can be run either
as a server or a client. The client sends the “Hello, world!” message to the
server. The server receives the message and prints it to the standard output.

2. The sample is written and tested in Go 1.19. Any Go version 1.17+ should work with this example (as the vsock package dependency is written in 1.17).

## How to use the enclave as the server and the parent instance as the client

1. Build the Enclave Image File (EIF) starting from the `Dockerfile.server` file
in this directory. We chose to use a multi-stage build to build the Docker image
from *go-alpine* and then deploy the application using a *scratch* image to keep
the enclave image as small as possible, but you can also use other Docker images.

__Note__: You can use any other port number besides 5005 by modifying the command
inside the `Dockerfile.server` file.

```
docker build -t vsock-sample-server -f Dockerfile.server .
nitro-cli build-enclave --docker-uri vsock-sample-server --output-file vsock_sample_server.eif
```

2. Configure the pool of memory and vCPUs (the `nitro-cli-config` script can be used)
and run the enclave using the previously-built EIF.

```
// 1 vCPUs and 128 MiB memory
nitro-cli-config -t 1 -m 128
nitro-cli run-enclave --eif-path vsock_sample_server.eif --cpu-count 1 --memory 128 --debug-mode
```

3. Connect to the enclave console using `nitro-cli`.

```
nitro-cli console --enclave-id $ENCLAVE_ID
```

4. In another terminal, build and run the client.

```
docker build -t vsock-sample-client -f Dockerfile.client .
docker run -it --rm vsock-sample-client -cid $ENCLAVE_CID -port 5005
```

__Note__: Here `$ENCLAVE_CID` is a generated integer value (e.g. 16) of the enclave CID.

5. The enclave console output should look like this:

```
[ 0.051633] rtc-pl031 40002000.rtc: setting system clock to 2022-12-08 20:21:47 UTC (1670530907)
[ 0.052366] Freeing unused kernel memory: 512K
[ 0.056733] nsm: loading out-of-tree module taints kernel.
[ 0.057098] nsm: module verification failed: signature and/or required key missing - tainting kernel
[ 0.058013] NSM RNG: returning rand bytes = 16
[ 0.058493] NSM RNG: returning rand bytes = 128
[ 0.058803] random: fast init done
[ 0.059766] NSM RNG: returning rand bytes = 128
[ 0.060030] random: crng init done
Listening on :5005
2022/12/08 20:22:43 [INFO]: Hello, world!
```

## How to use the parent instance as the server and the enclave as the client

1. Build the Enclave Image File (EIF) starting from the `Dockerfile.client` file
in this directory. We chose to use a multi-stage build to build the Docker image
from *go-alpine* and then deploy the application using a *scratch* image to keep
the enclave image as small as possible, but you can also use other Docker images.

__Notes__:
* The value 3 is the CID of the parent instance
* You can use any other port number besides 5005 by modifying the command inside the Dockerfile.client file

```
docker build -t vsock-sample-client -f Dockerfile.client .
```

```
nitro-cli build-enclave --docker-uri vsock-sample-client --output-file vsock_sample_client.eif
```

2. Build and run the server inside the parent instance.

```
docker build -t vsock-sample-server -f Dockerfile.server .
docker run -itd --name vsock-sample-server vsock-sample-server
```

3. Configure the pool of memory and vCPUs (the `nitro-cli-config`
script can be used) and run the enclave using the built EIF.

```
// 2 vCPUs and 128 MiB memory
nitro-cli-config -t 1 -m 128
nitro-cli run-enclave --eif-path vsock_sample_client.eif --cpu-count 1 --memory 128 --debug-mode
```

4. The server container output should print __Hello, world!__.

```
docker logs vsock-sample-server
Listening on :5005
[INFO]: Hello, world!
```

__Note__:
* The client application inside the enclave sends the message once and then exits thus the enclave shall terminate.

Now you can replace the client/server code with your own code.
10 changes: 10 additions & 0 deletions vsock_sample/go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/aws-samples/aws-nitro-enclaves-samples/vsock_sample/go

go 1.19

require github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2

require (
github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb // indirect
golang.org/x/sys v0.2.0 // indirect
)
6 changes: 6 additions & 0 deletions vsock_sample/go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2 h1:DZMFueDbfz6PNc1GwDRA8+6lBx1TB9UnxDQliCqR73Y=
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2/go.mod h1:SWzULI85WerrFt3u+nIm5F9l7EvxZTKQvd0InF3nmgM=
github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb h1:CKWls8QOVQs/qmuUuGOeHMpIqSx6f9S72udJ48vEeKo=
github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
84 changes: 84 additions & 0 deletions vsock_sample/go/vsock-sample.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package main

import (
"bufio"
"flag"
"fmt"
"io"
"log"
"net"
"os"
"path/filepath"

"github.com/linuxkit/virtsock/pkg/vsock"
)

func main() {
var showVersion bool
flag.BoolVar(&showVersion, "version", false, "Prints version information.")

clientCmd := flag.NewFlagSet("client", flag.ExitOnError)
cCid := clientCmd.Int("cid", 3, "The remote endpoint CID.")
cPort := clientCmd.Int("port", 5005, "The remote endpoint port.")

serverCmd := flag.NewFlagSet("server", flag.ExitOnError)
sPort := serverCmd.Uint("port", 5005, "The local port to listen on.")

switch os.Args[1] {
case "client":
clientCmd.Parse(os.Args[2:])
fmt.Printf("CID:\t%d\nPort:\t%d\n", *cCid, *cPort)

conn, err := vsock.Dial(uint32(*cCid), uint32(*cPort))
if err != nil {
log.Fatalf("failed to connect: %v", err)
}
defer conn.Close()

message := "Hello, world!"
w := bufio.NewWriter(conn)
n, merr := w.WriteString(message)
if merr != nil {
log.Fatalf("failed to write message: %v", merr)
}
w.Flush()
fmt.Printf("Wrote %d bytes to connection: %s\n", n, message)

case "server":
serverCmd.Parse(os.Args[2:])

lis, err := vsock.Listen(vsock.CIDAny, uint32(*sPort))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
defer lis.Close()
fmt.Printf("Listening on :%d\n", *sPort)
for {
conn, err := lis.Accept()
if err != nil {
fmt.Println("Error accepting", err.Error())
os.Exit(1)
}
defer conn.Close()

go func(c net.Conn) {
r := bufio.NewReader(c)
data, err := r.ReadString('\n')
if err != nil && err != io.EOF {
log.Println("[ERROR]: couldn't read from connection:", err.Error())
return
}
log.Printf("[INFO]: %s\n", data)
}(conn)
}

default:
flag.Parse()

if showVersion {
fmt.Printf("%s 0.1.0\n", filepath.Base(os.Args[0]))
os.Exit(0)
}
}

}