Skip to content

Commit 0100a64

Browse files
committed
Add support for netns
This change allows for users to pass in a custom network namespace in the create vm request. This will then pass that netns to the SDK which will run the VM in that netns. Signed-off-by: xibz <[email protected]>
1 parent 8c29d8c commit 0100a64

File tree

9 files changed

+155
-102
lines changed

9 files changed

+155
-102
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ require (
1818
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad // indirect
1919
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 // indirect
2020
github.com/docker/go-units v0.3.3
21-
github.com/firecracker-microvm/firecracker-go-sdk v0.19.0
21+
github.com/firecracker-microvm/firecracker-go-sdk v0.19.1-0.20191114205152-9e2ff62839b2
2222
github.com/go-ole/go-ole v1.2.4 // indirect
2323
github.com/godbus/dbus v0.0.0-20181025153459-66d97aec3384 // indirect
2424
github.com/gofrs/uuid v3.2.0+incompatible

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zF
5959
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
6060
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
6161
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
62-
github.com/firecracker-microvm/firecracker-go-sdk v0.19.0 h1:Fgb3WhB4q3J5e+ksMBtjVwBTvbDtPAkx7eQulb2BOq8=
63-
github.com/firecracker-microvm/firecracker-go-sdk v0.19.0/go.mod h1:kW0gxvPpPvMukUxxTO9DrpSlScrtrTDGY3VgjAj/Qwc=
62+
github.com/firecracker-microvm/firecracker-go-sdk v0.19.1-0.20191114205152-9e2ff62839b2 h1:Ab52E0UlBOmMIAK/igRycsxSAJVDBDMYq+Wr/n7z2E0=
63+
github.com/firecracker-microvm/firecracker-go-sdk v0.19.1-0.20191114205152-9e2ff62839b2/go.mod h1:kW0gxvPpPvMukUxxTO9DrpSlScrtrTDGY3VgjAj/Qwc=
6464
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0=
6565
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
6666
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=

proto/firecracker.pb.go

Lines changed: 42 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proto/firecracker.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,6 @@ message GetVMMetadataResponse {
7272
}
7373

7474
message JailerConfig {
75+
string NetNS = 1;
7576
}
7677

runtime/cni_integ_test.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/containerd/containerd/namespaces"
2929
"github.com/containerd/containerd/oci"
3030
"github.com/containerd/containerd/pkg/ttrpcutil"
31+
"github.com/firecracker-microvm/firecracker-go-sdk/cni/cmd/tc-redirect-tap/args"
3132
"github.com/shirou/gopsutil/cpu"
3233
"github.com/stretchr/testify/assert"
3334
"github.com/stretchr/testify/require"
@@ -39,8 +40,7 @@ import (
3940
)
4041

4142
func TestCNISupport_Isolated(t *testing.T) {
42-
prepareIntegTest(t)
43-
43+
prepareIntegTest(t, withJailer())
4444
testTimeout := 120 * time.Second
4545
ctx, cancel := context.WithTimeout(namespaces.WithNamespace(context.Background(), defaultNamespace), testTimeout)
4646
defer cancel()
@@ -96,8 +96,23 @@ func TestCNISupport_Isolated(t *testing.T) {
9696
CNIConfig: &proto.CNIConfiguration{
9797
NetworkName: cniNetworkName,
9898
InterfaceName: "veth0",
99+
Args: []*proto.CNIConfiguration_CNIArg{
100+
{
101+
Key: "IgnoreUnknown",
102+
Value: "true",
103+
},
104+
{
105+
Key: args.TCRedirectTapUID,
106+
Value: fmt.Sprintf("%d", jailerUID),
107+
},
108+
{
109+
Key: args.TCRedirectTapGID,
110+
Value: fmt.Sprintf("%d", jailerGID),
111+
},
112+
},
99113
},
100114
}},
115+
JailerConfig: &proto.JailerConfig{},
101116
})
102117
require.NoError(t, err, "failed to create vm")
103118

runtime/runc_jailer.go

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ import (
3535
"github.com/firecracker-microvm/firecracker-containerd/internal/vm"
3636
)
3737

38+
const (
39+
networkNamespaceRuncName = "network"
40+
)
41+
3842
// runcJailer uses runc to set up a jailed environment for the Firecracker VM.
3943
type runcJailer struct {
4044
ctx context.Context
@@ -46,6 +50,7 @@ type runcJailer struct {
4650
runcBinaryPath string
4751
uid uint32
4852
gid uint32
53+
configSpec specs.Spec
4954
}
5055

5156
const firecrackerFileName = "firecracker"
@@ -63,6 +68,19 @@ func newRuncJailer(ctx context.Context, logger *logrus.Entry, ociBundlePath, run
6368
gid: gid,
6469
}
6570

71+
spec := specs.Spec{}
72+
var configBytes []byte
73+
configBytes, err := ioutil.ReadFile(runcConfigPath)
74+
if err != nil {
75+
return nil, errors.Wrapf(err, "failed to read %s", runcConfigPath)
76+
}
77+
78+
if err = json.Unmarshal(configBytes, &spec); err != nil {
79+
return nil, errors.Wrapf(err, "failed to unmarshal %s", runcConfigPath)
80+
}
81+
82+
j.configSpec = spec
83+
6684
rootPath := j.RootPath()
6785

6886
const mode = os.FileMode(0700)
@@ -77,29 +95,35 @@ func newRuncJailer(ctx context.Context, logger *logrus.Entry, ociBundlePath, run
7795

7896
// JailPath returns the base directory from where the jail binary will be ran
7997
// from
80-
func (j runcJailer) OCIBundlePath() string {
98+
func (j *runcJailer) OCIBundlePath() string {
8199
return j.ociBundlePath
82100
}
83101

84102
// RootPath returns the root fs of the jailed system.
85-
func (j runcJailer) RootPath() string {
103+
func (j *runcJailer) RootPath() string {
86104
return filepath.Join(j.OCIBundlePath(), rootfsFolder)
87105
}
88106

89107
// JailPath will return the OCI bundle rootfs path
90-
func (j runcJailer) JailPath() vm.Dir {
108+
func (j *runcJailer) JailPath() vm.Dir {
91109
return vm.Dir(j.RootPath())
92110
}
93111

94112
// BuildJailedMachine will return the needed options for a jailed Firecracker
95113
// instance. In addition, some configuration values will be overwritten to the
96114
// jailed values, like SocketPath in the machineConfig.
97115
func (j *runcJailer) BuildJailedMachine(cfg *Config, machineConfig *firecracker.Config, vmID string) ([]firecracker.Opt, error) {
98-
handler := j.BuildJailedRootHandler(cfg, &machineConfig.SocketPath, vmID)
116+
handler := j.BuildJailedRootHandler(cfg, machineConfig, vmID)
99117
fifoHandler := j.BuildLinkFifoHandler()
100118
// Build a new client since BuildJailedRootHandler modifies the socket path value.
101119
client := firecracker.NewClient(machineConfig.SocketPath, j.logger, machineConfig.Debug)
102120

121+
if machineConfig.NetNS == "" {
122+
if netns := getNetNS(j.configSpec); netns != "" {
123+
machineConfig.NetNS = netns
124+
}
125+
}
126+
103127
opts := []firecracker.Opt{
104128
firecracker.WithProcessRunner(j.jailerCommand(vmID, cfg.Debug)),
105129
firecracker.WithClient(client),
@@ -120,10 +144,10 @@ func (j *runcJailer) BuildJailedMachine(cfg *Config, machineConfig *firecracker.
120144

121145
// BuildJailedRootHandler will populate the jail with the necessary files, which may be
122146
// device nodes, hard links, and/or bind-mount targets
123-
func (j *runcJailer) BuildJailedRootHandler(cfg *Config, socketPath *string, vmID string) firecracker.Handler {
147+
func (j *runcJailer) BuildJailedRootHandler(cfg *Config, machineConfig *firecracker.Config, vmID string) firecracker.Handler {
124148
ociBundlePath := j.OCIBundlePath()
125149
rootPath := j.RootPath()
126-
*socketPath = filepath.Join(rootPath, "api.socket")
150+
machineConfig.SocketPath = filepath.Join(rootPath, "api.socket")
127151

128152
return firecracker.Handler{
129153
Name: jailerHandlerName,
@@ -136,7 +160,9 @@ func (j *runcJailer) BuildJailedRootHandler(cfg *Config, socketPath *string, vmI
136160
}
137161

138162
j.logger.Debug("Overwritting process args of config")
139-
if err := j.overwriteConfig(cfg, filepath.Base(m.Cfg.SocketPath), rootPathToConfig); err != nil {
163+
// we pass m.Cfg as opposed to machineConfig as we want the populated
164+
// config defaults when calling NewMachine
165+
if err := j.overwriteConfig(cfg, &m.Cfg, filepath.Base(m.Cfg.SocketPath), rootPathToConfig); err != nil {
140166
return errors.Wrap(err, "failed to overwrite config.json")
141167
}
142168

@@ -206,7 +232,7 @@ func (j *runcJailer) BuildJailedRootHandler(cfg *Config, socketPath *string, vmI
206232

207233
// BuildLinkFifoHandler will return a new firecracker.Handler with the function
208234
// that will allow linking of the fifos making them visible to Firecracker.
209-
func (j runcJailer) BuildLinkFifoHandler() firecracker.Handler {
235+
func (j *runcJailer) BuildLinkFifoHandler() firecracker.Handler {
210236
return firecracker.Handler{
211237
Name: jailerFifoHandlerName,
212238
Fn: func(ctx context.Context, m *firecracker.Machine) error {
@@ -232,7 +258,7 @@ func (j runcJailer) BuildLinkFifoHandler() firecracker.Handler {
232258

233259
// StubDrivesOptions will return a set of options used to create a new stub
234260
// drive handler.
235-
func (j runcJailer) StubDrivesOptions() []stubDrivesOpt {
261+
func (j *runcJailer) StubDrivesOptions() []stubDrivesOpt {
236262
return []stubDrivesOpt{
237263
func(drives []models.Drive) error {
238264
for _, drive := range drives {
@@ -251,7 +277,7 @@ func (j runcJailer) StubDrivesOptions() []stubDrivesOpt {
251277
// the jail. For block devices we will use mknod to create the device and then
252278
// set the correct permissions to ensure visibility in the jail. Regular files
253279
// will be copied into the jail.
254-
func (j runcJailer) ExposeFileToJail(srcPath string) error {
280+
func (j *runcJailer) ExposeFileToJail(srcPath string) error {
255281
uid := j.uid
256282
gid := j.gid
257283

@@ -292,7 +318,7 @@ func (j runcJailer) ExposeFileToJail(srcPath string) error {
292318
}
293319

294320
// copyFileToJail will copy a file from src to dst, and chown the new file to the jail user.
295-
func (j runcJailer) copyFileToJail(src, dst string, mode os.FileMode) error {
321+
func (j *runcJailer) copyFileToJail(src, dst string, mode os.FileMode) error {
296322
if err := copyFile(src, dst, mode); err != nil {
297323
return err
298324
}
@@ -340,7 +366,7 @@ func copyFile(src, dst string, mode os.FileMode) error {
340366
return nil
341367
}
342368

343-
func (j runcJailer) jailerCommand(containerName string, isDebug bool) *exec.Cmd {
369+
func (j *runcJailer) jailerCommand(containerName string, isDebug bool) *exec.Cmd {
344370
cmd := exec.CommandContext(j.ctx, j.runcBinaryPath, "run", containerName)
345371
cmd.Dir = j.OCIBundlePath()
346372

@@ -353,19 +379,8 @@ func (j runcJailer) jailerCommand(containerName string, isDebug bool) *exec.Cmd
353379
}
354380

355381
// overwriteConfig will set the proper default values if a field had not been set.
356-
//
357-
// TODO: Add netns
358-
func (j runcJailer) overwriteConfig(cfg *Config, socketPath, configPath string) error {
359-
spec := specs.Spec{}
360-
configBytes, err := ioutil.ReadFile(configPath)
361-
if err != nil {
362-
return err
363-
}
364-
365-
if err := json.Unmarshal(configBytes, &spec); err != nil {
366-
return err
367-
}
368-
382+
func (j *runcJailer) overwriteConfig(cfg *Config, machineConfig *firecracker.Config, socketPath, configPath string) error {
383+
spec := j.configSpec
369384
if spec.Process.User.UID != 0 ||
370385
spec.Process.User.GID != 0 {
371386
return fmt.Errorf(
@@ -376,13 +391,22 @@ func (j runcJailer) overwriteConfig(cfg *Config, socketPath, configPath string)
376391
}
377392

378393
spec = j.setDefaultConfigValues(cfg, socketPath, spec)
379-
380394
spec.Root.Path = rootfsFolder
381395
spec.Root.Readonly = false
382396
spec.Process.User.UID = j.uid
383397
spec.Process.User.GID = j.gid
384398

385-
configBytes, err = json.Marshal(&spec)
399+
if machineConfig.NetNS != "" {
400+
for i, ns := range spec.Linux.Namespaces {
401+
if ns.Type == networkNamespaceRuncName {
402+
ns.Path = machineConfig.NetNS
403+
spec.Linux.Namespaces[i] = ns
404+
break
405+
}
406+
}
407+
}
408+
409+
configBytes, err := json.Marshal(&spec)
386410
if err != nil {
387411
return err
388412
}
@@ -396,7 +420,7 @@ func (j runcJailer) overwriteConfig(cfg *Config, socketPath, configPath string)
396420

397421
// setDefaultConfigValues will process the spec file provided and allow any
398422
// empty/zero values to be replaced with default values.
399-
func (j runcJailer) setDefaultConfigValues(cfg *Config, socketPath string, spec specs.Spec) specs.Spec {
423+
func (j *runcJailer) setDefaultConfigValues(cfg *Config, socketPath string, spec specs.Spec) specs.Spec {
400424
if spec.Process == nil {
401425
spec.Process = &specs.Process{}
402426
}
@@ -448,3 +472,13 @@ func mkdirAllWithPermissions(path string, mode os.FileMode, uid, gid uint32) err
448472

449473
return nil
450474
}
475+
476+
func getNetNS(spec specs.Spec) string {
477+
for _, ns := range spec.Linux.Namespaces {
478+
if ns.Type == networkNamespaceRuncName {
479+
return ns.Path
480+
}
481+
}
482+
483+
return ""
484+
}

0 commit comments

Comments
 (0)