@@ -23,6 +23,7 @@ import (
2323 "os/exec"
2424 "path/filepath"
2525 "strings"
26+ "sync"
2627 "syscall"
2728
2829 "github.com/firecracker-microvm/firecracker-go-sdk"
@@ -46,10 +47,13 @@ type runcJailer struct {
4647 runcBinaryPath string
4748 uid uint32
4849 gid uint32
50+ once sync.Once
4951}
5052
5153const firecrackerFileName = "firecracker"
5254
55+ var configSpec * specs.Spec
56+
5357func newRuncJailer (ctx context.Context , logger * logrus.Entry , ociBundlePath , runcBinPath string , uid , gid uint32 ) (* runcJailer , error ) {
5458 l := logger .WithField ("ociBundlePath" , ociBundlePath ).
5559 WithField ("runcBinaryPath" , runcBinPath )
@@ -77,17 +81,17 @@ func newRuncJailer(ctx context.Context, logger *logrus.Entry, ociBundlePath, run
7781
7882// JailPath returns the base directory from where the jail binary will be ran
7983// from
80- func (j runcJailer ) OCIBundlePath () string {
84+ func (j * runcJailer ) OCIBundlePath () string {
8185 return j .ociBundlePath
8286}
8387
8488// RootPath returns the root fs of the jailed system.
85- func (j runcJailer ) RootPath () string {
89+ func (j * runcJailer ) RootPath () string {
8690 return filepath .Join (j .OCIBundlePath (), rootfsFolder )
8791}
8892
8993// JailPath will return the OCI bundle rootfs path
90- func (j runcJailer ) JailPath () vm.Dir {
94+ func (j * runcJailer ) JailPath () vm.Dir {
9195 return vm .Dir (j .RootPath ())
9296}
9397
@@ -100,6 +104,12 @@ func (j *runcJailer) BuildJailedMachine(cfg *Config, machineConfig *firecracker.
100104 // Build a new client since BuildJailedRootHandler modifies the socket path value.
101105 client := firecracker .NewClient (machineConfig .SocketPath , j .logger , machineConfig .Debug )
102106
107+ if machineConfig .NetNS == "" {
108+ if netns := getNetNS (configSpec ); netns != "" {
109+ machineConfig .NetNS = netns
110+ }
111+ }
112+
103113 opts := []firecracker.Opt {
104114 firecracker .WithProcessRunner (j .jailerCommand (vmID , cfg .Debug )),
105115 firecracker .WithClient (client ),
@@ -206,7 +216,7 @@ func (j *runcJailer) BuildJailedRootHandler(cfg *Config, socketPath *string, vmI
206216
207217// BuildLinkFifoHandler will return a new firecracker.Handler with the function
208218// that will allow linking of the fifos making them visible to Firecracker.
209- func (j runcJailer ) BuildLinkFifoHandler () firecracker.Handler {
219+ func (j * runcJailer ) BuildLinkFifoHandler () firecracker.Handler {
210220 return firecracker.Handler {
211221 Name : jailerFifoHandlerName ,
212222 Fn : func (ctx context.Context , m * firecracker.Machine ) error {
@@ -232,7 +242,7 @@ func (j runcJailer) BuildLinkFifoHandler() firecracker.Handler {
232242
233243// StubDrivesOptions will return a set of options used to create a new stub
234244// drive handler.
235- func (j runcJailer ) StubDrivesOptions () []stubDrivesOpt {
245+ func (j * runcJailer ) StubDrivesOptions () []stubDrivesOpt {
236246 return []stubDrivesOpt {
237247 func (drives []models.Drive ) error {
238248 for _ , drive := range drives {
@@ -251,7 +261,7 @@ func (j runcJailer) StubDrivesOptions() []stubDrivesOpt {
251261// the jail. For block devices we will use mknod to create the device and then
252262// set the correct permissions to ensure visibility in the jail. Regular files
253263// will be copied into the jail.
254- func (j runcJailer ) ExposeFileToJail (srcPath string ) error {
264+ func (j * runcJailer ) ExposeFileToJail (srcPath string ) error {
255265 uid := j .uid
256266 gid := j .gid
257267
@@ -340,7 +350,7 @@ func copyFile(src, dst string, mode os.FileMode) error {
340350 return nil
341351}
342352
343- func (j runcJailer ) jailerCommand (containerName string , isDebug bool ) * exec.Cmd {
353+ func (j * runcJailer ) jailerCommand (containerName string , isDebug bool ) * exec.Cmd {
344354 cmd := exec .CommandContext (j .ctx , j .runcBinaryPath , "run" , containerName )
345355 cmd .Dir = j .OCIBundlePath ()
346356
@@ -353,36 +363,48 @@ func (j runcJailer) jailerCommand(containerName string, isDebug bool) *exec.Cmd
353363}
354364
355365// 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- }
366+ func (j * runcJailer ) overwriteConfig (cfg * Config , socketPath , configPath string ) error {
367+ var err error
368+ j .once .Do (func () {
369+ if configSpec == nil {
370+ spec := specs.Spec {}
371+ var configBytes []byte
372+ configBytes , err = ioutil .ReadFile (configPath )
373+ if err != nil {
374+ return
375+ }
364376
365- if err : = json .Unmarshal (configBytes , & spec ); err != nil {
366- return err
367- }
377+ if err = json .Unmarshal (configBytes , & spec ); err != nil {
378+ return
379+ }
368380
369- if spec .Process .User .UID != 0 ||
370- spec .Process .User .GID != 0 {
371- return fmt .Errorf (
372- "using UID %d and GID %d, these values must not be set" ,
373- spec .Process .User .UID ,
374- spec .Process .User .GID ,
375- )
376- }
381+ configSpec = & spec
377382
378- spec = j .setDefaultConfigValues (cfg , socketPath , spec )
383+ if spec .Process .User .UID != 0 ||
384+ spec .Process .User .GID != 0 {
385+ err = fmt .Errorf (
386+ "using UID %d and GID %d, these values must not be set" ,
387+ spec .Process .User .UID ,
388+ spec .Process .User .GID ,
389+ )
390+ return
391+ }
379392
380- spec .Root .Path = rootfsFolder
381- spec .Root .Readonly = false
393+ spec = j .setDefaultConfigValues (cfg , socketPath , spec )
394+ spec .Root .Path = rootfsFolder
395+ spec .Root .Readonly = false
396+ }
397+ })
398+
399+ if err != nil {
400+ return err
401+ }
402+
403+ spec := * configSpec
382404 spec .Process .User .UID = j .uid
383405 spec .Process .User .GID = j .gid
384406
385- configBytes , err = json .Marshal (& spec )
407+ configBytes , err : = json .Marshal (& spec )
386408 if err != nil {
387409 return err
388410 }
@@ -396,7 +418,7 @@ func (j runcJailer) overwriteConfig(cfg *Config, socketPath, configPath string)
396418
397419// setDefaultConfigValues will process the spec file provided and allow any
398420// empty/zero values to be replaced with default values.
399- func (j runcJailer ) setDefaultConfigValues (cfg * Config , socketPath string , spec specs.Spec ) specs.Spec {
421+ func (j * runcJailer ) setDefaultConfigValues (cfg * Config , socketPath string , spec specs.Spec ) specs.Spec {
400422 if spec .Process == nil {
401423 spec .Process = & specs.Process {}
402424 }
@@ -448,3 +470,17 @@ func mkdirAllWithPermissions(path string, mode os.FileMode, uid, gid uint32) err
448470
449471 return nil
450472}
473+
474+ func getNetNS (spec * specs.Spec ) string {
475+ if spec == nil {
476+ return ""
477+ }
478+
479+ for _ , ns := range spec .Linux .Namespaces {
480+ if ns .Type == "network" {
481+ return ns .Path
482+ }
483+ }
484+
485+ return ""
486+ }
0 commit comments