artifact: git needs more files for private repositories (#16508)
* landlock: git needs more files for private repositories This PR fixes artifact downloading so that git may work when cloning from private repositories. It needs - file read on /etc/passwd - dir read on /root/.ssh - file write on /root/.ssh/known_hosts Add these rules to the landlock rules for the artifact sandbox. * cr: use nonexistent instead of devnull Co-authored-by: Michael Schurter <mschurter@hashicorp.com> * cr: use go-homdir for looking up home directory * pr: pull go-homedir into explicit require * cr: fixup homedir tests in homeless root cases * cl: fix root test for real --------- Co-authored-by: Michael Schurter <mschurter@hashicorp.com>
This commit is contained in:
parent
81b8c52472
commit
5b1970468e
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
artifact: Fixed a bug where artifact downloading failed when using git-ssh
|
||||||
|
```
|
|
@ -7,7 +7,9 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
"github.com/shoenig/go-landlock"
|
"github.com/shoenig/go-landlock"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -42,12 +44,35 @@ func credentials() (uint32, uint32) {
|
||||||
return userUID, userGID
|
return userUID, userGID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findHomeDir returns the home directory as provided by os.UserHomeDir. In case
|
||||||
|
// os.UserHomeDir returns an error, we return /root if the current process is being
|
||||||
|
// run by root, or /dev/null otherwise.
|
||||||
|
func findHomeDir() string {
|
||||||
|
// When running as a systemd unit the process may not have the $HOME
|
||||||
|
// environment variable set, and os.UserHomeDir will return an error.
|
||||||
|
|
||||||
|
// if home is set, just use that
|
||||||
|
if home, err := homedir.Dir(); err == nil {
|
||||||
|
return home
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are the root user, use "/root"
|
||||||
|
if unix.Getuid() == 0 {
|
||||||
|
return "/root"
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing safe to do
|
||||||
|
return "/nonexistent"
|
||||||
|
}
|
||||||
|
|
||||||
// defaultEnvironment is the default minimal environment variables for Linux.
|
// defaultEnvironment is the default minimal environment variables for Linux.
|
||||||
func defaultEnvironment(taskDir string) map[string]string {
|
func defaultEnvironment(taskDir string) map[string]string {
|
||||||
tmpDir := filepath.Join(taskDir, "tmp")
|
tmpDir := filepath.Join(taskDir, "tmp")
|
||||||
|
homeDir := findHomeDir()
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"PATH": "/usr/local/bin:/usr/bin:/bin",
|
"PATH": "/usr/local/bin:/usr/bin:/bin",
|
||||||
"TMPDIR": tmpDir,
|
"TMPDIR": tmpDir,
|
||||||
|
"HOME": homeDir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,26 +96,60 @@ func lockdown(allocDir, taskDir string) error {
|
||||||
landlock.Dir(allocDir, "rwc"),
|
landlock.Dir(allocDir, "rwc"),
|
||||||
landlock.Dir(taskDir, "rwc"),
|
landlock.Dir(taskDir, "rwc"),
|
||||||
}
|
}
|
||||||
paths = append(paths, systemVersionControlGlobalConfigs()...)
|
|
||||||
|
paths = append(paths, additionalFilesForVCS()...)
|
||||||
locker := landlock.New(paths...)
|
locker := landlock.New(paths...)
|
||||||
return locker.Lock(landlock.Mandatory)
|
return locker.Lock(landlock.Mandatory)
|
||||||
}
|
}
|
||||||
|
|
||||||
func systemVersionControlGlobalConfigs() []*landlock.Path {
|
func additionalFilesForVCS() []*landlock.Path {
|
||||||
const (
|
const (
|
||||||
|
sshDir = ".ssh" // git ssh
|
||||||
|
knownHosts = ".ssh/known_hosts" // git ssh
|
||||||
|
etcPasswd = "/etc/passwd" // git ssh
|
||||||
gitGlobalFile = "/etc/gitconfig" // https://git-scm.com/docs/git-config#SCOPES
|
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
|
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
|
hgGlobalDir = "/etc/mercurial/hgrc.d" // https://www.mercurial-scm.org/doc/hgrc.5.html#files
|
||||||
)
|
)
|
||||||
return loadVersionControlGlobalConfigs(gitGlobalFile, hgGlobalFile, hgGlobalDir)
|
return filesForVCS(
|
||||||
|
sshDir,
|
||||||
|
knownHosts,
|
||||||
|
etcPasswd,
|
||||||
|
gitGlobalFile,
|
||||||
|
hgGlobalFile,
|
||||||
|
hgGlobalDir,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadVersionControlGlobalConfigs(gitGlobalFile, hgGlobalFile, hgGlobalDir string) []*landlock.Path {
|
func filesForVCS(
|
||||||
|
sshDir,
|
||||||
|
knownHosts,
|
||||||
|
etcPasswd,
|
||||||
|
gitGlobalFile,
|
||||||
|
hgGlobalFile,
|
||||||
|
hgGlobalDir string) []*landlock.Path {
|
||||||
|
|
||||||
|
// omit ssh if there is no home directory
|
||||||
|
home := findHomeDir()
|
||||||
|
sshDir = filepath.Join(home, sshDir)
|
||||||
|
knownHosts = filepath.Join(home, knownHosts)
|
||||||
|
|
||||||
|
// only add if a path exists
|
||||||
exists := func(p string) bool {
|
exists := func(p string) bool {
|
||||||
_, err := os.Stat(p)
|
_, err := os.Stat(p)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
result := make([]*landlock.Path, 0, 3)
|
|
||||||
|
result := make([]*landlock.Path, 0, 6)
|
||||||
|
if exists(sshDir) {
|
||||||
|
result = append(result, landlock.Dir(sshDir, "r"))
|
||||||
|
}
|
||||||
|
if exists(knownHosts) {
|
||||||
|
result = append(result, landlock.File(knownHosts, "rw"))
|
||||||
|
}
|
||||||
|
if exists(etcPasswd) {
|
||||||
|
result = append(result, landlock.File(etcPasswd, "r"))
|
||||||
|
}
|
||||||
if exists(gitGlobalFile) {
|
if exists(gitGlobalFile) {
|
||||||
result = append(result, landlock.File(gitGlobalFile, "r"))
|
result = append(result, landlock.File(gitGlobalFile, "r"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,42 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
"github.com/shoenig/go-landlock"
|
"github.com/shoenig/go-landlock"
|
||||||
"github.com/shoenig/test/must"
|
"github.com/shoenig/test/must"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUtil_loadVersionControlGlobalConfigs(t *testing.T) {
|
func TestUtil_loadVersionControlGlobalConfigs(t *testing.T) {
|
||||||
|
// not parallel
|
||||||
|
|
||||||
const filePerm = 0o644
|
const filePerm = 0o644
|
||||||
const dirPerm = 0o755
|
const dirPerm = 0o755
|
||||||
fakeEtc := t.TempDir()
|
|
||||||
|
|
||||||
var (
|
fakeEtc := t.TempDir()
|
||||||
gitFile = filepath.Join(fakeEtc, "gitconfig")
|
fakeHome := t.TempDir()
|
||||||
hgFile = filepath.Join(fakeEtc, "hgrc")
|
|
||||||
hgDir = filepath.Join(fakeEtc, "hgrc.d")
|
homedir.DisableCache = true
|
||||||
|
t.Cleanup(func() {
|
||||||
|
homedir.DisableCache = false
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Setenv("HOME", fakeHome)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ssh = ".ssh"
|
||||||
|
knownHosts = ".ssh/known_hosts"
|
||||||
)
|
)
|
||||||
|
|
||||||
err := os.WriteFile(gitFile, []byte("git"), filePerm)
|
var (
|
||||||
|
gitConfig = filepath.Join(fakeEtc, "gitconfig")
|
||||||
|
hgFile = filepath.Join(fakeEtc, "hgrc")
|
||||||
|
hgDir = filepath.Join(fakeEtc, "hgrc.d")
|
||||||
|
etcPasswd = filepath.Join(fakeEtc, "passwd")
|
||||||
|
sshDir = filepath.Join(fakeHome, ssh)
|
||||||
|
knownHostsFile = filepath.Join(fakeHome, knownHosts)
|
||||||
|
)
|
||||||
|
|
||||||
|
err := os.WriteFile(gitConfig, []byte("git"), filePerm)
|
||||||
must.NoError(t, err)
|
must.NoError(t, err)
|
||||||
|
|
||||||
err = os.WriteFile(hgFile, []byte("hg"), filePerm)
|
err = os.WriteFile(hgFile, []byte("hg"), filePerm)
|
||||||
|
@ -31,9 +51,21 @@ func TestUtil_loadVersionControlGlobalConfigs(t *testing.T) {
|
||||||
err = os.Mkdir(hgDir, dirPerm)
|
err = os.Mkdir(hgDir, dirPerm)
|
||||||
must.NoError(t, err)
|
must.NoError(t, err)
|
||||||
|
|
||||||
paths := loadVersionControlGlobalConfigs(gitFile, hgFile, hgDir)
|
err = os.WriteFile(etcPasswd, []byte("x:y:z"), filePerm)
|
||||||
|
must.NoError(t, err)
|
||||||
|
|
||||||
|
err = os.Mkdir(sshDir, dirPerm)
|
||||||
|
must.NoError(t, err)
|
||||||
|
|
||||||
|
err = os.WriteFile(knownHostsFile, []byte("abc123"), filePerm)
|
||||||
|
must.NoError(t, err)
|
||||||
|
|
||||||
|
paths := filesForVCS(ssh, knownHosts, etcPasswd, gitConfig, hgFile, hgDir)
|
||||||
must.SliceEqual(t, []*landlock.Path{
|
must.SliceEqual(t, []*landlock.Path{
|
||||||
landlock.File(gitFile, "r"),
|
landlock.Dir(sshDir, "r"),
|
||||||
|
landlock.File(knownHostsFile, "rw"),
|
||||||
|
landlock.File(etcPasswd, "r"),
|
||||||
|
landlock.File(gitConfig, "r"),
|
||||||
landlock.File(hgFile, "r"),
|
landlock.File(hgFile, "r"),
|
||||||
landlock.Dir(hgDir, "r"),
|
landlock.Dir(hgDir, "r"),
|
||||||
}, paths)
|
}, paths)
|
||||||
|
|
|
@ -2,12 +2,14 @@ package getter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/go-getter"
|
"github.com/hashicorp/go-getter"
|
||||||
"github.com/hashicorp/nomad/ci"
|
"github.com/hashicorp/nomad/ci"
|
||||||
"github.com/hashicorp/nomad/client/testutil"
|
"github.com/hashicorp/nomad/client/testutil"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
"github.com/shoenig/test/must"
|
"github.com/shoenig/test/must"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -142,21 +144,31 @@ func TestUtil_getTaskDir(t *testing.T) {
|
||||||
|
|
||||||
func TestUtil_environment(t *testing.T) {
|
func TestUtil_environment(t *testing.T) {
|
||||||
// not parallel
|
// not parallel
|
||||||
|
|
||||||
testutil.RequireLinux(t)
|
testutil.RequireLinux(t)
|
||||||
|
|
||||||
|
homedir.DisableCache = true
|
||||||
|
t.Cleanup(func() {
|
||||||
|
homedir.DisableCache = false
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("default", func(t *testing.T) {
|
t.Run("default", func(t *testing.T) {
|
||||||
|
t.Setenv("HOME", "/test")
|
||||||
result := environment("/a/b/c", "")
|
result := environment("/a/b/c", "")
|
||||||
must.Eq(t, []string{
|
must.Eq(t, []string{
|
||||||
|
"HOME=/test",
|
||||||
"PATH=/usr/local/bin:/usr/bin:/bin",
|
"PATH=/usr/local/bin:/usr/bin:/bin",
|
||||||
"TMPDIR=/a/b/c/tmp",
|
"TMPDIR=/a/b/c/tmp",
|
||||||
}, result)
|
}, result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("append", func(t *testing.T) {
|
t.Run("append", func(t *testing.T) {
|
||||||
|
t.Setenv("HOME", "/test")
|
||||||
t.Setenv("ONE", "1")
|
t.Setenv("ONE", "1")
|
||||||
t.Setenv("TWO", "2")
|
t.Setenv("TWO", "2")
|
||||||
result := environment("/a/b/c", "ONE,TWO")
|
result := environment("/a/b/c", "ONE,TWO")
|
||||||
must.Eq(t, []string{
|
must.Eq(t, []string{
|
||||||
|
"HOME=/test",
|
||||||
"ONE=1",
|
"ONE=1",
|
||||||
"PATH=/usr/local/bin:/usr/bin:/bin",
|
"PATH=/usr/local/bin:/usr/bin:/bin",
|
||||||
"TMPDIR=/a/b/c/tmp",
|
"TMPDIR=/a/b/c/tmp",
|
||||||
|
@ -165,19 +177,61 @@ func TestUtil_environment(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("override", func(t *testing.T) {
|
t.Run("override", func(t *testing.T) {
|
||||||
|
t.Setenv("HOME", "/test")
|
||||||
t.Setenv("PATH", "/opt/bin")
|
t.Setenv("PATH", "/opt/bin")
|
||||||
t.Setenv("TMPDIR", "/scratch")
|
t.Setenv("TMPDIR", "/scratch")
|
||||||
result := environment("/a/b/c", "PATH,TMPDIR")
|
result := environment("/a/b/c", "PATH,TMPDIR")
|
||||||
must.Eq(t, []string{
|
must.Eq(t, []string{
|
||||||
|
"HOME=/test",
|
||||||
"PATH=/opt/bin",
|
"PATH=/opt/bin",
|
||||||
"TMPDIR=/scratch",
|
"TMPDIR=/scratch",
|
||||||
}, result)
|
}, result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("missing", func(t *testing.T) {
|
t.Run("missing", func(t *testing.T) {
|
||||||
|
t.Setenv("HOME", "/test")
|
||||||
result := environment("/a/b/c", "DOES_NOT_EXIST")
|
result := environment("/a/b/c", "DOES_NOT_EXIST")
|
||||||
must.Eq(t, []string{
|
must.Eq(t, []string{
|
||||||
"DOES_NOT_EXIST=",
|
"DOES_NOT_EXIST=",
|
||||||
|
"HOME=/test",
|
||||||
|
"PATH=/usr/local/bin:/usr/bin:/bin",
|
||||||
|
"TMPDIR=/a/b/c/tmp",
|
||||||
|
}, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("homeless non-root", func(t *testing.T) {
|
||||||
|
testutil.RequireNonRoot(t)
|
||||||
|
|
||||||
|
// assert we fallback via go-homdir ...
|
||||||
|
userHome, err := homedir.Dir()
|
||||||
|
must.NoError(t, err)
|
||||||
|
|
||||||
|
// ... when HOME env var is not set, as is the case in some systemd setups
|
||||||
|
t.Setenv("HOME", "")
|
||||||
|
|
||||||
|
result := environment("/a/b/c", "")
|
||||||
|
must.Eq(t, []string{
|
||||||
|
fmt.Sprintf("HOME=%s", userHome),
|
||||||
|
"PATH=/usr/local/bin:/usr/bin:/bin",
|
||||||
|
"TMPDIR=/a/b/c/tmp",
|
||||||
|
}, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("homeless root", func(t *testing.T) {
|
||||||
|
testutil.RequireRoot(t)
|
||||||
|
|
||||||
|
t.Setenv("HOME", "/root") // fake running as full root
|
||||||
|
|
||||||
|
// assert we fallback via go-homdir ...
|
||||||
|
userHome, err := homedir.Dir()
|
||||||
|
must.NoError(t, err)
|
||||||
|
|
||||||
|
// ... when HOME env var is not set, as is the case in some systemd setups
|
||||||
|
t.Setenv("HOME", "")
|
||||||
|
|
||||||
|
result := environment("/a/b/c", "")
|
||||||
|
must.Eq(t, []string{
|
||||||
|
fmt.Sprintf("HOME=%s", userHome),
|
||||||
"PATH=/usr/local/bin:/usr/bin:/bin",
|
"PATH=/usr/local/bin:/usr/bin:/bin",
|
||||||
"TMPDIR=/a/b/c/tmp",
|
"TMPDIR=/a/b/c/tmp",
|
||||||
}, result)
|
}, result)
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -89,6 +89,7 @@ require (
|
||||||
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
|
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
|
||||||
github.com/mitchellh/copystructure v1.2.0
|
github.com/mitchellh/copystructure v1.2.0
|
||||||
github.com/mitchellh/go-glint v0.0.0-20210722152315-6515ceb4a127
|
github.com/mitchellh/go-glint v0.0.0-20210722152315-6515ceb4a127
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b
|
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b
|
||||||
github.com/mitchellh/go-testing-interface v1.14.1
|
github.com/mitchellh/go-testing-interface v1.14.1
|
||||||
github.com/mitchellh/hashstructure v1.1.0
|
github.com/mitchellh/hashstructure v1.1.0
|
||||||
|
@ -226,7 +227,6 @@ require (
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.12 // indirect
|
github.com/mattn/go-runewidth v0.0.12 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
|
||||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||||
github.com/mitchellh/pointerstructure v1.2.1
|
github.com/mitchellh/pointerstructure v1.2.1
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
|
|
Loading…
Reference in New Issue