occlum.go 11.7 KB
Newer Older
S
stormgbs 已提交
1 2 3 4
package occlum

import (
	"context"
5
	"encoding/json"
S
stormgbs 已提交
6 7 8 9 10 11
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

12 13 14 15 16 17 18
	"github.com/BurntSushi/toml"
	shim_config "github.com/alibaba/inclavare-containers/shim/config"
	"github.com/alibaba/inclavare-containers/shim/runtime/carrier"
	carr_const "github.com/alibaba/inclavare-containers/shim/runtime/carrier/constants"
	"github.com/alibaba/inclavare-containers/shim/runtime/config"
	"github.com/alibaba/inclavare-containers/shim/runtime/utils"
	"github.com/alibaba/inclavare-containers/shim/runtime/v2/rune/constants"
S
stormgbs 已提交
19 20
	"github.com/containerd/containerd"
	"github.com/containerd/containerd/cio"
21
	"github.com/containerd/containerd/cmd/ctr/commands"
S
stormgbs 已提交
22 23 24 25 26 27
	"github.com/containerd/containerd/runtime/v2/task"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/sirupsen/logrus"
)

const (
28
	defaultNamespace         = "k8s.io"
29
	startScriptFileName      = "start.sh"
30 31 32
	replaceOcclumImageScript = "replace_occlum_image.sh"
	carrierScriptFileName    = "carrier.sh"
	rootfsDirName            = "rootfs"
33
	dataDirName              = "data"
S
stormgbs 已提交
34 35 36 37
)

var _ carrier.Carrier = &occlum{}

38 39 40 41 42 43
type occlumBuildTask struct {
	client    *containerd.Client
	container *containerd.Container
	task      *containerd.Task
}

S
stormgbs 已提交
44 45 46 47 48 49
type occlum struct {
	context       context.Context
	bundle        string
	workDirectory string
	entryPoints   []string
	configPath    string
50
	task          *occlumBuildTask
S
stormgbs 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
	spec          *specs.Spec
	shimConfig    *shim_config.Config
}

// NewOcclumCarrier returns an carrier instance of occlum.
func NewOcclumCarrier(ctx context.Context, bundle string) (carrier.Carrier, error) {
	var cfg shim_config.Config
	if _, err := toml.DecodeFile(constants.ConfigurationPath, &cfg); err != nil {
		return nil, err
	}
	setLogLevel(cfg.LogLevel)
	return &occlum{
		context:    ctx,
		bundle:     bundle,
		shimConfig: &cfg,
66
		task:       &occlumBuildTask{},
S
stormgbs 已提交
67 68 69 70 71 72 73 74 75 76 77
	}, nil
}

// Name impl Carrier.
func (c *occlum) Name() string {
	return "occlum"
}

// BuildUnsignedEnclave impl Carrier.
func (c *occlum) BuildUnsignedEnclave(req *task.CreateTaskRequest, args *carrier.BuildUnsignedEnclaveArgs) (
	unsignedEnclave string, err error) {
78 79
	timeStart := time.Now()
	ts := timeStart
S
stormgbs 已提交
80 81 82 83 84
	// Initialize environment variables for occlum in config.json
	if err := c.initBundleConfig(); err != nil {
		return "", err
	}

85 86 87 88
	// Copy the script files that are used to build encalve.so by occlum into rootfs
	rootfsDir := filepath.Join(req.Bundle, rootfsDirName)
	dataDir := filepath.Join(req.Bundle, dataDirName)
	os.MkdirAll(dataDir, 0755)
S
stormgbs 已提交
89

90
	replaceImagesScript := filepath.Join(dataDir, replaceOcclumImageScript)
91
	if err := ioutil.WriteFile(replaceImagesScript, []byte(carr_const.ReplaceOcclumImageScript), os.ModePerm); err != nil {
S
stormgbs 已提交
92 93 94
		return "", err
	}

95
	carrierScript := filepath.Join(dataDir, carrierScriptFileName)
96 97 98 99
	if err := ioutil.WriteFile(carrierScript, []byte(carr_const.CarrierScript), os.ModePerm); err != nil {
		return "", err
	}

100 101 102
	// Execute the carrier script to generate the unsigned enclave.so in rootfs
	cmdArgs := []string{
		"/bin/bash", filepath.Join(dataDir, carrierScriptFileName),
103 104 105
		"--action", "buildUnsignedEnclave",
		"--entry_point", c.entryPoints[0],
		"--work_dir", c.workDirectory,
106
		"--rootfs", rootfsDir,
S
stormgbs 已提交
107
	}
108
	var occlumConfigPath string
109
	if c.configPath != "" {
110
		occlumConfigPath = filepath.Join(rootfsDir, c.configPath)
111 112
	} else {
		c.configPath = "Occlum.json"
113 114
		occlumConfigPath = filepath.Join(dataDir, c.configPath)
		if err := c.saveOcclumConfig(occlumConfigPath); err != nil {
115 116
			return "", err
		}
S
stormgbs 已提交
117
	}
118
	logrus.Debugf("BuildUnsignedEnclave: command: %v", cmdArgs)
119
	timeStart = time.Now()
120 121 122
	cmdArgs = append(cmdArgs, "--occlum_config_path", occlumConfigPath)
	if _, err := utils.ExecCommand("/bin/bash", cmdArgs...); err != nil {
		logrus.Errorf("BuildUnsignedEnclave: execute command failed. error: %++v", err)
123 124
		return "", err
	}
125
	logrus.Debugf("BuildUnsignedEnclave: init and build enclave time cost: %d", (time.Now().Sub(timeStart))/time.Second)
126
	enclavePath := filepath.Join(rootfsDir, c.workDirectory, "./build/lib/libocclum-libos.so")
127
	logrus.Debugf("BuildUnsignedEnclave: total time cost: %d", (time.Now().Sub(ts))/time.Second)
128

S
stormgbs 已提交
129 130 131 132 133 134
	return enclavePath, nil
}

// GenerateSigningMaterial impl Carrier.
func (c *occlum) GenerateSigningMaterial(req *task.CreateTaskRequest, args *carrier.CommonArgs) (
	signingMaterial string, err error) {
135
	timeStart := time.Now()
136 137 138 139 140 141
	rootfsDir := filepath.Join(req.Bundle, rootfsDirName)
	dataDir := filepath.Join(req.Bundle, dataDirName)
	signingMaterial = filepath.Join(rootfsDir, c.workDirectory, "enclave_sig.dat")
	args.Config = filepath.Join(rootfsDir, c.workDirectory, "Enclave.xml")
	cmdArgs := []string{
		filepath.Join(dataDir, carrierScriptFileName),
142 143 144 145
		"--action", "generateSigningMaterial",
		"--enclave_config_path", args.Config,
		"--unsigned_encalve_path", args.Enclave,
		"--unsigned_material_path", signingMaterial,
S
stormgbs 已提交
146
	}
147 148 149 150
	logrus.Debugf("GenerateSigningMaterial: sgx_sign gendata command: %v", cmdArgs)
	//FIXME debug
	time.Sleep(time.Minute * 2)
	if _, err := utils.ExecCommand("/bin/bash", cmdArgs...); err != nil {
151 152
		logrus.Errorf("GenerateSigningMaterial: sgx_sign gendata failed. error: %++v", err)
		return "", err
S
stormgbs 已提交
153
	}
154
	logrus.Debugf("GenerateSigningMaterial: sgx_sign gendata successfully")
155
	logrus.Debugf("GenerateSigningMaterial: total time cost: %d", (time.Now().Sub(timeStart))/time.Second)
S
stormgbs 已提交
156 157 158 159 160 161
	return signingMaterial, nil
}

// CascadeEnclaveSignature impl Carrier.
func (c *occlum) CascadeEnclaveSignature(req *task.CreateTaskRequest, args *carrier.CascadeEnclaveSignatureArgs) (
	signedEnclave string, err error) {
162
	timeStart := time.Now()
163 164 165 166 167
	rootfsDir := filepath.Join(req.Bundle, rootfsDirName)
	dataDir := filepath.Join(req.Bundle, dataDirName)
	signedEnclave = filepath.Join(rootfsDir, c.workDirectory, "./build/lib/libocclum-libos.signed.so")
	cmdArgs := []string{
		filepath.Join(dataDir, carrierScriptFileName),
168 169 170 171 172
		"--action", "cascadeEnclaveSignature",
		"--enclave_config_path", args.Config,
		"--unsigned_encalve_path", args.Enclave,
		"--unsigned_material_path", args.SigningMaterial,
		"--signed_enclave_path", signedEnclave,
173 174
		"--public_key_path", args.Key,
		"--signature_path", args.Signature,
S
stormgbs 已提交
175
	}
176 177
	logrus.Debugf("CascadeEnclaveSignature: sgx_sign catsig command: %v", cmdArgs)
	if _, err := utils.ExecCommand("/bin/bash", cmdArgs...); err != nil {
178 179 180 181
		logrus.Errorf("CascadeEnclaveSignature: sgx_sign catsig failed. error: %++v", err)
		return "", err
	}
	logrus.Debugf("CascadeEnclaveSignature: sgx_sign catsig successfully")
182
	logrus.Debugf("CascadeEnclaveSignature: total time cost: %d", (time.Now().Sub(timeStart))/time.Second)
S
stormgbs 已提交
183 184 185 186 187
	return signedEnclave, nil
}

// Cleanup impl Carrier.
func (c *occlum) Cleanup() error {
188
	/*timeStart := time.Now()
189
	ts := timeStart
190 191 192 193
	defer func() {
		if c.task.client != nil {
			c.task.client.Close()
		}
194
		logrus.Debugf("Cleanup: total time cost: %d", (time.Now().Sub(ts))/time.Second)
195 196 197
	}()
	defer func() {
		if c.task.container != nil {
198
			timeStart = time.Now()
199 200 201 202 203
			container := *c.task.container
			if err := container.Delete(c.context, containerd.WithSnapshotCleanup); err != nil {
				logrus.Errorf("Cleanup: delete container %s failed. err: %++v", container.ID(), err)
			}
			logrus.Debugf("Cleanup: delete container %s successfully.", container.ID())
204
			logrus.Debugf("Cleanup: delete occlum SDK container time cost: %d", (time.Now().Sub(ts))/time.Second)
205 206 207 208 209 210
		}
	}()

	if c.task.task == nil {
		return nil
	}
211
	timeStart = time.Now()
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
	t := *c.task.task
	if err := t.Kill(c.context, syscall.SIGTERM); err != nil {
		logrus.Errorf("Cleanup: kill task %s failed. err: %++v", t.ID(), err)
		return err
	}
	for {
		status, err := t.Status(c.context)
		if err != nil {
			logrus.Errorf("Cleanup: get task %s status failed. error: %++v", t.ID(), err)
			return err
		}
		if status.ExitStatus != 0 {
			logrus.Errorf("Cleanup: task %s exit abnormally. exit code: %d, task status: %s", t.ID(),
				status.ExitStatus, status.Status)
			return fmt.Errorf("task  %s exit abnormally. exit code: %d, task status: %s",
				t.ID(), status.ExitStatus, status.Status)
		}
		if status.Status != containerd.Stopped {
			logrus.Debugf("Cleanup: task %s status: %s", t.ID(), status.Status)
			time.Sleep(time.Second)
			continue
		}
		break
	}
236 237
	logrus.Debugf("Cleanup: kill occlum SDK container task time cost: %d", (time.Now().Sub(timeStart))/time.Second)
	timeStart = time.Now()
238 239 240 241
	if _, err := t.Delete(c.context); err != nil {
		logrus.Errorf("Cleanup: delete task %s failed. error: %++v", t.ID(), err)
		return err
	}
242
	logrus.Debugf("Cleanup: delete occlum SDK container task time cost: %d", (time.Now().Sub(timeStart))/time.Second)
243
	logrus.Debugf("Cleanup: clean occlum container and task successfully")*/
S
stormgbs 已提交
244 245 246 247 248 249 250 251 252 253 254
	return nil
}

func (c *occlum) initBundleConfig() error {
	configPath := filepath.Join(c.bundle, "config.json")
	spec, err := config.LoadSpec(configPath)
	if err != nil {
		return err
	}
	c.workDirectory = spec.Process.Cwd
	c.entryPoints = spec.Process.Args
255 256 257 258
	enclaveRuntimePath := c.shimConfig.EnclaveRuntime.Occlum.EnclaveRuntimePath
	if enclaveRuntimePath == "" {
		enclaveRuntimePath = fmt.Sprintf("%s/liberpal-occlum.so", c.workDirectory)
	}
S
stormgbs 已提交
259 260 261 262 263 264 265 266 267 268 269 270
	envs := map[string]string{
		carr_const.EnclaveRuntimePathKeyName: enclaveRuntimePath,
		carr_const.EnclaveTypeKeyName:        string(carr_const.IntelSGX),
		carr_const.EnclaveRuntimeArgsKeyName: carr_const.DefaultEnclaveRuntimeArgs,
	}
	c.spec = spec
	if err := config.UpdateEnvs(spec, envs, false); err != nil {
		return err
	}
	return config.SaveSpec(configPath, spec)
}

271 272 273 274 275 276
func (o *occlum) saveOcclumConfig(path string) error {
	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
		return err
	}
	cfg := GetDefaultOcclumConfig()
	cfg.ApplyEnvs(o.spec.Process.Env)
277
	cfg.ApplyEntrypoints([]string{o.entryPoints[0]})
278 279 280 281 282 283 284
	bytes, err := json.Marshal(cfg)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(path, bytes, 0644)
}

S
stormgbs 已提交
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
func createNamespaceIfNotExist(client *containerd.Client, namespace string) error {
	svc := client.NamespaceService()

	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second*60)
	defer cancel()
	nses, err := svc.List(ctx)
	if err != nil {
		return err
	}
	for _, ns := range nses {
		if ns == namespace {
			return nil
		}
	}

	return svc.Create(ctx, namespace, nil)
}

func setLogLevel(level string) {
	switch level {
	case "debug":
		logrus.SetLevel(logrus.DebugLevel)
	case "info":
		logrus.SetLevel(logrus.InfoLevel)
	case "warn":
		logrus.SetLevel(logrus.WarnLevel)
	case "error":
		logrus.SetLevel(logrus.ErrorLevel)
	default:
		logrus.SetLevel(logrus.InfoLevel)
	}
}
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364

func (c *occlum) execTask(args ...string) error {
	container := *c.task.container
	t := *c.task.task
	if container == nil || t == nil {
		return fmt.Errorf("task is not exist")
	}
	spec, err := container.Spec(c.context)
	if err != nil {
		logrus.Errorf("execTask: get container spec failed. error: %++v", err)
		return err
	}
	pspec := spec.Process
	pspec.Terminal = false
	pspec.Args = args

	cioOpts := []cio.Opt{cio.WithStdio, cio.WithFIFODir("/run/containerd/fifo")}
	ioCreator := cio.NewCreator(cioOpts...)
	process, err := t.Exec(c.context, utils.GenerateID(), pspec, ioCreator)
	if err != nil {
		logrus.Errorf("execTask: exec process in task failed. error: %++v", err)
		return err
	}
	defer process.Delete(c.context)
	statusC, err := process.Wait(c.context)
	if err != nil {
		return err
	}
	sigc := commands.ForwardAllSignals(c.context, process)
	defer commands.StopCatch(sigc)

	if err := process.Start(c.context); err != nil {
		logrus.Errorf("execTask: start process failed. error: %++v", err)
		return err
	}
	status := <-statusC
	code, _, err := status.Result()
	if err != nil {
		logrus.Errorf("execTask: exec process failed. error: %++v", err)
		return err
	}
	if code != 0 {
		return fmt.Errorf("process exit abnormaly. exitCode: %d, error: %++v", code, status.Error())
	}
	logrus.Debugf("execTask: exec successfully.")
	return nil
}