@@ -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.
3943type 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
5156const 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.
97115func (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