Skip to content

Commit

Permalink
Drop user privileges to configured user and chroot
Browse files Browse the repository at this point in the history
After each subprocess has finished their setup phase, the POSIX
privileges are being dropped to a minimum. This includes a chroot to
either the store's path or a just created bottomless pit (empty root
jail directory) and a change of the effective user and group to the
configured pair from the configuration.
  • Loading branch information
oxzi committed Oct 2, 2023
1 parent 6537690 commit c5da57c
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 3 deletions.
67 changes: 67 additions & 0 deletions gosh.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
"bufio"
"context"
"flag"
"fmt"
"os"
"os/exec"
"os/signal"
"os/user"
"strconv"
"time"

"golang.org/x/sys/unix"
Expand All @@ -21,6 +24,9 @@ import (
// repository as it serves both as an example as well as documentation and
// otherwise the documentation will diverge anyways.
type Config struct {
User string
Group string

Store struct {
Path string
}
Expand Down Expand Up @@ -104,6 +110,58 @@ func forkChild(child string, extraFiles []*os.File, ctx context.Context) (*exec.
return cmd, nil
}

// posixPermDrop uses (more or less) POSIX defined options to drop privileges.
//
// Frist, a chroot is set to the given path. Afterwards, the effective UID and
// GID are being set to those of the given user and group.
//
// It says "more or less POSIX" as setresuid(2) and setresgid(2) aren't part of
// any standard (yet), but are supported by most operating systems.
func posixPermDrop(chroot, username, group string) error {
// Lookup requires access to /etc/{passwd,group} - perform before chrooting.
userStruct, err := user.Lookup(username)
if err != nil {
return err
}
userId, err := strconv.ParseInt(userStruct.Uid, 10, 64)
if err != nil {
return err
}
groupStruct, err := user.LookupGroup(group)
if err != nil {
return err
}
groupId, err := strconv.ParseInt(groupStruct.Gid, 10, 64)
if err != nil {
return err
}
uid, gid := int(userId), int(groupId)

err = unix.Chroot(chroot)
if err != nil {
return fmt.Errorf("chroot: %w", err)
}
err = unix.Chdir("/")
if err != nil {
return fmt.Errorf("chdir: %w", err)
}

err = unix.Setgroups([]int{gid})
if err != nil {
return fmt.Errorf("setgroups: %w", err)
}
err = unix.Setresgid(gid, gid, gid)
if err != nil {
return fmt.Errorf("setresgid: %w", err)
}
err = unix.Setresuid(uid, uid, uid)
if err != nil {
return fmt.Errorf("setresuid: %w", err)
}

return nil
}

func mainMonitor(conf Config) {
storeRpcServer, storeRpcClient, err := Socketpair()
if err != nil {
Expand All @@ -127,6 +185,15 @@ func mainMonitor(conf Config) {
log.Fatal(err)
}

bottomlessPit, err := os.MkdirTemp("", "gosh-monitor-chroot")
if err != nil {
log.WithError(err).Fatal("Cannot create bottomless pit jail")
}
err = posixPermDrop(bottomlessPit, conf.User, conf.Group)
if err != nil {
log.WithError(err).Fatal("Cannot drop permissions")
}

<-ctx.Done()
}

Expand Down
5 changes: 5 additions & 0 deletions gosh.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
---

# user and group will be the system user and group to drop permissions to.
user: "_gosh"
group: "_gosh"


# The store section describes the storage server's configuration.
store:
path: "./store"
Expand Down
9 changes: 7 additions & 2 deletions gosh_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import (
)

func mainStore(conf Config) {
log.WithField("store", conf.Store.Path).Info("Starting store child")
log.WithField("config", conf.Store).Debug("Starting store child")

store, err := NewStore(conf.Store.Path, true)
err := posixPermDrop(conf.Store.Path, conf.User, conf.Group)
if err != nil {
log.WithError(err).Fatal("Cannot drop permissions")
}

store, err := NewStore("/", true)
if err != nil {
log.Fatal(err)
}
Expand Down
11 changes: 10 additions & 1 deletion gosh_webserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func mkListenSocket(protocol, bound, unixChmod, unixOwner, unixGroup string) (*o
}

func mainWebserver(conf Config) {
log.Info("Starting web server child")
log.WithField("config", conf.Webserver).Debug("Starting web server child")

rpcConn, err := UnixConnFromFile(os.NewFile(3, ""))
if err != nil {
Expand Down Expand Up @@ -126,6 +126,15 @@ func mainWebserver(conf Config) {
log.WithError(err).Fatal("Cannot create socket to be bound to")
}

bottomlessPit, err := os.MkdirTemp("", "gosh-webserver-chroot")
if err != nil {
log.WithError(err).Fatal("Cannot create bottomless pit jail")
}
err = posixPermDrop(bottomlessPit, conf.User, conf.Group)
if err != nil {
log.WithError(err).Fatal("Cannot drop permissions")
}

server, err := NewServer(
storeClient,
maxFilesize, conf.Webserver.ItemConfig.MaxLifetime,
Expand Down

0 comments on commit c5da57c

Please sign in to comment.