Skip to content

Commit d309371

Browse files
committed
Adds jailer support to the runtime
This adds jailing to the runtime. Users are now able to enable jailing by providing a jailer config and setting the "runc_binary" in the firecracker json file. Signed-off-by: xibz <[email protected]>
1 parent aaf44f9 commit d309371

11 files changed

+943
-38
lines changed

runtime/config.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,17 @@ type Config struct {
4747
LogLevel string `json:"log_level"`
4848
HtEnabled bool `json:"ht_enabled"`
4949
Debug bool `json:"debug"`
50-
5150
// If a CreateVM call specifies no network interfaces and DefaultNetworkInterfaces is non-empty,
5251
// the VM will default to using the network interfaces as specified here. This is especially
5352
// useful when a CNI-based network interface is provided in DefaultNetworkInterfaces.
5453
DefaultNetworkInterfaces []proto.FirecrackerNetworkInterface `json:"default_network_interfaces"`
54+
JailerConfig JailerConfig `json:"jailer"`
55+
}
56+
57+
// JailerConfig houses a set of configurable values for jailing
58+
// TODO: Add netns field
59+
type JailerConfig struct {
60+
RuncBinaryPath string `json:"runc_binary_path"`
5561
}
5662

5763
// LoadConfig loads configuration from JSON file at 'path'
@@ -76,6 +82,7 @@ func LoadConfig(path string) (*Config, error) {
7682
CPUCount: defaultCPUCount,
7783
CPUTemplate: string(defaultCPUTemplate),
7884
}
85+
7986
if err := json.Unmarshal(data, cfg); err != nil {
8087
return nil, errors.Wrapf(err, "failed to unmarshal config from %q", path)
8188
}

runtime/drive_handler.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ type stubDriveHandler struct {
5151
mutex sync.Mutex
5252
}
5353

54-
func newStubDriveHandler(path string, logger *logrus.Entry, count int) (*stubDriveHandler, error) {
54+
// stubDrivesOpt is used to make and modify changes to the stub drives.
55+
type stubDrivesOpt func(stubDrives []models.Drive) error
56+
57+
func newStubDriveHandler(path string, logger *logrus.Entry, count int, opts ...stubDrivesOpt) (*stubDriveHandler, error) {
5558
h := stubDriveHandler{
5659
RootPath: path,
5760
logger: logger,
@@ -60,6 +63,13 @@ func newStubDriveHandler(path string, logger *logrus.Entry, count int) (*stubDri
6063
if err != nil {
6164
return nil, err
6265
}
66+
67+
for _, opt := range opts {
68+
if err := opt(drives); err != nil {
69+
h.logger.WithError(err).Debug("failed to apply option to stub drives")
70+
return nil, err
71+
}
72+
}
6373
h.drives = drives
6474
return &h, nil
6575
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
{
2+
"ociVersion": "1.0.1",
3+
"process": {
4+
"terminal": false,
5+
"user": {
6+
"uid": 0,
7+
"gid": 0
8+
},
9+
"args": [
10+
"/firecracker",
11+
"--api-sock",
12+
"api.socket"
13+
],
14+
"env": [
15+
"PATH=/"
16+
],
17+
"cwd": "/",
18+
"capabilities": {
19+
"effective": [
20+
],
21+
"bounding": [
22+
],
23+
"inheritable": [
24+
],
25+
"permitted": [
26+
],
27+
"ambient": [
28+
]
29+
},
30+
"rlimits": [
31+
{
32+
"type": "RLIMIT_NOFILE",
33+
"hard": 1024,
34+
"soft": 1024
35+
}
36+
],
37+
"noNewPrivileges": true
38+
},
39+
"root": {
40+
"path": "rootfs",
41+
"readonly": false
42+
},
43+
"hostname": "runc",
44+
"mounts": [
45+
{
46+
"destination": "/proc",
47+
"type": "proc",
48+
"source": "proc"
49+
}
50+
],
51+
"linux": {
52+
"devices": [
53+
{
54+
"path": "/dev/kvm",
55+
"type": "c",
56+
"major": 10,
57+
"minor": 232,
58+
"fileMode": 438,
59+
"uid": 0,
60+
"gid": 0
61+
},
62+
{
63+
"path": "/dev/net/tun",
64+
"type": "c",
65+
"major": 10,
66+
"minor": 200,
67+
"fileMode": 438,
68+
"uid": 0,
69+
"gid": 0
70+
}
71+
],
72+
"resources": {
73+
"devices": [
74+
{
75+
"allow": false,
76+
"access": "rwm"
77+
},
78+
{
79+
"allow": true,
80+
"major": 10,
81+
"minor": 232,
82+
"access": "rwm"
83+
},
84+
{
85+
"allow": true,
86+
"major": 10,
87+
"minor": 200,
88+
"access": "rwm"
89+
}
90+
]
91+
},
92+
"namespaces": [
93+
{
94+
"type": "cgroup"
95+
},
96+
{
97+
"type": "pid"
98+
},
99+
{
100+
"type": "network"
101+
},
102+
{
103+
"type": "ipc"
104+
},
105+
{
106+
"type": "uts"
107+
},
108+
{
109+
"type": "mount"
110+
}
111+
],
112+
"maskedPaths": [
113+
"/proc/asound",
114+
"/proc/kcore",
115+
"/proc/latency_stats",
116+
"/proc/timer_list",
117+
"/proc/timer_stats",
118+
"/proc/sched_debug",
119+
"/sys/firmware",
120+
"/proc/scsi"
121+
],
122+
"readonlyPaths": [
123+
"/proc/bus",
124+
"/proc/fs",
125+
"/proc/irq",
126+
"/proc/sys",
127+
"/proc/sysrq-trigger"
128+
]
129+
}
130+
}

runtime/jailer.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package main
15+
16+
import (
17+
"context"
18+
19+
"github.com/firecracker-microvm/firecracker-go-sdk"
20+
"github.com/sirupsen/logrus"
21+
22+
"github.com/firecracker-microvm/firecracker-containerd/internal/vm"
23+
"github.com/firecracker-microvm/firecracker-containerd/proto"
24+
)
25+
26+
const (
27+
kernelImageFileName = "kernel-image"
28+
jailerHandlerName = "firecracker-containerd-jail-handler"
29+
jailerFifoHandlerName = "firecracker-containerd-jail-fifo-handler"
30+
rootfsFolder = "rootfs"
31+
32+
// TODO evenetually we can get rid of this when we add usernamespaces to
33+
// jailing.
34+
jailerUID = 300000
35+
jailerGID = 300000
36+
)
37+
38+
var (
39+
runcConfigPath = "/etc/containerd/firecracker-runc-config.json"
40+
)
41+
42+
// jailer will allow modification and provide options to the the Firecracker VM
43+
// to allow for jailing. In addition, this will allow for given files to be exposed
44+
// to the jailed filesystem.
45+
type jailer interface {
46+
// BuildJailedMachine will modify the firecracker.Config and provide
47+
// firecracker.Opt to be passed into firecracker.NewMachine which will allow
48+
// for the VM to be jailed.
49+
BuildJailedMachine(cfg *Config, machineCfg *firecracker.Config, vmID string) ([]firecracker.Opt, error)
50+
// ExposeDeviceToJail will expose the given device provided by the snapshotter
51+
// to the jailed filesystem
52+
ExposeDeviceToJail(path string) error
53+
// JailPath is used to return the directory we are supposed to be working in.
54+
JailPath() vm.Dir
55+
// StubDrivesOptions will return a set of options used to create a new stub
56+
// drive handler.
57+
StubDrivesOptions() []stubDrivesOpt
58+
}
59+
60+
// newJailer is used to construct a jailer from the CreateVM request. If no
61+
// request or jailer config was provided, then the noopJailer will be returned.
62+
func newJailer(
63+
ctx context.Context,
64+
logger *logrus.Entry,
65+
ociBundlePath string,
66+
service *service,
67+
request *proto.CreateVMRequest,
68+
) (jailer, error) {
69+
if request == nil || request.JailerConfig == nil {
70+
l := logger.WithField("jailer", "noop")
71+
return newNoopJailer(ctx, l, service.shimDir), nil
72+
}
73+
74+
l := logger.WithField("jailer", "runc")
75+
return newRuncJailer(ctx, l, ociBundlePath, service.config.JailerConfig.RuncBinaryPath, jailerUID, jailerGID)
76+
}

runtime/jailer_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package main
15+
16+
import (
17+
"os"
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
)
22+
23+
func TestCopyFile_simple(t *testing.T) {
24+
srcPath := "./firecracker-runc-config.json.example"
25+
dstPath := "./test-copy-file"
26+
27+
const expectedMode = 0600
28+
err := copyFile(srcPath, dstPath, expectedMode)
29+
assert.NoError(t, err, "failed to copy file")
30+
defer os.Remove(dstPath)
31+
32+
info, err := os.Stat(dstPath)
33+
assert.NoError(t, err, "failed to stat file")
34+
assert.Equal(t, os.FileMode(expectedMode), info.Mode())
35+
}
36+
37+
func TestCopyFile_invalidPaths(t *testing.T) {
38+
srcPath := "./invalid.path"
39+
dstPath := "./test-copy-file"
40+
41+
err := copyFile(srcPath, dstPath, 0600)
42+
assert.Error(t, err, "copyFile should have returned an error")
43+
}

runtime/noop_jailer.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package main
15+
16+
import (
17+
"context"
18+
19+
"github.com/firecracker-microvm/firecracker-containerd/internal/vm"
20+
"github.com/firecracker-microvm/firecracker-go-sdk"
21+
"github.com/sirupsen/logrus"
22+
)
23+
24+
// noopJailer is a jailer that returns only successful responses and performs
25+
// no operations during calls
26+
type noopJailer struct {
27+
logger *logrus.Entry
28+
shimDir vm.Dir
29+
ctx context.Context
30+
}
31+
32+
func newNoopJailer(ctx context.Context, logger *logrus.Entry, shimDir vm.Dir) noopJailer {
33+
return noopJailer{
34+
logger: logger,
35+
shimDir: shimDir,
36+
ctx: ctx,
37+
}
38+
}
39+
40+
func (j noopJailer) BuildJailedMachine(cfg *Config, machineConfig *firecracker.Config, vmID string) ([]firecracker.Opt, error) {
41+
cmd := firecracker.VMCommandBulder{}.
42+
WithBin(cfg.FirecrackerBinaryPath).
43+
WithSocketPath(j.JailPath().FirecrackerVSockRelPath()).
44+
Build(j.ctx)
45+
46+
j.logger.Debug("noop operation for BuildJailedMachine")
47+
return []firecracker.Opt{
48+
firecracker.WithProcessRunner(cmd),
49+
}, nil
50+
}
51+
52+
func (j noopJailer) JailPath() vm.Dir {
53+
j.logger.Debug("noop operation returning shim dir for JailPath")
54+
return j.shimDir
55+
}
56+
57+
func (j noopJailer) ExposeDeviceToJail(path string) error {
58+
j.logger.Debug("noop operation for ExposeDeviceToJail")
59+
return nil
60+
}
61+
62+
func (j noopJailer) StubDrivesOptions() []stubDrivesOpt {
63+
j.logger.Debug("noop operation for StubDrivesOptions")
64+
return []stubDrivesOpt{}
65+
}

0 commit comments

Comments
 (0)