diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19cce9a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +firecracker +release-v1.9.1-x86_64 +fc_kernel +fc_rfs +jailer +bin \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..de735f2 --- /dev/null +++ b/Readme.md @@ -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 "(?<=)(firecracker-ci/v1.10/x86_64/vmlinux-5\.10\.[0-9]{3})(?=)" -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. \ No newline at end of file diff --git a/bin/firetest b/bin/firetest index e5c1e4a..770db0a 100755 Binary files a/bin/firetest and b/bin/firetest differ diff --git a/main.go b/main.go index 2437eb7..a67068a 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import "ranjankuldeep/test/methods" func main() { - methods.ExampleJailerConfig_enablingJailer() + methods.JailerEnabledVM() select {} + } diff --git a/methods/jailer.go b/methods/jailer.go index 9bfa8ce..d557b73 100644 --- a/methods/jailer.go +++ b/methods/jailer.go @@ -9,13 +9,35 @@ 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) @@ -23,51 +45,32 @@ func ExampleJailerConfig_enablingJailer() { 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), @@ -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, diff --git a/netlink/netlink.go b/netlink/netlink.go index 83c5651..8a68596 100644 --- a/netlink/netlink.go +++ b/netlink/netlink.go @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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{ @@ -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{ diff --git a/netlink/netns.go b/netlink/netns.go index 099bcfe..c120aa9 100644 --- a/netlink/netns.go +++ b/netlink/netns.go @@ -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() @@ -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) @@ -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 { @@ -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") } diff --git a/netlink/tap.go b/netlink/tap.go index 69c325d..bc872e5 100644 --- a/netlink/tap.go +++ b/netlink/tap.go @@ -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 {