diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-11-01 13:52:15 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-11-03 13:53:00 +0300 |
commit | 0e67646e4201dd6399cdc8a7232961f1aa318337 (patch) | |
tree | e795becd256713d9b1cc0866894698986d2198e6 | |
parent | 04af331b09732d8a6d4af4e38e28f0a86d3e6a36 (diff) |
tests: Make the VCS workaround for unprivileged builds global
The testcfg package has some helper functions that build Go binaries
required by Gitaly at runtime. As Go 1.18 has started to run Git as part
of the build process to embed VCS information into the resulting Git
binaries we had two employ two workarounds:
- We intercept Git commands and thus had to use a Git command
factory to set up a proper environment.
- Git refuses to run in repositories not owned by the current user,
and because CI runs tests as an unprivileged user this safety
mechanism kicked in. We thus had to inject the `safe.directory`
Git configuration into the process.
With the introduction of Go 1.19, we furthermore have to change how we
set up the unprivileged user. As a consequence, we also need to start
building binaries outside of our tests as an unprivileged user and thus
the second issue also hits us during the build process.
Now that we have dropped Go 1.17 though we can just bail and do the easy
thing, which is to pass `-buildvcs=false` to Go and be done. This allows
us to get rid of the nasty build hacks and prepares us for Go 1.19.
-rw-r--r-- | .gitlab-ci.yml | 6 | ||||
-rw-r--r-- | internal/testhelper/testcfg/build.go | 62 |
2 files changed, 10 insertions, 58 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 282a31d67..43983a524 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,6 +22,12 @@ variables: PGBOUNCER_VERSION: "1.16.1" BUNDLE_PATH: "${CI_PROJECT_DIR}/.ruby" GOPATH: "${CI_PROJECT_DIR}/.go" + # We run the build as an untrusted user in a source directory owned by + # "root". Running Git commands in that repository will thus fail due to + # Git's `safe.directory` protections, and that in turns breaks the Go + # build. We work around this by telling Go to not embed VCS information in + # the binaries. + GOFLAGS: "-buildvcs=false" # Run tests with an intercepted home directory so that we detect cases where # Gitaly picks up the gitconfig even though it ought not to. GITALY_TESTING_INTERCEPT_HOME: "YesPlease" diff --git a/internal/testhelper/testcfg/build.go b/internal/testhelper/testcfg/build.go index 3d527a041..0eb03031d 100644 --- a/internal/testhelper/testcfg/build.go +++ b/internal/testhelper/testcfg/build.go @@ -1,18 +1,15 @@ package testcfg import ( - "context" "fmt" "os" "os/exec" "path/filepath" - "runtime" "strings" "sync" "testing" "github.com/stretchr/testify/require" - "gitlab.com/gitlab-org/gitaly/v15/internal/git/gittest" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/config" "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper" ) @@ -93,67 +90,15 @@ func BuildBinary(tb testing.TB, targetDir, sourcePath string) string { buildOnce.Do(func() { require.NoFileExists(tb, sharedBinaryPath, "binary has already been built") - cfg := Build(tb) - gitCommandFactory := gittest.NewCommandFactory(tb, cfg) - gitExecEnv := gitCommandFactory.GetExecutionEnvironment(context.TODO()) - - // Unfortunately, Go has started to execute Git as parts of its build process in - // order to embed VCS information into the resulting binary. In Gitaly we're doing a - // bunch of things to verify that we don't ever use Git information from outside of - // our defined parameters: we intercept Git executed via PATH, and we also override - // Git configuration locations. So executing Git without special logic simply does - // not work. - // - // While we could in theory just ask it not to do that via `-buildvcs=false`, this - // option is only understood with Go 1.18+. So we have the option between either - // using logic that is conditional on the Go version here, or alternatively we fix - // the environment to allow for the execution of Git. We opt for the latter here and - // set up a Git command factory. - gitEnvironment := make([]string, 0, len(os.Environ())) - // We need to filter out some environments we set globally in our tests which would // cause Git to not operate correctly. + filteredEnvironment := make([]string, 0, len(os.Environ())) for _, env := range os.Environ() { if !strings.HasPrefix(env, "GIT_DIR=") { - gitEnvironment = append(gitEnvironment, env) + filteredEnvironment = append(filteredEnvironment, env) } } - // Furthermore, as we're using the Git command factory which may or may not use - // bundled Git we need to append some environment variables that make Git find its - // auxiliary helper binaries. - gitEnvironment = append(gitEnvironment, gitExecEnv.EnvironmentVariables...) - - // And last but not least we need to override PATH so that our Git binary from the - // command factory is up front. - gitEnvironment = append(gitEnvironment, fmt.Sprintf( - "PATH=%s:%s", filepath.Dir(gitExecEnv.BinaryPath), os.Getenv("PATH"), - )) - - // Go 1.18 has started to extract VCS information so that it can be embedded into - // the resulting binary and will thus execute Git in the Gitaly repository. In CI, - // the Gitaly repository is owned by a different user than the one that is executing - // tests though, which means that Git will refuse to open the repository because of - // CVE-2022-24765. - // - // Let's override this mechanism by labelling the Git repository as safe. While this - // does in theory make us vulnerable to this exploit, it is clear that any adversary - // would already have arbitrary code execution because we are executing code right - // now that would be controlled by the very same adversary. - // - // Note that we cannot pass `safe.directory` via command line arguments by design. - // Instead, we just override the system-level gitconfig to point to a temporary file - // that contains this setting. - _, currentFile, _, ok := runtime.Caller(0) - require.True(tb, ok) - gitconfigPath := filepath.Join(testhelper.TempDir(tb), "gitconfig") - require.NoError(tb, os.WriteFile(gitconfigPath, []byte( - "[safe]\ndirectory = "+filepath.Join(filepath.Dir(currentFile), "..", "..", "..")+"\n"), 0o400), - ) - gitEnvironment = append(gitEnvironment, - "GIT_CONFIG_SYSTEM="+gitconfigPath, - ) - buildTags := []string{ "static", "system_libgit2", "gitaly_test", } @@ -164,11 +109,12 @@ func BuildBinary(tb testing.TB, targetDir, sourcePath string) string { cmd := exec.Command( "go", "build", + "-buildvcs=false", "-tags", strings.Join(buildTags, ","), "-o", sharedBinaryPath, sourcePath, ) - cmd.Env = gitEnvironment + cmd.Env = filteredEnvironment output, err := cmd.CombinedOutput() require.NoError(tb, err, "building Go executable: %v, output: %q", err, output) |