Skip to content
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

feat(envbuilder.go): add support for build secrets #391

Merged
merged 16 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
38 changes: 29 additions & 9 deletions envbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"github.com/coder/envbuilder/internal/ebutil"
"github.com/coder/envbuilder/internal/workingdir"
"github.com/coder/envbuilder/log"
"github.com/coder/serpent"
"github.com/containerd/platforms"
"github.com/distribution/distribution/v3/configuration"
"github.com/distribution/distribution/v3/registry/handlers"
Expand Down Expand Up @@ -411,6 +412,10 @@
})
}

magicTempDir := workingdir.At(buildParams.BuildContext, workingdir.TempDir)
SasSwart marked this conversation as resolved.
Show resolved Hide resolved
if err := opts.Filesystem.MkdirAll(magicTempDir.Path(), 0o755); err != nil {
return fmt.Errorf("create magic temp dir in build context: %w", err)
}
// In order to allow 'resuming' envbuilder, embed the binary into the image
// if it is being pushed.
// As these files will be owned by root, it is considerate to clean up
Expand All @@ -427,10 +432,6 @@
if err := util.AddAllowedPathToDefaultIgnoreList(workingDir.Features()); err != nil {
return fmt.Errorf("add features to ignore list: %w", err)
}
magicTempDir := workingdir.At(buildParams.BuildContext, workingdir.TempDir)
if err := opts.Filesystem.MkdirAll(magicTempDir.Path(), 0o755); err != nil {
return fmt.Errorf("create magic temp dir in build context: %w", err)
}
// Add the magic directives that embed the binary into the built image.
buildParams.DockerfileContent += workingdir.Directives

Expand Down Expand Up @@ -525,10 +526,10 @@
if val, ok := os.LookupEnv("KANIKO_REGISTRY_MIRROR"); ok {
registryMirror = strings.Split(val, ";")
}
var destinations []string
if opts.CacheRepo != "" {
destinations = append(destinations, opts.CacheRepo)
}
var destinations = []string{"image"}
SasSwart marked this conversation as resolved.
Show resolved Hide resolved
// if opts.CacheRepo != "" {
// destinations = append(destinations, opts.CacheRepo)
// }
kOpts := &config.KanikoOptions{
// Boilerplate!
CustomPlatform: platforms.Format(platforms.Normalize(platforms.DefaultSpec())),
Expand All @@ -538,6 +539,7 @@
RunStderr: stderrWriter,
Destinations: destinations,
NoPush: !opts.PushImage || len(destinations) == 0,
TarPath: filepath.Join(magicTempDir.Path(), "image.tar"),
SasSwart marked this conversation as resolved.
Show resolved Hide resolved
CacheRunLayers: true,
CacheCopyLayers: true,
ForceBuildMetadata: opts.PushImage, // Force layers with no changes to be cached, required for cache probing.
Expand Down Expand Up @@ -573,13 +575,31 @@
Reproducible: opts.PushImage,
}

buildSecrets := serpent.ParseEnviron(os.Environ(), "ENVBUILDER_SECRET_")
if len(buildSecrets) > 0 {
secretsPath := filepath.Join(magicTempDir.Path(), "secrets")
opts.Logger(log.LevelDebug, "writing secrets to %q", secretsPath)
os.Mkdir(secretsPath, 0o755)

Check failure on line 582 in envbuilder.go

View workflow job for this annotation

GitHub Actions / test

Error return value of `os.Mkdir` is not checked (errcheck)
for _, secret := range buildSecrets {
secretPath := filepath.Join(magicTempDir.Path(), "secrets", secret.Name)
if err := os.WriteFile(secretPath, []byte(secret.Value), 0o644); err != nil {
return nil, fmt.Errorf("write secret %q: %w", secret.Name, err)
}
}
}
defer func() {
if err := os.RemoveAll(filepath.Join(magicTempDir.Path(), "secrets")); err != nil {
opts.Logger(log.LevelWarn, "failed to clean up secrets: %s", err)
}
}()

endStage := startStage("🏗️ Building image...")
image, err := executor.DoBuild(kOpts)
if err != nil {
return nil, xerrors.Errorf("do build: %w", err)
}
endStage("🏗️ Built image!")
if opts.PushImage {
if opts.PushImage || true {
SasSwart marked this conversation as resolved.
Show resolved Hide resolved
endStage = startStage("🏗️ Pushing image...")
if err := executor.DoPush(image, kOpts); err != nil {
return nil, xerrors.Errorf("do push: %w", err)
Expand Down
3 changes: 3 additions & 0 deletions examples/buildsecrets/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM alpine:latest

RUN cat /workspace/.envbuilder.tmp/secrets/FOO | sha256sum > /secret_hash.txt
17 changes: 17 additions & 0 deletions examples/buildsecrets/demonstrate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

./../../scripts/build.sh --base=build-secrets-envbuilder
docker run -it --rm \
-e ENVBUILDER_INIT_SCRIPT='/bin/sh' \
-e ENVBUILDER_WORKSPACE_FOLDER=/workspace \
-e ENVBUILDER_SECRET_FOO='this is a secret' \
-v $PWD:/workspace \
build-secrets-envbuilder:latest

# This script will drop you into a shell inside an envbuilder built alpine container.
# Notice that the secret that was set above is nowhere to be found. Yet, it's sha256 is
# present in /secret_hash.txt. This is a demonstration of how secrets can be passed to
# the build process without being exposed in the final running container. If you'd like to
# dig deeper, you can extract the built image, which is inside /workspace/.envbuilder.tmp.
# It's only there for demonstration purposes. We would not keep the tmp dir there
# in the final PR.
5 changes: 5 additions & 0 deletions examples/buildsecrets/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"build": {
"dockerfile": "Dockerfile"
}
}
Loading