提交 8b7d5bd3 编写于 作者: Y YiLin.Li 提交者: jia zhang

rune: add attest command to support to remote attestation.

Signed-off-by: NYilin Li <YiLin.Li@linux.alibaba.com>
上级 2c5620e8
package main
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/opencontainers/runc/libcontainer"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/runc/libenclave/attestation/sgx"
_ "github.com/opencontainers/runc/libenclave/attestation/sgx/ias"
"github.com/opencontainers/runc/libenclave/intelsgx"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli"
)
const (
envSeparator = "="
)
var attestCommand = cli.Command{
Name: "attest",
Usage: "attest generates a remote attestation to the corresponding enclave container",
ArgsUsage: `<container-id> [command options]
Where "<container-id>" is the name for the instance of the container`,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "product",
Usage: "specify whether using production attestation service",
},
cli.StringFlag{
Name: "spid",
Usage: "specify SPID",
},
cli.StringFlag{
Name: "subscription-key, -key",
Usage: "specify the subscription key",
},
cli.BoolFlag{
Name: "linkable",
Usage: "specify the EPID signatures policy type",
},
},
Action: func(context *cli.Context) error {
if err := revisePidFile(context); err != nil {
return err
}
status, err := attestProcess(context)
if err == nil {
os.Exit(status)
}
return fmt.Errorf("attest failed: %v", err)
},
SkipArgReorder: true,
}
func attestProcess(context *cli.Context) (int, error) {
container, err := getContainer(context)
if err != nil {
return -1, err
}
status, err := container.Status()
if err != nil {
return -1, err
}
if status == libcontainer.Stopped {
return -1, fmt.Errorf("cannot attest a container that has stopped")
}
config := container.Config()
if config.Enclave == nil {
return -1, fmt.Errorf("Attest command: container.Config.Enclave is null")
}
state, err := container.State()
if err != nil {
return -1, err
}
bundle := utils.SearchLabels(state.Config.Labels, "bundle")
p, err := getAttestProcess(context, bundle)
if err != nil {
return -1, err
}
logLevel := "info"
if context.GlobalBool("debug") {
logLevel = "debug"
}
r := &runner{
enableSubreaper: false,
shouldDestroy: false,
container: container,
consoleSocket: context.String("console-socket"),
detach: false,
action: CT_ACT_RUN,
init: false,
preserveFDs: context.Int("preserve-fds"),
logLevel: logLevel,
}
return r.run(p)
}
func getAttestProcess(context *cli.Context, bundle string) (*specs.Process, error) {
// process via cli flags
if err := os.Chdir(bundle); err != nil {
return nil, err
}
spec, err := loadSpec(specConfig)
if err != nil {
return nil, err
}
p := spec.Process
p.Args = context.Args()[1:]
// override the cwd, if passed
if context.String("cwd") != "" {
p.Cwd = context.String("cwd")
}
if ap := context.String("apparmor"); ap != "" {
p.ApparmorProfile = ap
}
if l := context.String("process-label"); l != "" {
p.SelinuxLabel = l
}
if caps := context.StringSlice("cap"); len(caps) > 0 {
for _, c := range caps {
p.Capabilities.Bounding = append(p.Capabilities.Bounding, c)
p.Capabilities.Inheritable = append(p.Capabilities.Inheritable, c)
p.Capabilities.Effective = append(p.Capabilities.Effective, c)
p.Capabilities.Permitted = append(p.Capabilities.Permitted, c)
p.Capabilities.Ambient = append(p.Capabilities.Ambient, c)
}
}
// append the passed env variables
p.Env = append(p.Env, "SPID"+envSeparator+context.String("spid"))
p.Env = append(p.Env, "SUBSCRIPTION_KEY"+envSeparator+context.String("subscription-key"))
isProductEnclave := strconv.Itoa(int(sgx.DebugEnclave))
if context.Bool("product") {
isProductEnclave = strconv.Itoa(int(sgx.ProductEnclave))
}
p.Env = append(p.Env, "PRODUCT"+envSeparator+isProductEnclave)
quoteType := strconv.Itoa(int(intelsgx.QuoteSignatureTypeUnlinkable))
if context.Bool("linkable") {
quoteType = strconv.Itoa(int(intelsgx.QuoteSignatureTypeLinkable))
}
p.Env = append(p.Env, "QUOTE_TYPE"+envSeparator+quoteType)
var AttestCommand string = "true"
p.Env = append(p.Env, "AttestCommand"+envSeparator+AttestCommand)
// set the tty
p.Terminal = false
if context.IsSet("tty") {
p.Terminal = context.Bool("tty")
}
if context.IsSet("no-new-privs") {
p.NoNewPrivileges = context.Bool("no-new-privs")
}
// override the user, if passed
if context.String("user") != "" {
u := strings.SplitN(context.String("user"), ":", 2)
if len(u) > 1 {
gid, err := strconv.Atoi(u[1])
if err != nil {
return nil, fmt.Errorf("parsing %s as int for gid failed: %v", u[1], err)
}
p.User.GID = uint32(gid)
}
uid, err := strconv.Atoi(u[0])
if err != nil {
return nil, fmt.Errorf("parsing %s as int for uid failed: %v", u[0], err)
}
p.User.UID = uint32(uid)
}
for _, gid := range context.Int64Slice("additional-gids") {
if gid < 0 {
return nil, fmt.Errorf("additional-gids must be a positive number %d", gid)
}
p.User.AdditionalGids = append(p.User.AdditionalGids, uint32(gid))
}
return p, validateAttestProcessSpec(p)
}
......@@ -140,15 +140,7 @@ func handleRequest(conn net.Conn, id int) {
var err error
resp := &pb.AgentServiceResponse{}
resp.Exec = &pb.AgentServiceResponse_Execute{}
exitCode := int32(1)
defer func() {
resp.Exec.ExitCode = exitCode
if err != nil {
resp.Exec.Error = fmt.Sprint(err)
}
protoBufWrite(conn, resp)
}()
req := &pb.AgentServiceRequest{}
if err = protoBufRead(conn, req); err != nil {
......@@ -166,6 +158,32 @@ func handleRequest(conn net.Conn, id int) {
}
defer connFile.Close()
if req.Attest != nil {
logrus.Infof("In function handleRequest: get a attest request")
resp.Attest = &pb.AgentServiceResponse_Attest{}
err = enclaveRuntime.LaunchAttestation(req.Attest.Spid,
req.Attest.SubscriptionKey,
req.Attest.Product,
req.Attest.QuoteType)
if err != nil {
resp.Attest.Error = fmt.Sprint(err)
} else {
exitCode = 0
}
resp.Attest.ExitCode = exitCode
protoBufWrite(conn, resp)
return
}
resp.Exec = &pb.AgentServiceResponse_Execute{}
defer func() {
resp.Exec.ExitCode = exitCode
if err != nil {
resp.Exec.Error = fmt.Sprint(err)
}
protoBufWrite(conn, resp)
}()
// Retrieve signal pipe.
signalPipe, err := utils.RecvFd(connFile)
if err != nil {
......
......@@ -12,8 +12,16 @@ message AgentServiceRequest{
int32 sig = 1;
}
message Attest {
string spid = 1;
string subscriptionKey = 2;
uint32 product = 3;
uint32 quoteType = 4;
}
Execute exec = 1;
Kill kill = 2;
Attest attest = 3;
}
message AgentServiceResponse {
......@@ -22,5 +30,11 @@ message AgentServiceResponse {
string error = 2;
}
message Attest {
int32 exitCode = 1;
string error = 2;
}
Execute exec = 1;
Attest attest = 2;
}
......@@ -6,14 +6,17 @@ import (
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/runc/libenclave/attestation/sgx"
"github.com/opencontainers/runc/libenclave/configs"
"github.com/opencontainers/runc/libenclave/intelsgx"
"github.com/opencontainers/runc/libenclave/internal/runtime"
pb "github.com/opencontainers/runc/libenclave/proto"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"io"
"io/ioutil"
"net"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
)
......@@ -100,6 +103,18 @@ func StartInitialization(cmd []string, cfg *RuneletConfig) (exitCode int32, err
notifySignal := make(chan os.Signal, signalBufferSize)
if fifoFd == -1 {
AttestCommand := os.Getenv("AttestCommand")
if AttestCommand == "true" {
logrus.Infof("Get an attest command")
exitCode, err = remoteAttest(agentPipe, config, notifySignal)
if err != nil {
return exitCode, err
}
logrus.Debugf("remote attest normally exits")
return exitCode, err
} else {
logrus.Infof("Get an exec command")
exitCode, err = remoteExec(agentPipe, cmd, notifySignal)
if err != nil {
return exitCode, err
......@@ -108,6 +123,7 @@ func StartInitialization(cmd []string, cfg *RuneletConfig) (exitCode int32, err
return exitCode, err
}
}
notifyExit := make(chan struct{})
sigForwarderExit := forwardSignal(rt, notifySignal, notifyExit)
......@@ -201,6 +217,105 @@ func finalizeInitialization(fifoFd int) error {
return nil
}
func remoteAttest(agentPipe *os.File, config *configs.InitEnclaveConfig, notifySignal chan os.Signal) (exitCode int32, err error) {
logrus.Infof("preparing to remote Attest")
c, err := net.FileConn(agentPipe)
if err != nil {
return 1, err
}
defer c.Close()
conn, ok := c.(*net.UnixConn)
if !ok {
return 1, fmt.Errorf("casting to UnixConn faild")
}
sgxEnclaveType := os.Getenv("PRODUCT")
isProductEnclave, err := strconv.ParseUint(sgxEnclaveType, 10, 32)
if err != nil {
return 1, fmt.Errorf("Invalid sgxEnclaveType Configuration, error = %v!\n", err)
}
if isProductEnclave != sgx.DebugEnclave && isProductEnclave != sgx.ProductEnclave {
return 1, fmt.Errorf("Unsupported is_product_enclave Configuration %v!\n", sgxEnclaveType)
}
quoteType := os.Getenv("QUOTE_TYPE")
raEpidQuoteType, err := strconv.ParseUint(quoteType, 10, 32)
if err != nil {
return 1, fmt.Errorf("Invalid Quote Type Configuration, error = %v!\n", err)
}
if raEpidQuoteType != (intelsgx.QuoteSignatureTypeUnlinkable) && raEpidQuoteType != (intelsgx.QuoteSignatureTypeLinkable) {
return 1, fmt.Errorf("Unsupported Quote Type Configuration %v!\n", raEpidQuoteType)
}
req := &pb.AgentServiceRequest{}
req.Attest = &pb.AgentServiceRequest_Attest{
Spid: os.Getenv("SPID"),
SubscriptionKey: os.Getenv("SUBSCRIPTION_KEY"),
Product: (uint32)(isProductEnclave),
QuoteType: (uint32)(raEpidQuoteType),
}
if err = protoBufWrite(conn, req); err != nil {
return 1, err
}
agentFile, err := conn.File()
if err != nil {
return 1, err
}
defer agentFile.Close()
// Send signal notification pipe.
childSignalPipe, parentSignalPipe, err := os.Pipe()
agentFile, err = conn.File()
if err != nil {
return 1, err
}
defer agentFile.Close()
// Send signal notification pipe.
childSignalPipe, parentSignalPipe, err = os.Pipe()
if err != nil {
return 1, err
}
defer func() {
if err != nil {
childSignalPipe.Close()
}
parentSignalPipe.Close()
}()
if err = utils.SendFd(agentFile, childSignalPipe.Name(), childSignalPipe.Fd()); err != nil {
return 1, err
}
// Close the child signal pipe in parent side **after** sending all stdio fds to
// make sure the parent runelet has retrieved the child signal pipe.
childSignalPipe.Close()
signal.Notify(notifySignal)
notifyExit := make(chan struct{})
sigForwarderExit := forwardSignalToParent(parentSignalPipe, notifySignal, notifyExit)
resp := &pb.AgentServiceResponse{}
if err = protoBufRead(conn, resp); err != nil {
return 1, err
}
notifyExit <- struct{}{}
logrus.Debug("awaiting for signal forwarder exiting ...")
<-sigForwarderExit
logrus.Debug("signal forwarder exited")
if resp.Attest.Error == "" {
err = nil
} else {
err = fmt.Errorf(resp.Attest.Error)
}
return resp.Attest.ExitCode, err
}
func remoteExec(agentPipe *os.File, cmd []string, notifySignal chan os.Signal) (exitCode int32, err error) {
c := strings.Join(cmd, " ")
logrus.Debugf("preparing to remote exec %s", c)
......
......@@ -130,6 +130,7 @@ func main() {
startCommand,
stateCommand,
updateCommand,
attestCommand,
}
app.Before = func(context *cli.Context) error {
if !context.IsSet("root") && xdgRuntimeDir != "" {
......
......@@ -399,6 +399,19 @@ func validateProcessSpec(spec *specs.Process) error {
return nil
}
func validateAttestProcessSpec(spec *specs.Process) error {
if spec.Cwd == "" {
return fmt.Errorf("Cwd property must not be empty")
}
if !filepath.IsAbs(spec.Cwd) {
return fmt.Errorf("Cwd must be an absolute path")
}
if spec.SelinuxLabel != "" && !selinux.GetEnabled() {
return fmt.Errorf("selinux label is specified in config, but selinux is disabled or not supported")
}
return nil
}
type CtAct uint8
const (
......
......@@ -34,7 +34,7 @@ For example, generate the quote file according to the given local report file:
},
cli.BoolFlag{
Name: "linkable",
Usage: "linkable",
Usage: "specify the EPID signatures policy type",
},
},
Action: func(context *cli.Context) error {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册