ff4503aac6
* client: disable running artifact downloader as nobody This PR reverts a change from Nomad 1.5 where artifact downloads were executed as the nobody user on Linux systems. This was done as an attempt to improve the security model of artifact downloading where third party tools such as git or mercurial would be run as the root user with all the security implications thereof. However, doing so conflicts with Nomad's own advice for securing the Client data directory - which when setup with the recommended directory permissions structure prevents artifact downloads from working as intended. Artifact downloads are at least still now executed as a child process of the Nomad agent, and on modern Linux systems make use of the kernel Landlock feature for limiting filesystem access of the child process. * docs: update upgrade guide for 1.5.1 sandboxing * docs: add cl * docs: add title to upgrade guide fix
105 lines
2.7 KiB
Go
105 lines
2.7 KiB
Go
//go:build linux
|
|
|
|
package getter
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"github.com/shoenig/go-landlock"
|
|
)
|
|
|
|
var (
|
|
// userUID is the current user's uid
|
|
userUID uint32
|
|
|
|
// userGID is the current user's gid
|
|
userGID uint32
|
|
)
|
|
|
|
func init() {
|
|
userUID = uint32(syscall.Getuid())
|
|
userGID = uint32(syscall.Getgid())
|
|
}
|
|
|
|
// attributes returns the system process attributes to run
|
|
// the sandbox process with
|
|
func attributes() *syscall.SysProcAttr {
|
|
uid, gid := credentials()
|
|
return &syscall.SysProcAttr{
|
|
Credential: &syscall.Credential{
|
|
Uid: uid,
|
|
Gid: gid,
|
|
},
|
|
}
|
|
}
|
|
|
|
// credentials returns the UID and GID of the user the child process
|
|
// will run as - for now this is always the same user the Nomad agent is
|
|
// running as.
|
|
func credentials() (uint32, uint32) {
|
|
return userUID, userGID
|
|
}
|
|
|
|
// defaultEnvironment is the default minimal environment variables for Linux.
|
|
func defaultEnvironment(taskDir string) map[string]string {
|
|
tmpDir := filepath.Join(taskDir, "tmp")
|
|
return map[string]string{
|
|
"PATH": "/usr/local/bin:/usr/bin:/bin",
|
|
"TMPDIR": tmpDir,
|
|
}
|
|
}
|
|
|
|
// lockdown isolates this process to only be able to write and
|
|
// create files in the task's task directory.
|
|
// dir - the task directory
|
|
//
|
|
// Only applies to Linux, when available.
|
|
func lockdown(allocDir, taskDir string) error {
|
|
// landlock not present in the kernel, do not sandbox
|
|
if !landlock.Available() {
|
|
return nil
|
|
}
|
|
paths := []*landlock.Path{
|
|
landlock.DNS(),
|
|
landlock.Certs(),
|
|
landlock.Shared(),
|
|
landlock.Dir("/bin", "rx"),
|
|
landlock.Dir("/usr/bin", "rx"),
|
|
landlock.Dir("/usr/local/bin", "rx"),
|
|
landlock.Dir(allocDir, "rwc"),
|
|
landlock.Dir(taskDir, "rwc"),
|
|
}
|
|
paths = append(paths, systemVersionControlGlobalConfigs()...)
|
|
locker := landlock.New(paths...)
|
|
return locker.Lock(landlock.Mandatory)
|
|
}
|
|
|
|
func systemVersionControlGlobalConfigs() []*landlock.Path {
|
|
const (
|
|
gitGlobalFile = "/etc/gitconfig" // https://git-scm.com/docs/git-config#SCOPES
|
|
hgGlobalFile = "/etc/mercurial/hgrc" // https://www.mercurial-scm.org/doc/hgrc.5.html#files
|
|
hgGlobalDir = "/etc/mercurial/hgrc.d" // https://www.mercurial-scm.org/doc/hgrc.5.html#files
|
|
)
|
|
return loadVersionControlGlobalConfigs(gitGlobalFile, hgGlobalFile, hgGlobalDir)
|
|
}
|
|
|
|
func loadVersionControlGlobalConfigs(gitGlobalFile, hgGlobalFile, hgGlobalDir string) []*landlock.Path {
|
|
exists := func(p string) bool {
|
|
_, err := os.Stat(p)
|
|
return err == nil
|
|
}
|
|
result := make([]*landlock.Path, 0, 3)
|
|
if exists(gitGlobalFile) {
|
|
result = append(result, landlock.File(gitGlobalFile, "r"))
|
|
}
|
|
if exists(hgGlobalFile) {
|
|
result = append(result, landlock.File(hgGlobalFile, "r"))
|
|
}
|
|
if exists(hgGlobalDir) {
|
|
result = append(result, landlock.Dir(hgGlobalDir, "r"))
|
|
}
|
|
return result
|
|
}
|