diff options
author | Nick Thomas <nick@gitlab.com> | 2018-04-25 00:42:56 +0300 |
---|---|---|
committer | Nick Thomas <nick@gitlab.com> | 2018-04-27 19:13:46 +0300 |
commit | 7667febecf3bb627d3e0912a59fa1d8918519280 (patch) | |
tree | b7037c5429cdb3eb4e7b205a8c4f8c0aaf1e010a /internal | |
parent | 05c03d65f64021f4a3ead9b627b7293e7b63ca07 (diff) |
Restore the old in-place chroot behaviour as a command-line option
Diffstat (limited to 'internal')
-rw-r--r-- | internal/jail/jail.go | 55 | ||||
-rw-r--r-- | internal/jail/jail_test.go | 43 |
2 files changed, 76 insertions, 22 deletions
diff --git a/internal/jail/jail.go b/internal/jail/jail.go index d2b04be0..22f22eac 100644 --- a/internal/jail/jail.go +++ b/internal/jail/jail.go @@ -21,30 +21,45 @@ type pathAndMode struct { // Jail is a Chroot jail builder type Jail struct { + root string + deleteRoot bool directories []pathAndMode files map[string]pathAndMode bindMounts map[string]string } -// New returns a Jail for path -func New(path string, perm os.FileMode) *Jail { +// Into returns a Jail on path, assuming it already exists on disk. On disposal, +// the jail *will not* remove the path +func Into(path string) *Jail { return &Jail{ - directories: []pathAndMode{pathAndMode{path: path, mode: perm}}, - files: make(map[string]pathAndMode), - bindMounts: make(map[string]string), + root: path, + deleteRoot: false, + files: make(map[string]pathAndMode), + bindMounts: make(map[string]string), } } -// TimestampedJail return a Jail with Path composed by prefix and current timestamp -func TimestampedJail(prefix string, perm os.FileMode) *Jail { +// Create returns a Jail on path, creating the directory if needed. On disposal, +// the jail will remove the path +func Create(path string, perm os.FileMode) *Jail { + jail := Into(path) + jail.deleteRoot = true + jail.directories = append(jail.directories, pathAndMode{path: path, mode: perm}) + + return jail +} + +// CreateTimestamped returns a Jail on a path composed by prefix and current +// timestamp, creating the directory. On disposal, the jail will remove the path +func CreateTimestamped(prefix string, perm os.FileMode) *Jail { jailPath := path.Join(os.TempDir(), fmt.Sprintf("%s-%d", prefix, time.Now().UnixNano())) - return New(jailPath, perm) + return Create(jailPath, perm) } // Path returns the path of the jail func (j *Jail) Path() string { - return j.directories[0].path + return j.root } // Build creates the jail, making directories and copying files. If an error @@ -87,7 +102,27 @@ func (j *Jail) Build() error { } func (j *Jail) removeAll() error { - return os.RemoveAll(j.Path()) + // Deleting the root will remove all child directories, so there's no need + // to traverse files and directories + if j.deleteRoot { + if err := os.RemoveAll(j.Path()); err != nil { + return fmt.Errorf("Can't delete jail %q. %s", j.Path(), err) + } + } else { + for path := range j.files { + if err := os.Remove(path); err != nil { + return fmt.Errorf("Can't delete file in jail %q: %s", path, err) + } + } + + for _, dest := range j.directories { + if err := os.Remove(dest.path); err != nil { + return fmt.Errorf("Can't delete directory in jail %q: %s", dest.path, err) + } + } + } + + return nil } // Dispose erases everything inside the jail diff --git a/internal/jail/jail_test.go b/internal/jail/jail_test.go index 04a00a74..cb242b35 100644 --- a/internal/jail/jail_test.go +++ b/internal/jail/jail_test.go @@ -26,8 +26,8 @@ func TestTimestampedJails(t *testing.T) { prefix := "jail" var mode os.FileMode = 0755 - j1 := jail.TimestampedJail(prefix, mode) - j2 := jail.TimestampedJail(prefix, mode) + j1 := jail.CreateTimestamped(prefix, mode) + j2 := jail.CreateTimestamped(prefix, mode) assert.NotEqual(j1.Path, j2.Path()) } @@ -36,7 +36,7 @@ func TestJailPath(t *testing.T) { assert := assert.New(t) jailPath := tmpJailPath() - cage := jail.New(jailPath, 0755) + cage := jail.Create(jailPath, 0755) assert.Equal(jailPath, cage.Path()) } @@ -45,7 +45,7 @@ func TestJailBuild(t *testing.T) { assert := assert.New(t) jailPath := tmpJailPath() - cage := jail.New(jailPath, 0755) + cage := jail.Create(jailPath, 0755) _, err := os.Stat(cage.Path()) assert.Error(err, "Jail path should not exist before Jail.Build()") @@ -60,7 +60,7 @@ func TestJailBuild(t *testing.T) { func TestJailOnlySupportsOneBindMount(t *testing.T) { jailPath := tmpJailPath() - cage := jail.New(jailPath, 0755) + cage := jail.Create(jailPath, 0755) cage.Bind("/bin", "/bin") cage.Bind("/lib", "/lib") @@ -75,7 +75,7 @@ func TestJailOnlySupportsOneBindMount(t *testing.T) { func TestJailBuildCleansUpWhenMountFails(t *testing.T) { jailPath := tmpJailPath() - cage := jail.New(jailPath, 0755) + cage := jail.Create(jailPath, 0755) cage.Bind("/foo", "/this/path/does/not/exist/so/mount/will/fail") err := cage.Build() @@ -89,7 +89,7 @@ func TestJailDispose(t *testing.T) { assert := assert.New(t) jailPath := tmpJailPath() - cage := jail.New(jailPath, 0755) + cage := jail.Create(jailPath, 0755) err := cage.Build() require.NoError(t, err) @@ -105,7 +105,7 @@ func TestJailDisposeDoNotFailOnMissingPath(t *testing.T) { assert := assert.New(t) jailPath := tmpJailPath() - cage := jail.New(jailPath, 0755) + cage := jail.Create(jailPath, 0755) _, err := os.Stat(cage.Path()) assert.Error(err, "Jail path should not exist") @@ -132,7 +132,7 @@ func TestJailWithCharacterDevice(t *testing.T) { expectedRdev := sys.Rdev jailPath := tmpJailPath() - cage := jail.New(jailPath, 0755) + cage := jail.Create(jailPath, 0755) cage.MkDir("/dev", 0755) require.NoError(t, cage.CharDev("/dev/urandom")) @@ -182,7 +182,7 @@ func TestJailWithFiles(t *testing.T) { t.Run(test.name, func(t *testing.T) { assert := assert.New(t) - cage := jail.TimestampedJail("jail-mkdir", 0755) + cage := jail.CreateTimestamped("jail-mkdir", 0755) for _, dir := range test.directories { cage.MkDir(dir, 0755) } @@ -219,7 +219,7 @@ func TestJailCopyTo(t *testing.T) { content := "hello" - cage := jail.TimestampedJail("check-file-copy", 0755) + cage := jail.CreateTimestamped("check-file-copy", 0755) tmpFile, err := ioutil.TempFile("", "dummy-file") if err != nil { @@ -269,7 +269,7 @@ func TestJailLazyUnbind(t *testing.T) { tmpFile.Close() jailPath := tmpJailPath() - cage := jail.New(jailPath, 0755) + cage := jail.Create(jailPath, 0755) cage.MkDir("/my-bind", 0755) cage.Bind("/my-bind", toBind) @@ -298,3 +298,22 @@ func TestJailLazyUnbind(t *testing.T) { _, err = os.Stat(tmpFilePath) require.NoError(t, err, "disposing a jail should not delete files under binded directories") } + +func TestJailIntoOnlyCleansSubpaths(t *testing.T) { + jailPath := tmpJailPath() + require.NoError(t, os.MkdirAll(jailPath, 0755)) + defer os.RemoveAll(jailPath) + + chroot := jail.Into(jailPath) + chroot.MkDir("/etc", 0755) + chroot.Copy("/etc/resolv.conf") + require.NoError(t, chroot.Build()) + require.NoError(t, chroot.Dispose()) + + _, err := os.Stat(path.Join(jailPath, "/etc/resolv.conf")) + require.True(t, os.IsNotExist(err), "/etc/resolv.conf in jail was not removed") + _, err = os.Stat(path.Join(jailPath, "/etc")) + require.True(t, os.IsNotExist(err), "/etc in jail was not removed") + _, err = os.Stat(jailPath) + require.NoError(t, err, "/ in jail (corresponding to external directory) was removed") +} |