diff --git a/.gitignore b/.gitignore index a411d73..dca86ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ ssh/* image/* !**/.gitkeep +.idea +nim/qsandbox diff --git a/README.md b/README.md index 2893677..1b59028 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # qemu-sandbox -PoC shell sandboxing using QEMU and virtiofsd. Just enter `qsandbox run` to open your current working dir in a temporary QEMU vm. +PoC shell sandboxing using QEMU and virtiofsd. Simply enter `qsandbox run` to open your current working dir in a temporary QEMU vm. ## Installation diff --git a/build.sh b/build.sh index e1bdf96..cde9934 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,8 @@ #!/bin/bash # Based on https://blog.stefan-koch.name/2020/05/31/automation-archlinux-qemu-installation -src=https://ftp.halifax.rwth-aachen.de/archlinux/iso/2021.11.01/archlinux-bootstrap-2021.11.01-x86_64.tar.gz +src="https://mirror.rackspace.com/archlinux/iso/2021.11.01/archlinux-bootstrap-2021.11.01-x86_64.tar.gz" + archive=image/archlinux.tar.gz image=image/image.raw mountpoint=image/arch @@ -14,6 +15,7 @@ mkdir -p $mountpoint mkdir -p ssh qemu-img create -f raw $image 20G + loop="$(sudo losetup --show -f -P $image)" sudo mkfs.ext4 "$loop" sudo mount "$loop" "$mountpoint" @@ -24,16 +26,14 @@ key="$(cat ssh/qemu_ssh.pub)" sudo "$mountpoint/bin/arch-chroot" "$mountpoint" /bin/bash <> /etc/pacman.d/mirrorlist +echo 'Server = https://mirror.rackspace.com/archlinux/\$repo/os/\$arch' >> /etc/pacman.d/mirrorlist pacman-key --init pacman-key --populate archlinux pacman -Syu --noconfirm -pacman -S --noconfirm base linux linux-firmware mkinitcpio dhcpcd dropbear kitty-terminfo -systemctl enable dhcpcd dropbear - -dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key +pacman -S --noconfirm base linux linux-firmware mkinitcpio openssh kitty-terminfo +systemctl enable sshd # Standard Archlinux Setup ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime diff --git a/network.sh b/network.sh new file mode 100644 index 0000000..f2d57dc --- /dev/null +++ b/network.sh @@ -0,0 +1,4 @@ +ip address add 10.0.2.15/24 broadcast + dev ens3 +ip link set dev ens3 up +ip route add 10.0.2.0/24 dev ens3 +ip route add default via 10.0.2.2 dev ens3 diff --git a/nim/commands/help.nim b/nim/commands/help.nim new file mode 100644 index 0000000..2ca5675 --- /dev/null +++ b/nim/commands/help.nim @@ -0,0 +1,16 @@ +import std/strformat +import std/parseopt +import os + +proc help*(args: OptParser) = + let bin = lastPathPart(getAppFilename()) + + echo "qsandbox - temporary sandboxes using qemu" + echo "" + echo "Usage:" + echo &" {bin} run - start sandbox and mount current working dir" + echo &" {bin} list - list running sandboxes" + echo &" {bin} enter - open ssh connection to a sandbox" + echo &" {bin} qemu - start the qemu process for a new sandbox, used by run" + + quit(0) \ No newline at end of file diff --git a/nim/commands/list.nim b/nim/commands/list.nim new file mode 100644 index 0000000..801f751 --- /dev/null +++ b/nim/commands/list.nim @@ -0,0 +1,4 @@ +import std/parseopt + +proc list*(args: OptParser) = + echo "list" \ No newline at end of file diff --git a/nim/commands/qemu.nim b/nim/commands/qemu.nim new file mode 100644 index 0000000..3976ace --- /dev/null +++ b/nim/commands/qemu.nim @@ -0,0 +1,89 @@ +import strformat +import parseopt +import osproc +import posix +import linux +import os + +# should be in the linux module but seems to be missing +const CLONE_NEWUSER = 0x10000000'i32 + +# c bindings +proc unshare(flag: cint): cint {.importc, header: ""} + +proc mount(source: cstring, target: cstring, filesystemtype: cstring, + mountflags: culong, data: pointer): cint {.importc, header:""} + +const + GID_MAP = "/proc/self/gid_map" + UID_MAP = "/proc/self/uid_map" + +proc virtiofsd(paths: seq[string]): Pid = + let uid = getuid() + let gid = getgid() + let pid = fork() + + if pid < 0: + raise newException(OSError, "Fork failed") + + if pid > 0: + # we are the parent + return pid + + # create new mount namespace + if unshare(CLONE_NEWUSER or CLONE_NEWNS) != 0: + raise newException(OSError, "Unshare failed") + + # map our uid to root + writeFile(UID_MAP, &"0 {uid} 1") + writeFile(GID_MAP, &"0 {gid} 1") + + # create a tmpfs in /var/run so virtiofsd can write there + if mount(cstring("tmpfs"), cstring("/var/run"), cstring("tmpfs"), 0, cstring("")) != 0: + raise newException(OSError, "Mount failed") + + # start a virtiofsd process for each mount + var procs {.threadvar.}: seq[Process] + + for i, path in paths: + procs.add(startProcess( + command = "/usr/lib/qemu/virtiofsd", + args = @[&"--socket-path=/tmp/mount.{i}.sock", "-o", &"source={path}"], + options = {poParentStreams} + )) + + onSignal(SIGTERM): + for process in procs: + terminate(process) + + quit(0) + + for process in procs: + discard waitForExit(process) + +proc qemu*(args: OptParser) = + let childPid = virtiofsd(@["/tmp", "/home"]) + + let qemu = startProcess( + command = "qemu-system-x86_64", + options = {poParentStreams, poUsePath}, + args = @[ + "-enable-kvm", "-cpu", "host", "-m", "512m", "-smp", "2", + "-kernel", "/home/martin/code/qemu/build-image/image/vmlinuz-linux", + "-append", "earlyprintk=ttyS0 console=ttyS0 root=/dev/vda rw quiet", + "-initrd" , "/home/martin/code/qemu/build-image/image/initramfs-linux-custom.img", + "-m", "4G", "-object", "memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on", "-numa", "node,memdev=mem", + "-device", "virtio-rng-pci", + "-bios", "/usr/share/qemu/qboot.rom", + "-drive", "if=virtio,file=/home/martin/code/qemu/build-image/image/image.qcow2", + "-netdev", "user,id=net0,hostfwd=tcp::2222-:22", + "-device", "virtio-net-pci,netdev=net0", + "-nodefaults", "-no-user-config", "-nographic", + "-serial", "stdio" + ] + ) + + discard waitForExit(qemu) + + #discard kill(childPid, SIGTERM) + #discard waitPid(childPid,cast[var cint](nil),0) diff --git a/nim/commands/run.nim b/nim/commands/run.nim new file mode 100644 index 0000000..58fc812 --- /dev/null +++ b/nim/commands/run.nim @@ -0,0 +1,4 @@ +import std/parseopt + +proc run*(args: OptParser) = + echo "Run" \ No newline at end of file diff --git a/nim/commands/ssh.nim b/nim/commands/ssh.nim new file mode 100644 index 0000000..6fbf504 --- /dev/null +++ b/nim/commands/ssh.nim @@ -0,0 +1,4 @@ +import std/parseopt + +proc ssh*(args: OptParser) = + echo "ssh" \ No newline at end of file diff --git a/nim/qsandbox.nim b/nim/qsandbox.nim new file mode 100644 index 0000000..6188ded --- /dev/null +++ b/nim/qsandbox.nim @@ -0,0 +1,34 @@ +import parseopt + +import commands/help +import commands/list +import commands/qemu +import commands/run +import commands/ssh + +proc cmd (options: OptParser) = + case options.key: + of "qemu": + qemu(options) + of "run": + run(options) + of "ssh": + ssh(options) + of "list": + list(options) + else: + help(options) + +proc main () = + var options = initOptParser() + options.next() + + case options.kind + of cmdEnd: help(options) + of cmdShortOption, cmdLongOption: + echo "Unkown argument" + quit(1) + of cmdArgument: + cmd(options) + +main() \ No newline at end of file diff --git a/qsandbox b/qsandbox index 6b4afc4..a036cd0 100755 --- a/qsandbox +++ b/qsandbox @@ -82,8 +82,6 @@ function run { tmp_dir="$(mktemp -d --suffix=.qemu)" - echo "$work_dir" - echo "$ssh_port" > "$tmp_dir/ssh" echo "$ssh_port" > "$tmp_dir/app" echo "$work_dir" > "$tmp_dir/work_dir"