Exit from a chroot with Golang

Lately I needed to chroot into a directory, do some work and then exit from it using Golang, but I could only find an example for Python online.

So here comes its Go equivalent! I decided to make a helper function for this :

import (
    "os"
    "syscall"
)

func Chroot(path string) (func() error, error) {
    root, err := os.Open("/")
    if err != nil {
        return nil, err
    }

    if err := syscall.Chroot(path); err != nil {
        root.Close()
        return nil, err
    }

    return func() error {
        defer root.Close()
        if err := root.Chdir(); err != nil {
            return err
        }
        return syscall.Chroot(".")
    }, nil
}

Here we’re opening the real root first, just to keep a file descriptor around. Then once we have chrooted we’ll still be able to call root.Chdir(), which will trigger fchdir(2) behind the scene.

Here is an example on how to use that helper function :

package main

func main() {
    exit, err := Chroot("/path/to/dir")
    if err != nil {
        panic(err)
    }

    // do some work
    //...

    // exit from the chroot
    if err := exit(); err != nil {
        panic(err)
    }
}

Please note that chroot(2) does not change the current working directory, so depending on your needs you might want to call os.Chdir(“/”) after you chrooted.

  • Guy Tabrar

    I’m getting ‘operation not permitted’ when trying to exit the chroot using your function on FreeBSD. Any ideas?

    Thanks!

    • Bilal

      According to the FreeBSD chroot syscall doc :

      If `kern.chroot_allow_open_directories’ is set to one (the default),
      chroot() will fail with EPERM if there are any directories open and the
      process is already subject to the chroot() system call.

      It seems like you’d have to set this sysctl variable to 2 for example (even temporarily) to be able to use this trick.

      Hope that helps.

      • Guy Tabrar

        That worked – thank you very much for your help!