Skip to content

fix: lie about ownership of all files in build context when running as non-root user #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions pkg/executor/cache_probe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
Expand Down Expand Up @@ -165,6 +166,9 @@ COPY foo/baz.txt copied/
})

t.Run("MultiStage", func(t *testing.T) {
if os.Getuid() != 0 {
t.Skip("this test fails because DoBuild is not running as the root user")
}
// Share cache between both builds.
regCache := setupCacheRegistry(t)

Expand Down Expand Up @@ -218,9 +222,13 @@ COPY foo/baz.txt copied/
opts, fn = prepare()
defer fn()
image2, err := DoCacheProbe(opts)
testutil.CheckNoError(t, err)
if err != nil {
t.Fatalf("cache probe failed: %+v", err)
}
digest2, err := image2.Digest()
testutil.CheckNoError(t, err)
if err != nil {
t.Fatalf("digest failed: %+v", err)
}

if digest1.String() != digest2.String() {
t.Errorf("expected %s, got %s", digest1.String(), digest2.String())
Expand Down
28 changes: 15 additions & 13 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ import (
"io"
"math"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
Expand Down Expand Up @@ -97,6 +95,13 @@ type CacheHasherFileInfoSum interface {

// CacheHasher takes into account everything the regular hasher does except for mtime
func CacheHasher() func(string) (string, error) {
// In order to avoid failing cache probes when a cache probe operation
// is run as a non-root user, we need to pretend that files are owned
// by root.
isRoot := os.Getuid() == 0
if isRoot {
logrus.Debugf("kaniko running as root: %t", isRoot)
}
hasher := func(p string) (string, error) {
h := md5.New()
fi, err := filesystem.FS.Lstat(p)
Expand All @@ -114,17 +119,14 @@ func CacheHasher() func(string) (string, error) {

h.Write([]byte(fi.Mode().String()))

// Cian: this is a disgusting hack, but it removes the need for the
// envbuilder binary to be owned by root when doing a cache probe.
// We want to ignore UID and GID changes for the envbuilder binary
// specifically. When building and pushing an image using the envbuilder
// image, the embedded envbuilder binary will most likely be owned by
// root:root. However, when performing a cache probe operation, it is more
// likely that the file will be owned by the UID/GID that is running
// envbuilder, which in this case is not guaranteed to be root.
// Let's just pretend that it is, cross our fingers, and hope for the best.
lyingAboutOwnership := !fi.IsDir() &&
strings.HasSuffix(filepath.Clean(filepath.Dir(p)), ".envbuilder.tmp")
// Cian: this is a disgusting hack. When building and pushing an image
// using the envbuilder image, the files in .envbuilder will most likely
// be owned by root:root. However, when performing a cache probe operation,
// it is almost certain that the UID/GID that is running the cache probe
// operation is not root. This means that the cache probe operation will
// fail unless we lie about the UID/GID of the files used to build the
// image.
lyingAboutOwnership := !fi.IsDir() && !isRoot
if lyingAboutOwnership {
h.Write([]byte(strconv.FormatUint(uint64(0), 36)))
h.Write([]byte(","))
Expand Down
Loading