Skip to content

Instantly share code, notes, and snippets.

@xor-gate
Last active July 21, 2016 20:31
Show Gist options
  • Save xor-gate/810aaa6e8c432c742f30ce2c37e58127 to your computer and use it in GitHub Desktop.
Save xor-gate/810aaa6e8c432c742f30ce2c37e58127 to your computer and use it in GitHub Desktop.

Blaat

This uses the linux namespaces superpowers to chroot into a rootfs

package main
import (
"fmt"
"os"
"os/exec"
"syscall"
"path/filepath"
)
func main() {
switch os.Args[1] {
case "run":
parent()
case "child":
child()
default:
panic("wat should I do")
}
}
func parent() {
cmd := exec.Command(os.Args[0], append([]string{"child"}, os.Args[2:]...)...)
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNET,
Pdeathsig: syscall.SIGKILL,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Println("ERROR", err)
os.Exit(1)
}
}
func pivotRoot(root string) error {
if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil {
return fmt.Errorf("Error creating mount namespace before pivot: %v", err)
}
//syscall.Unmount(root, 0)
//mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)
//mount(new_root, new_root, "bind", MS_BIND|MS_REC, NULL)
// OMG !!! https://lists.freedesktop.org/archives/systemd-devel/2014-May/019193.html
// we need this to satisfy restriction:
// "new_root and put_old must not be on the same filesystem as the current root"
// strace mount --make-rprivate / -> mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) = 0
if err := syscall.Mount("none", "/", "", syscall.MS_SLAVE|syscall.MS_REC, ""); err != nil {
return fmt.Errorf("Mount rootfs to itself error: %v", err)
}
if err := syscall.Mount(root, root, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
return fmt.Errorf("Mount rootfs to itself error: %v", err)
}
// create rootfs/.pivot_root as path for old_root
pivotDir := filepath.Join(root, ".pivot_root")
syscall.Unmount(pivotDir, syscall.MNT_DETACH)
os.Remove(pivotDir)
if err := os.Mkdir(pivotDir, 0777); err != nil {
return err
}
fmt.Printf("Pivot root dir: %s\n", pivotDir)
fmt.Printf("Pivot root to %s\n", root)
// pivot_root to rootfs, now old_root is mounted in rootfs/.pivot_root
// mounts from it still can be seen in `mount`
if err := syscall.PivotRoot(root, pivotDir); err != nil {
syscall.Unmount(root, 0)
//os.Remove(pivotDir)
return fmt.Errorf("pivot_root %v", err)
}
// change working directory to /
// it is recommendation from man-page
if err := syscall.Chdir("/"); err != nil {
return fmt.Errorf("chdir / %v", err)
}
// path to pivot root now changed, update
pivotDir = filepath.Join("/", ".pivot_root")
// Make the pivotDir (where the old root lives) private so it can be unmounted without propagating to the host
if err := syscall.Mount("", pivotDir, "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
return fmt.Errorf("Error making old root private after pivot: %v", err)
}
// umount rootfs/.pivot_root(which is now /.pivot_root) with all submounts
// now we have only mounts that we mounted ourself in `mount`
if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil {
return fmt.Errorf("unmount pivot_root dir %v", err)
}
// remove temporary directory
return os.Remove(pivotDir)
}
func child() {
/*
root := "/"
if err := syscall.Mount(root, root, "bind,ro", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
return
}
must(os.MkdirAll("/.oldrootfs", 0700))
must(syscall.PivotRoot("/", "/.oldrootfs"))
root := "/var/lib/lxc/test/rootfs"
syscall.Mount("proc", "/proc", "proc", syscall.MS_BIND|syscall.MS_REC, "")
syscall.Mount(root, root, "bind,ro", syscall.MS_BIND|syscall.MS_REC, "")
//pivotRoot(root)
os.Chdir("/")
if err := syscall.Sethostname([]byte("boem")); err != nil {
fmt.Printf("Sethostname: %v\n", err)
return
}
*/
syscall.Clearenv()
root := "/var/lib/lxc/test/rootfs"
err := pivotRoot(root)
if (err != nil) {
fmt.Println("pivotRoot: ", err)
return
}
syscall.Mount("none", "/proc", "", syscall.MS_SLAVE|syscall.MS_REC, "")
syscall.Mount("proc", "/proc", "proc", 0, "")
syscall.Mount("none", "/sys", "", syscall.MS_SLAVE|syscall.MS_REC, "")
syscall.Mount("sys", "/sys", "sys", 0, "")
syscall.Mount("none", "/tmpfs", "", syscall.MS_SLAVE|syscall.MS_REC, "")
syscall.Mount("tmpfs", "/tmp", "tmpfs", 0, "")
if err := syscall.Sethostname([]byte("boem")); err != nil {
fmt.Printf("Sethostname: %v\n", err)
return
}
cmd := exec.Command(os.Args[2], os.Args[3:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Println("ERROR", err)
os.Exit(1)
}
}
func must(err error) {
if err != nil {
panic(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment