Skip to content

Adding netns to jailer #305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ require (
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad // indirect
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 // indirect
github.com/docker/go-units v0.3.3
github.com/firecracker-microvm/firecracker-go-sdk v0.19.0
github.com/firecracker-microvm/firecracker-go-sdk v0.19.1-0.20191114205152-9e2ff62839b2
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/godbus/dbus v0.0.0-20181025153459-66d97aec3384 // indirect
github.com/gofrs/uuid v3.2.0+incompatible
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zF
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/firecracker-microvm/firecracker-go-sdk v0.19.0 h1:Fgb3WhB4q3J5e+ksMBtjVwBTvbDtPAkx7eQulb2BOq8=
github.com/firecracker-microvm/firecracker-go-sdk v0.19.0/go.mod h1:kW0gxvPpPvMukUxxTO9DrpSlScrtrTDGY3VgjAj/Qwc=
github.com/firecracker-microvm/firecracker-go-sdk v0.19.1-0.20191114205152-9e2ff62839b2 h1:Ab52E0UlBOmMIAK/igRycsxSAJVDBDMYq+Wr/n7z2E0=
github.com/firecracker-microvm/firecracker-go-sdk v0.19.1-0.20191114205152-9e2ff62839b2/go.mod h1:kW0gxvPpPvMukUxxTO9DrpSlScrtrTDGY3VgjAj/Qwc=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
Expand Down
75 changes: 42 additions & 33 deletions proto/firecracker.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions proto/firecracker.proto
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,6 @@ message GetVMMetadataResponse {
}

message JailerConfig {
string NetNS = 1;
}

19 changes: 17 additions & 2 deletions runtime/cni_integ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/oci"
"github.com/containerd/containerd/pkg/ttrpcutil"
"github.com/firecracker-microvm/firecracker-go-sdk/cni/cmd/tc-redirect-tap/args"
"github.com/shirou/gopsutil/cpu"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -39,8 +40,7 @@ import (
)

func TestCNISupport_Isolated(t *testing.T) {
prepareIntegTest(t)

prepareIntegTest(t, withJailer())
testTimeout := 120 * time.Second
ctx, cancel := context.WithTimeout(namespaces.WithNamespace(context.Background(), defaultNamespace), testTimeout)
defer cancel()
Expand Down Expand Up @@ -96,8 +96,23 @@ func TestCNISupport_Isolated(t *testing.T) {
CNIConfig: &proto.CNIConfiguration{
NetworkName: cniNetworkName,
InterfaceName: "veth0",
Args: []*proto.CNIConfiguration_CNIArg{
{
Key: "IgnoreUnknown",
Value: "true",
},
{
Key: args.TCRedirectTapUID,
Value: fmt.Sprintf("%d", jailerUID),
},
{
Key: args.TCRedirectTapGID,
Value: fmt.Sprintf("%d", jailerGID),
},
},
},
}},
JailerConfig: &proto.JailerConfig{},
})
require.NoError(t, err, "failed to create vm")

Expand Down
90 changes: 62 additions & 28 deletions runtime/runc_jailer.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ import (
"github.com/firecracker-microvm/firecracker-containerd/internal/vm"
)

const (
networkNamespaceRuncName = "network"
)

// runcJailer uses runc to set up a jailed environment for the Firecracker VM.
type runcJailer struct {
ctx context.Context
Expand All @@ -46,6 +50,7 @@ type runcJailer struct {
runcBinaryPath string
uid uint32
gid uint32
configSpec specs.Spec
}

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

spec := specs.Spec{}
var configBytes []byte
configBytes, err := ioutil.ReadFile(runcConfigPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to read %s", runcConfigPath)
}

if err = json.Unmarshal(configBytes, &spec); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal %s", runcConfigPath)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use runcConfigPath instead of hard-coding firecracker-runc-config.json here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to use Sprintf inside Wrapf :)


j.configSpec = spec

rootPath := j.RootPath()

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

// JailPath returns the base directory from where the jail binary will be ran
// from
func (j runcJailer) OCIBundlePath() string {
func (j *runcJailer) OCIBundlePath() string {
return j.ociBundlePath
}

// RootPath returns the root fs of the jailed system.
func (j runcJailer) RootPath() string {
func (j *runcJailer) RootPath() string {
return filepath.Join(j.OCIBundlePath(), rootfsFolder)
}

// JailPath will return the OCI bundle rootfs path
func (j runcJailer) JailPath() vm.Dir {
func (j *runcJailer) JailPath() vm.Dir {
return vm.Dir(j.RootPath())
}

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

if machineConfig.NetNS == "" {
if netns := getNetNS(j.configSpec); netns != "" {
machineConfig.NetNS = netns
}
}

opts := []firecracker.Opt{
firecracker.WithProcessRunner(j.jailerCommand(vmID, cfg.Debug)),
firecracker.WithClient(client),
Expand All @@ -120,10 +144,10 @@ func (j *runcJailer) BuildJailedMachine(cfg *Config, machineConfig *firecracker.

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

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

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

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

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

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

Expand Down Expand Up @@ -292,7 +318,7 @@ func (j runcJailer) ExposeFileToJail(srcPath string) error {
}

// copyFileToJail will copy a file from src to dst, and chown the new file to the jail user.
func (j runcJailer) copyFileToJail(src, dst string, mode os.FileMode) error {
func (j *runcJailer) copyFileToJail(src, dst string, mode os.FileMode) error {
if err := copyFile(src, dst, mode); err != nil {
return err
}
Expand Down Expand Up @@ -340,7 +366,7 @@ func copyFile(src, dst string, mode os.FileMode) error {
return nil
}

func (j runcJailer) jailerCommand(containerName string, isDebug bool) *exec.Cmd {
func (j *runcJailer) jailerCommand(containerName string, isDebug bool) *exec.Cmd {
cmd := exec.CommandContext(j.ctx, j.runcBinaryPath, "run", containerName)
cmd.Dir = j.OCIBundlePath()

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

// overwriteConfig will set the proper default values if a field had not been set.
//
// TODO: Add netns
func (j runcJailer) overwriteConfig(cfg *Config, socketPath, configPath string) error {
spec := specs.Spec{}
configBytes, err := ioutil.ReadFile(configPath)
if err != nil {
return err
}

if err := json.Unmarshal(configBytes, &spec); err != nil {
return err
}

func (j *runcJailer) overwriteConfig(cfg *Config, machineConfig *firecracker.Config, socketPath, configPath string) error {
spec := j.configSpec
if spec.Process.User.UID != 0 ||
spec.Process.User.GID != 0 {
return fmt.Errorf(
Expand All @@ -376,13 +391,22 @@ func (j runcJailer) overwriteConfig(cfg *Config, socketPath, configPath string)
}

spec = j.setDefaultConfigValues(cfg, socketPath, spec)

spec.Root.Path = rootfsFolder
spec.Root.Readonly = false
spec.Process.User.UID = j.uid
spec.Process.User.GID = j.gid

configBytes, err = json.Marshal(&spec)
if machineConfig.NetNS != "" {
for i, ns := range spec.Linux.Namespaces {
if ns.Type == networkNamespaceRuncName {
ns.Path = machineConfig.NetNS
spec.Linux.Namespaces[i] = ns
break
}
}
}

configBytes, err := json.Marshal(&spec)
if err != nil {
return err
}
Expand All @@ -396,7 +420,7 @@ func (j runcJailer) overwriteConfig(cfg *Config, socketPath, configPath string)

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

return nil
}

func getNetNS(spec specs.Spec) string {
for _, ns := range spec.Linux.Namespaces {
if ns.Type == networkNamespaceRuncName {
return ns.Path
}
}

return ""
}
Loading