From beaef88a4bb84a096139f0e9774129cf3c0b1cdd Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 14 Nov 2024 16:13:56 -0500 Subject: [PATCH] Clean up workspace dir from inside runner to avoid permission errors. When running privileged docker as an unprivileged user, the files that are created in the WorkspaceDir are created as root. Even if the build were run as non-root, they would not necessarily be the same ownership as the user invoking melange. As a result, WorkspaceDir would not be able to be cleaned up and melange would just leave files there to later be cleaned up with a dangerous 'sudo rm -Rf' by the user. The change here is to clean up the WorkspaceDir from _inside_ the container, where the uid is the same as the uid that created the files. I believe this will waste IO and/or time on the qemu runner, where /home/build isn't actually bind'd in. Later we could expose a CleanWorkspace from the runner that was a noop in qemu. Signed-off-by: Scott Moser --- pkg/build/build.go | 35 +++++++++++++++++++++++++++-------- pkg/build/pipeline.go | 4 +++- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/pkg/build/build.go b/pkg/build/build.go index aefce5641..7db272c7e 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -58,6 +58,22 @@ import ( const melangeOutputDirName = "melange-out" +var shellEmptyDir = []string{ + "sh", "-c", + `d="$1"; +[ $# -eq 1 ] || { echo "must provide dir. got $# args."; exit 1; } +cd "$d" || { echo "failed cd '$d'"; exit 1; } +set -- +for e in * .*; do + [ "$e" = "." -o "$e" = ".." -o "$e" = "*" ] && continue + set -- "$@" "$e" +done +[ $# -gt 0 ] || { echo "nothing to cleanup. $d was empty."; exit 0; } +echo "cleaning Workspace by removing $# file/directories in $d" +rm -Rf "$@"`, + "shellEmptyDir", +} + var ErrSkipThisArch = errors.New("error: skip this arch") type Build struct { @@ -947,6 +963,17 @@ func (b *Build) BuildPackage(ctx context.Context) error { } } + // clean build environment + log.Debugf("cleaning workspacedir") + cleanEnv := map[string]string{} + if err := pr.runner.Run(ctx, pr.config, cleanEnv, append(shellEmptyDir, WorkDir)...); err != nil { + log.Warnf("unable to clean workspace: %s", err) + } + // if the Runner used WorkspaceDir as WorkDir, then this will be empty already. + if err := os.RemoveAll(b.WorkspaceDir); err != nil { + log.Warnf("unable to clean workspace: %s", err) + } + if !b.isBuildLess() { // clean build guest container if err := os.RemoveAll(b.GuestDir); err != nil { @@ -954,14 +981,6 @@ func (b *Build) BuildPackage(ctx context.Context) error { } } - // clean build environment - // TODO(epsilon-phase): implement a way to clean up files that are not owned by the user - // that is running melange. files created inside the build not owned by the build user are - // not be possible to delete with this strategy. - if err := os.RemoveAll(b.WorkspaceDir); err != nil { - log.Warnf("unable to clean workspace: %s", err) - } - // generate APKINDEX.tar.gz and sign it if b.GenerateIndex { packageDir := filepath.Join(b.OutDir, b.Arch.ToAPK()) diff --git a/pkg/build/pipeline.go b/pkg/build/pipeline.go index b1b140c62..3c8b94799 100644 --- a/pkg/build/pipeline.go +++ b/pkg/build/pipeline.go @@ -34,6 +34,8 @@ import ( "github.com/chainguard-dev/clog" ) +const WorkDir = "/home/build" + func (sm *SubstitutionMap) MutateWith(with map[string]string) (map[string]string, error) { nw := maps.Clone(sm.Substitutions) @@ -187,7 +189,7 @@ func (r *pipelineRunner) runPipeline(ctx context.Context, pipeline *config.Pipel envOverride[k] = v } - workdir := "/home/build" + workdir := WorkDir if pipeline.WorkDir != "" { workdir = pipeline.WorkDir }