Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
245c70c
feat(pkg/commands): add support for build secrets
SasSwart Oct 18, 2024
81f924f
feat(pkg/commands): pass secrets into the run command
SasSwart Oct 21, 2024
b509184
feat(pkg/commands): pass secrets into the run marker
SasSwart Oct 21, 2024
4a667d0
chore(pkg): pass a nil linter to tests that now require is
SasSwart Oct 21, 2024
83c8a18
chore(commands): improve variable naming and add documentation
SasSwart Oct 21, 2024
d37707e
chore(commands): improve variable naming and add documentation
SasSwart Oct 21, 2024
7d27c5a
chore(pkg/executor): set a default working directory if one hasn't be…
SasSwart Oct 21, 2024
80e0bdb
deps: bump github.com/moby/buildkit and github.com/docker/docker (#3242)
aaron-prindle Jul 8, 2024
75c841a
chore(deps): slim down to a failing test for reproducibility
SasSwart Oct 22, 2024
c0d174f
chore(deps): bump github.com/moby/buildkit from v0.14.1 to v0.16.0
SasSwart Oct 22, 2024
d1daf05
chore(deps): test against the latest version of docker
SasSwart Oct 22, 2024
bf140c2
chore(deps): test against the latest version of docker
SasSwart Oct 22, 2024
6e3f198
chore(pkg/executor): remove defunct code
SasSwart Oct 22, 2024
2b0d796
chore(integration): add integration tests for build secrets
SasSwart Oct 23, 2024
2999f79
Merge remote-tracking branch 'origin/main' into jjs/build-secrets
SasSwart Oct 23, 2024
df94a9d
fix(pkg/commands): improve code documentation and style
SasSwart Oct 23, 2024
0f29fa1
fix(pkg/integration): test relative targets for docker build secrets
SasSwart Oct 23, 2024
97120de
fix(pkg/integration): test relative targets for docker build secrets
SasSwart Oct 23, 2024
8f144a6
fix(pkg/integration): fix TestLayers
SasSwart Oct 24, 2024
8783c3e
fix(pkg/commands): guarantee secret cleanup after run commands
SasSwart Oct 24, 2024
0bd5bf7
fix(pkg/commands): add tests for fileCreatorCleaner
SasSwart Oct 25, 2024
91a95ae
fix(pkg/commands): use the filesystem package where appropriate
SasSwart Oct 25, 2024
7224b96
fix(pkg/filesystem): remove defunct package level functions
SasSwart Oct 25, 2024
a8c3296
fix(pkg/filesystem): fix an edge case in the fileCreatorCleaner
SasSwart Oct 25, 2024
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
3 changes: 3 additions & 0 deletions integration/dockerfiles/Dockerfile_test_run_mount
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ RUN --mount=type=secret,id=FOO,env=FOO,required=true echo "$FOO" > /etc/foo
RUN --mount=type=secret,id=BAR cat /run/secrets/BAR > /etc/bar
# Make sure we set secrets to custom disk location the same way docker does
RUN --mount=type=secret,id=BAZ,target=/baz.secret cat /baz.secret > /etc/baz
# Make sure relative targets for secret mounts work:
WORKDIR /etc
RUN --mount=type=secret,id=QUX,target=qux.secret cat qux.secret > /etc/qux

# Test with ARG so that we know our implementation of secrets don't override args
ARG file
Expand Down
1 change: 1 addition & 0 deletions integration/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ var secretsMap = map[string][]buildSecret{
{name: "FOO", value: "foo"},
{name: "BAR", value: "bar"},
{name: "BAZ", value: "baz"},
{name: "QUX", value: "qux"},
},
}

Expand Down
5 changes: 3 additions & 2 deletions integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,8 +591,9 @@ func TestBuildWithHTTPError(t *testing.T) {

func TestLayers(t *testing.T) {
offset := map[string]int{
"Dockerfile_test_add": 12,
"Dockerfile_test_scratch": 3,
"Dockerfile_test_add": 12,
"Dockerfile_test_scratch": 3,
"Dockerfile_test_run_mount": 1, // The WORKDIR layer is not present in the kaniko image
}

if os.Getenv("CI") == "true" {
Expand Down
117 changes: 59 additions & 58 deletions pkg/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
return runCommandInExec(config, buildArgs, r.cmd, r.output, r.buildSecrets)
}

func runCommandInExec(config *v1.Config, buildArgs *dockerfile.BuildArgs, cmdRun *instructions.RunCommand, output *RunOutput, buildSecrets []string) error {
func runCommandInExec(config *v1.Config, buildArgs *dockerfile.BuildArgs, cmdRun *instructions.RunCommand, output *RunOutput, buildSecrets []string) (err error) {
if output == nil {
output = &RunOutput{}
}
Expand Down Expand Up @@ -152,7 +152,14 @@ func runCommandInExec(config *v1.Config, buildArgs *dockerfile.BuildArgs, cmdRun
buildSecretsMap[secretName] = secretValue
}

var buildSecretDirOrFilesToClean []string
secretFileManager := FileCreatorCleaner{}
defer func() {
cleanupErr := secretFileManager.Clean()
if err == nil {
err = cleanupErr
}
}()

mounts := instructions.GetMounts(cmdRun)
for _, mount := range mounts {
switch mount.Type {
Expand All @@ -174,17 +181,10 @@ func runCommandInExec(config *v1.Config, buildArgs *dockerfile.BuildArgs, cmdRun
if targetFile == "" {
targetFile = filepath.Join(secretsDir, mount.CacheID)
}

createdDirs, err := mkdirAllAndListCreated(filepath.Dir(targetFile), 0700)
if err != nil {
return errors.Wrap(err, "creating directories for secret file")
}
buildSecretDirOrFilesToClean = append(buildSecretDirOrFilesToClean, createdDirs...)

if err := os.WriteFile(targetFile, []byte(secret), 0600); err != nil {
return errors.Wrap(err, "writing secret to file")
if !filepath.IsAbs(targetFile) {
targetFile = filepath.Join(config.WorkingDir, targetFile)
}
buildSecretDirOrFilesToClean = append(buildSecretDirOrFilesToClean, targetFile)
secretFileManager.MkdirAndWriteFile(targetFile, []byte(secret), 0700, 0600)
}

// We don't return in the block above, because its possible to have both a target and an env.
Expand Down Expand Up @@ -226,68 +226,69 @@ func runCommandInExec(config *v1.Config, buildArgs *dockerfile.BuildArgs, cmdRun
return errors.Wrap(err, "waiting for process to exit")
}

// We need to recursively clean up after any secrets that were written to disk.
// But also, we need to clean up any directories that we created to house those
// secrets. But also we should only clean up that directory if the build process
// hasn't put anything else in there. If it has put anything else non secret in
// there, then that directory is now meant to make it into the container and we
// shouldn't remove it. This loop does that. We iterate in reverse because that
// way we can remove the deepest directories first.
for i := len(buildSecretDirOrFilesToClean) - 1; i >= 0; i-- {
dir := buildSecretDirOrFilesToClean[i]

info, err := filesystem.FS.Stat(dir)
if os.IsNotExist(err) {
continue
} else if err != nil {
return errors.Wrap(err, "statting directory")
}

if info.IsDir() {
err := os.Remove(dir)
if err != nil {
if os.IsExist(err) {
continue
}
return err
}
} else {
err := os.Remove(dir)
if err != nil {
return err
}
}
}

// it's not an error if there are no grandchildren
if err := syscall.Kill(-pgid, syscall.SIGKILL); err != nil && err.Error() != "no such process" {
return err
}
return nil
}

func mkdirAllAndListCreated(path string, perm os.FileMode) ([]string, error) {
var createdDirs []string
absPath, err := filepath.Abs(path)
if err != nil {
return nil, err
}
// FileCreatorCleaner keeps tracks of all files and directories that it created in the order that they were created.
// Once asked to clean up, it will remove all files and directories in the reverse order that they were created.
type FileCreatorCleaner struct {
filesToClean []string
dirsToClean []string
}

func (s *FileCreatorCleaner) MkdirAndWriteFile(path string, data []byte, dirPerm, filePerm os.FileMode) error {
dirPath := filepath.Dir(path)
parentDirs := filepath.SplitList(dirPath)

dirs := filepath.SplitList(absPath)
// Start at the root directory
currentPath := string(os.PathSeparator)

for _, dir := range dirs {
currentPath = filepath.Join(currentPath, dir)
for _, nextDirDown := range parentDirs {
// Traverse one level down
currentPath = filepath.Join(currentPath, nextDirDown)

if _, err := filesystem.FS.Stat(currentPath); os.IsNotExist(err) {
if err := os.Mkdir(currentPath, perm); err != nil {
return createdDirs, err
if err := os.Mkdir(currentPath, dirPerm); err != nil {
return err
}
createdDirs = append(createdDirs, currentPath)
s.dirsToClean = append(s.dirsToClean, currentPath)
}
}

return createdDirs, nil
// With all parent directories created, we can now create the actual secret file
if err := os.WriteFile(path, []byte(data), 0600); err != nil {
return errors.Wrap(err, "writing secret to file")
}
s.filesToClean = append(s.filesToClean, path)

return nil
}

func (s *FileCreatorCleaner) Clean() error {
for i := len(s.filesToClean) - 1; i >= 0; i-- {
if err := os.Remove(s.filesToClean[i]); err != nil {
return err
}
}

for i := len(s.dirsToClean) - 1; i >= 0; i-- {
if err := os.Remove(s.dirsToClean[i]); err != nil {
pathErr := os.PathError{}
// If a path that we need to clean up is not empty, then that means
// that a third party has placed something in there since we created it.
// In that case, we should not remove it, because it no longer belongs exclusively to us.
if errors.As(err, &pathErr); pathErr.Err == syscall.ENOTEMPTY {
continue
}
return err
}
}

return nil
}

// addDefaultHOME adds the default value for HOME if it isn't already set
Expand Down
Loading