Skip to content
Open
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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
firecracker
release-v1.9.1-x86_64
fc_kernel
fc_rfs
jailer
bin
105 changes: 105 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Test JAILER enclosed MicroVM

Steps required to run:

1. Download all the binaries

Getting all the rootfs and kernel.
```
ARCH="$(uname -m)"

latest=$(wget "http://spec.ccfc.min.s3.amazonaws.com/?prefix=firecracker-ci/v1.10/x86_64/vmlinux-5.10&list-type=2" -O - 2>/dev/null | grep "(?<=<Key>)(firecracker-ci/v1.10/x86_64/vmlinux-5\.10\.[0-9]{3})(?=</Key>)" -o -P)

# Download a linux kernel binary
wget "https://s3.amazonaws.com/spec.ccfc.min/${latest}"

# Download a rootfs
wget "https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.10/${ARCH}/ubuntu-22.04.ext4"

# Download the ssh key for the rootfs
wget "https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.10/${ARCH}/ubuntu-22.04.id_rsa"

# Set user read permission on the ssh key
chmod 400 ./ubuntu-22.04.id_rsa
```

Getting the firecracker and jailer binay.

Note : For jailer binary you might need to build the firecracker from scratch with docker.

```
ARCH="$(uname -m)"
release_url="https://github.com/firecracker-microvm/firecracker/releases"
latest=$(basename $(curl -fsSLI -o /dev/null -w %{url_effective} ${release_url}/latest))
curl -L ${release_url}/download/${latest}/firecracker-${latest}-${ARCH}.tgz \
| tar -xz

# Rename the binary to "firecracker"
mv release-${latest}-$(uname -m)/firecracker-${latest}-${ARCH} firecracker

```

To instead build firecracker from source, you will need to have docker installed:

```
ARCH="$(uname -m)"

# Clone the firecracker repository
git clone https://github.com/firecracker-microvm/firecracker firecracker_src

# Start docker
sudo systemctl start docker

# Build firecracker
#
# It is possible to build for gnu, by passing the arguments '-l gnu'.
#
# This will produce the firecracker and jailer binaries under
# `./firecracker/build/cargo_target/${toolchain}/debug`.
#
sudo ./firecracker_src/tools/devtool build

# Rename the binary to "firecracker"
sudo cp ./firecracker_src/build/cargo_target/${ARCH}-unknown-linux-musl/debug/firecracker firecracker
```

Place all the binaries in the root directory with named as following:

ubuntu --> fc_rfs
kernel --> fc_kernel
firecracker_release --> firecracker
jailer_release --> jailer

Parameters set in static configuration file for firecracker.
```
UID := 123
GID := 100

const id = "4580"
const socketPath = "api.socket"
const kernelImagePath = "./fc_kernel"
const rfsPath = "./fc_rfs"
const firecrackerPath = "./firecracker"
const jailerPath = "./jailer"
const ChrootBaseDir = "/srv/jailer"

```
Note:
Ip address of vm set to "172.16.0.2" !!!
CHroot Base Dir set to "/srv/jailer/" !!!

Sandbox Environment Details:

Docker network name - testJailer
ContainerId - randomly generated
Ip address - "172.16.0.2"


## To start the vm
```
make run
```

## Environment Details

Terminal stdio, stdout, stderr will be connected to microvm shell.
Binary file modified bin/firetest
Binary file not shown.
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import "ranjankuldeep/test/methods"

func main() {
methods.ExampleJailerConfig_enablingJailer()
methods.JailerEnabledVM()
select {}

}
71 changes: 37 additions & 34 deletions methods/jailer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,65 +9,68 @@ import (

"github.com/firecracker-microvm/firecracker-go-sdk"
models "github.com/firecracker-microvm/firecracker-go-sdk/client/models"
"github.com/weaveworks/ignite/pkg/logs"
"github.com/google/uuid"
)

// JAILER CONFIGURATION
func ExampleJailerConfig_enablingJailer() {
func JailerEnabledVM() {
UID := 123
GID := 100

vmId := uuid.New()

id := vmId.String()
const socketPath = "api.socket"
const kernelImagePath = "./fc_kernel"
const rfsPath = "./fc_rfs"
const firecrackerPath = "./firecracker"
const jailerPath = "./jailer"
const ChrootBaseDir = "/srv/jailer"

// Owm the fc_kernel and fc_rfs by the uid and gid of the unauthorized usser
if err := os.Chown(kernelImagePath, UID, GID); err != nil {
fmt.Println("Error changing ownership of fc_kernel:", err)
return
}

if err := os.Chown(rfsPath, UID, GID); err != nil {
fmt.Println("Error changing ownership of fc_rfs:", err)
return
}

nsPath, err := CreateContainer("testVm")
if err != nil {
panic(err)
}
if err := SetUpSandBoxNetwork(nsPath, UID, GID); err != nil {
panic(err)
}
const socketPath = "api.socket"

ctx := context.Background()
vmmCtx, vmmCancel := context.WithCancel(ctx)
defer vmmCancel()

const id = "4569"
//
const kernelImagePath = "../vmlinux-5.10.210"
networkIfaces := []firecracker.NetworkInterface{{
StaticConfiguration: &firecracker.StaticNetworkConfiguration{
// MacAddress: "AA:FC:00:00:00:01", // potential bug here
MacAddress: "52:54:00:ab:cd:ef",
HostDevName: "tap0",
HostDevName: "tap0", // interface in the sandbox net namespace
IPConfiguration: &firecracker.IPConfiguration{
IPAddr: net.IPNet{
IP: net.IPv4(172, 16, 0, 2),
Mask: net.IPMask{255, 255, 255, 0},
IP: net.IPv4(172, 16, 0, 2), // Ip Address of the vm.
Mask: net.IPMask{255, 255, 255, 0}, // subnet mask
},
Gateway: net.IPv4(172, 16, 0, 1),
Nameservers: []string{"8.8.8.8"},
IfName: "eth0",
Nameservers: []string{"8.8.8.8"}, // nameserver set to google dns
IfName: "eth0", // interface of vm
},
},
}}
stdOutPath := "/dev/null"
stdout, err := os.OpenFile(stdOutPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
logs.Logger.Errorf("failed to create stdout file: %v", err)
}
stdErrPath := "/dev/null"
stderr, err := os.OpenFile(stdErrPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
logs.Logger.Errorf("failed to create stderr file: %v", err)
}
stdInPath := "/dev/null"
stdin, err := os.OpenFile(stdInPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
logs.Logger.Errorf("failed to create stderr file: %v", err)
}

fcCfg := firecracker.Config{
SocketPath: socketPath,
KernelImagePath: kernelImagePath,
KernelArgs: "console=ttyS0 reboot=k panic=1 pci=off",
Drives: firecracker.NewDrivesBuilder("../ubuntu-22.04.ext4.3").Build(),
Drives: firecracker.NewDrivesBuilder(rfsPath).Build(),
LogLevel: "Debug",
MachineCfg: models.MachineConfiguration{
VcpuCount: firecracker.Int64(2),
Expand All @@ -80,14 +83,14 @@ func ExampleJailerConfig_enablingJailer() {
Daemonize: false,
ID: id,
NumaNode: firecracker.Int(0),
JailerBinary: "../jailer",
ChrootBaseDir: "/srv/jailer",
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
JailerBinary: jailerPath,
ChrootBaseDir: ChrootBaseDir,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
CgroupVersion: "2",
ChrootStrategy: firecracker.NewNaiveChrootStrategy(kernelImagePath),
ExecFile: "../firecracker",
ExecFile: firecrackerPath,
},
NetNS: nsPath,
NetworkInterfaces: networkIfaces,
Expand Down
11 changes: 8 additions & 3 deletions netlink/netlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func DefaultNetlinkOps() NetlinkOps {
return &defaultNetlinkOps{}
}

// Object := Add the redirect filter with queing discipline.
func (ops *defaultNetlinkOps) AddTcRedirect(nsPath string, ethIface string, tuntapIface string) error {
ns, err := netns.GetFromPath(nsPath)
if err != nil {
Expand Down Expand Up @@ -67,6 +68,7 @@ func (ops *defaultNetlinkOps) AddTcRedirect(nsPath string, ethIface string, tunt
})
}

// Objective := Get the link interface from the main process namesapce.
func (ops defaultNetlinkOps) GetLink(name string) (netlink.Link, error) {
link, err := netlink.LinkByName(name)
if _, ok := err.(netlink.LinkNotFoundError); ok {
Expand All @@ -75,6 +77,7 @@ func (ops defaultNetlinkOps) GetLink(name string) (netlink.Link, error) {
return link, nil
}

// Objective := Remove the link interface from the namespace where the main process is running.
func (ops defaultNetlinkOps) RemoveLink(name string) error {
link, err := ops.GetLink(name)
if err != nil {
Expand All @@ -87,7 +90,7 @@ func (ops defaultNetlinkOps) RemoveLink(name string) error {
return err
}

// Add Ip addresses to the interface in the process namespace.
// Objective := Add Ip addresses to the interface in the namespace where the main process is running.
func (ops *defaultNetlinkOps) AddLinkIP(iface netlink.Link, ipAddr netlink.Addr) error {
logs.Logger.Info("Adding Address")
if err := netlink.AddrAdd(iface, &ipAddr); err != nil {
Expand All @@ -97,7 +100,8 @@ func (ops *defaultNetlinkOps) AddLinkIP(iface netlink.Link, ipAddr netlink.Addr)
return nil
}

// tc qdisc add dev $SRC_IFACE ingress
// CMD := tc qdisc add dev $SRC_IFACE ingress
// Objective := adding an ingress qdisc (querrying discipline) for the traffic filter.
func addIngressQdisc(link netlink.Link) error {
qdisc := &netlink.Ingress{
QdiscAttrs: netlink.QdiscAttrs{
Expand All @@ -114,10 +118,11 @@ func addIngressQdisc(link netlink.Link) error {
return nil
}

// tc filter add dev $SRC_IFACE parent ffff:
// CMD := tc filter add dev $SRC_IFACE parent ffff:
// protocol all
// u32 match u32 0 0
// action mirred egress mirror dev $DST_IFACE
// Objective := Filter the wildcard traffic from src interface to destination interface and mirror all the packets.
func addRedirectFilter(linkSrc, linkDest netlink.Link) error {
filter := &netlink.U32{
FilterAttrs: netlink.FilterAttrs{
Expand Down
9 changes: 6 additions & 3 deletions netlink/netns.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

var ErrLinkNotFound = errors.New("Link not found")

// WithNetNS switches to the given namespace, executes the provided function, and then switches back.
// Objective := WithNetNS switches to the given namespace, executes the work function, and then switches back to the original namespace.
func WithNetNS(ns netns.NsHandle, work func() error) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
Expand All @@ -29,12 +29,12 @@ func WithNetNS(ns netns.NsHandle, work func() error) error {
defer netns.Set(oldNs)

if err := work(); err != nil {
return fmt.Errorf("error executing work function in namespace: %w", err)
return fmt.Errorf("error executing work function in the given namespace: %w", err)
}

return nil
}

// Objective := WithNetNSLink switches to the given namespace, executes the work function with the provided link, and then switches back to the original namespace.
func WithNetNSLink(ns netns.NsHandle, ifName string, work func(link netlink.Link) error) error {
return WithNetNS(ns, func() error {
link, err := netlink.LinkByName(ifName)
Expand All @@ -48,6 +48,7 @@ func WithNetNSLink(ns netns.NsHandle, ifName string, work func(link netlink.Link
})
}

// Objective := WithNetNSByPath will switch to the given namespace path and execute the work function.
func WithNetNSByPath(path string, work func() error) error {
ns, err := netns.GetFromPath(path)
if err != nil {
Expand All @@ -56,10 +57,12 @@ func WithNetNSByPath(path string, work func() error) error {
return WithNetNS(ns, work)
}

// Objective := NSPathByPid will get the namespace name from the process pid.
func NSPathByPid(pid int) string {
return NSPathByPidWithProc("/proc", pid)
}

// Objective := NSPathByPidWithProc will get you the namespace name by having a process proc path.
func NSPathByPidWithProc(procPath string, pid int) string {
return filepath.Join(procPath, fmt.Sprint(pid), "/ns/net")
}
1 change: 1 addition & 0 deletions netlink/tap.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/vishvananda/netns"
)

// Objective := Add the tap device in the namespace fetched from the namspace path.
func (ops defaultNetlinkOps) AttachTap(nsPath string, tapName string, mtu int, ownerUID int, ownerGID int) error {
nsorigin, err := netns.Get()
if err != nil {
Expand Down