Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-pages.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Thomas <nick@gitlab.com>2018-04-25 18:25:48 +0300
committerNick Thomas <nick@gitlab.com>2018-04-26 18:00:27 +0300
commitb677d1c46d8b062f59cd6238fa1dece396e941ae (patch)
tree9cb69ce4a3aff5470efbf4af372161b11701b71b /internal/jail
parent67e2fe7eebe60809c6b26e24653ea1a8409abf82 (diff)
Create /dev/random and /dev/urandom when daemonizing and jailing
Go on Linux normally uses the getrandom() syscall to get entropy. However, this is onl available for Linux version >= 3.17 Making /dev/random and /dev/urandom available in the chrooted jail allows the daemon to operate correctly on these systems. It's important to handle this automatically, as the previous workaround of manually creating these character devices is made more difficult by the use of a random directory in $TMPDIR.
Diffstat (limited to 'internal/jail')
-rw-r--r--internal/jail/jail.go55
-rw-r--r--internal/jail/jail_test.go37
2 files changed, 91 insertions, 1 deletions
diff --git a/internal/jail/jail.go b/internal/jail/jail.go
index 16690235..d2b04be0 100644
--- a/internal/jail/jail.go
+++ b/internal/jail/jail.go
@@ -5,12 +5,18 @@ import (
"io"
"os"
"path"
+ "syscall"
"time"
+
+ "golang.org/x/sys/unix"
)
type pathAndMode struct {
path string
mode os.FileMode
+
+ // Only respected if mode is os.ModeCharDevice
+ rdev int
}
// Jail is a Chroot jail builder
@@ -64,7 +70,7 @@ func (j *Jail) Build() error {
}
for dest, src := range j.files {
- if err := copyFile(dest, src.path, src.mode); err != nil {
+ if err := handleFile(dest, src); err != nil {
j.removeAll()
return fmt.Errorf("Can't copy %q -> %q. %s", src.path, dest, err)
}
@@ -102,6 +108,34 @@ func (j *Jail) MkDir(path string, perm os.FileMode) {
j.directories = append(j.directories, pathAndMode{path: j.ExternalPath(path), mode: perm})
}
+// CharDev enqueues an mknod operation for the given character device at jail
+// building time
+func (j *Jail) CharDev(path string) error {
+ fi, err := os.Stat(path)
+ if err != nil {
+ return fmt.Errorf("Can't stat %q: %s", path, err)
+ }
+
+ if (fi.Mode() & os.ModeCharDevice) == 0 {
+ return fmt.Errorf("Can't mknod %q: not a character device", path)
+ }
+
+ // Read the device number from the underlying unix implementation of stat()
+ sys, ok := fi.Sys().(*syscall.Stat_t)
+ if !ok {
+ return fmt.Errorf("Couldn't determine rdev for %q", path)
+ }
+
+ jailedDest := j.ExternalPath(path)
+ j.files[jailedDest] = pathAndMode{
+ path: path,
+ mode: fi.Mode(),
+ rdev: int(sys.Rdev),
+ }
+
+ return nil
+}
+
// CopyTo enqueues a file copy operation at jail building time
func (j *Jail) CopyTo(dest, src string) error {
fi, err := os.Stat(src)
@@ -143,6 +177,25 @@ func (j *Jail) ExternalPath(internal string) string {
return path.Join(j.Path(), internal)
}
+func handleFile(dest string, src pathAndMode) error {
+ // Using `io.Copy` on a character device simply doesn't work
+ if (src.mode & os.ModeCharDevice) > 0 {
+ return createCharacterDevice(dest, src)
+ }
+
+ // FIXME: currently, symlinks, block devices, named pipes and other
+ // non-regular files will be `Open`ed and have that content streamed to a
+ // regular file inside the chroot. This is actually desired behaviour for,
+ // e.g., `/etc/resolv.conf`, but was very surprising
+ return copyFile(dest, src.path, src.mode)
+}
+
+func createCharacterDevice(dest string, src pathAndMode) error {
+ unixMode := uint32(src.mode.Perm() | syscall.S_IFCHR)
+
+ return unix.Mknod(dest, unixMode, src.rdev)
+}
+
func copyFile(dest, src string, perm os.FileMode) error {
srcFile, err := os.Open(src)
if err != nil {
diff --git a/internal/jail/jail_test.go b/internal/jail/jail_test.go
index 2acbcd6d..04a00a74 100644
--- a/internal/jail/jail_test.go
+++ b/internal/jail/jail_test.go
@@ -6,6 +6,7 @@ import (
"os"
"path"
"runtime"
+ "syscall"
"testing"
"time"
@@ -113,6 +114,42 @@ func TestJailDisposeDoNotFailOnMissingPath(t *testing.T) {
require.NoError(t, err)
}
+func TestJailWithCharacterDevice(t *testing.T) {
+ if os.Geteuid() != 0 {
+ t.Log("This test only works if run as root")
+ t.SkipNow()
+ }
+
+ // Determine the expected rdev
+ fi, err := os.Stat("/dev/urandom")
+ require.NoError(t, err)
+ sys, ok := fi.Sys().(*syscall.Stat_t)
+ if !ok {
+ t.Log("Couldn't determine expected rdev for /dev/urandom, skipping")
+ t.SkipNow()
+ }
+
+ expectedRdev := sys.Rdev
+
+ jailPath := tmpJailPath()
+ cage := jail.New(jailPath, 0755)
+ cage.MkDir("/dev", 0755)
+
+ require.NoError(t, cage.CharDev("/dev/urandom"))
+ require.NoError(t, cage.Build())
+ defer cage.Dispose()
+
+ fi, err = os.Lstat(path.Join(cage.Path(), "/dev/urandom"))
+ require.NoError(t, err)
+
+ isCharDev := fi.Mode()&os.ModeCharDevice == os.ModeCharDevice
+ assert.True(t, isCharDev, "Created file was not a character device")
+
+ sys, ok = fi.Sys().(*syscall.Stat_t)
+ require.True(t, ok, "Couldn't determine rdev of created character device")
+ assert.Equal(t, expectedRdev, sys.Rdev, "Incorrect rdev for /dev/urandom")
+}
+
func TestJailWithFiles(t *testing.T) {
tests := []struct {
name string