This uses the linux namespaces superpowers to chroot into a rootfs
Last active
July 21, 2016 20:31
-
-
Save xor-gate/810aaa6e8c432c742f30ce2c37e58127 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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