Skip to content
Merged
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
25 changes: 2 additions & 23 deletions bake/bake.go
Original file line number Diff line number Diff line change
Expand Up @@ -1330,16 +1330,6 @@ func updateContext(t *build.Inputs, inp *Input) {
t.ContextPath = inp.URL
}

func isRemoteContext(t build.Inputs, inp *Input) bool {
if urlutil.IsRemoteURL(t.ContextPath) {
return true
}
if inp != nil && urlutil.IsRemoteURL(inp.URL) && !strings.HasPrefix(t.ContextPath, "cwd://") {
return true
}
return false
}

func collectLocalPaths(t build.Inputs) []string {
var out []string
if t.ContextState == nil {
Expand Down Expand Up @@ -1509,19 +1499,8 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
bo.Platforms = platforms

secrets := t.Secrets
if isRemoteContext(bi, inp) {
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_TOKEN"); ok {
secrets = append(secrets, &buildflags.Secret{
ID: llb.GitAuthTokenKey,
Env: "BUILDX_BAKE_GIT_AUTH_TOKEN",
})
}
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_HEADER"); ok {
secrets = append(secrets, &buildflags.Secret{
ID: llb.GitAuthHeaderKey,
Env: "BUILDX_BAKE_GIT_AUTH_HEADER",
})
}
if inp != nil && shouldAttachGitAuthSecrets(inp.URL, bi.ContextPath) {
secrets = append(secrets, gitAuthSecretsFromEnv(inp.URL)...)
}
bo.SecretSpecs = secrets.Normalize()
secretAttachment, err := build.CreateSecrets(bo.SecretSpecs)
Expand Down
78 changes: 78 additions & 0 deletions bake/gitauth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package bake

import (
"os"
"strings"

"github.com/docker/buildx/util/buildflags"
"github.com/docker/buildx/util/urlutil"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/util/gitutil"
)

const (
bakeGitAuthTokenEnv = "BUILDX_BAKE_GIT_AUTH_TOKEN" // #nosec G101 -- environment variable key, not a credential
bakeGitAuthHeaderEnv = "BUILDX_BAKE_GIT_AUTH_HEADER"
)

func gitAuthSecretsFromEnv(remoteURL string) buildflags.Secrets {
return gitAuthSecretsFromEnviron(os.Environ(), remoteURL)
}

func gitAuthSecretsFromEnviron(environ []string, remoteURL string) buildflags.Secrets {
host, ok := gitAuthHostFromURL(remoteURL)
if !ok {
return nil
}
secrets := make(buildflags.Secrets, 0, 2)
secrets = append(secrets, gitAuthSecretsForEnv(llb.GitAuthTokenKey, bakeGitAuthTokenEnv, environ, host)...)
secrets = append(secrets, gitAuthSecretsForEnv(llb.GitAuthHeaderKey, bakeGitAuthHeaderEnv, environ, host)...)
return secrets
}

func gitAuthSecretsForEnv(secretIDPrefix, envPrefix string, environ []string, host string) buildflags.Secrets {
envKey, ok := findGitAuthEnvKey(envPrefix, environ)
if !ok || host == "" {
return nil
}
return buildflags.Secrets{&buildflags.Secret{
ID: secretIDPrefix + "." + host,
Env: envKey,
}}
}

func shouldAttachGitAuthSecrets(inputURL, contextPath string) bool {
if !urlutil.IsRemoteURL(inputURL) || !urlutil.IsRemoteURL(contextPath) {
return false
}
inputHost, ok := gitAuthHostFromURL(inputURL)
if !ok {
return false
}
contextHost, ok := gitAuthHostFromURL(contextPath)
if !ok {
return false
}
return strings.EqualFold(inputHost, contextHost)
}

func gitAuthHostFromURL(remoteURL string) (string, bool) {
gitURL, err := gitutil.ParseURL(remoteURL)
if err != nil || gitURL.Host == "" {
return "", false
}
return gitURL.Host, true
}

func findGitAuthEnvKey(envKey string, environ []string) (string, bool) {
for _, env := range environ {
key, _, ok := strings.Cut(env, "=")
if !ok {
continue
}
if strings.EqualFold(key, envKey) {
return key, true
}
}
return "", false
}
83 changes: 83 additions & 0 deletions bake/gitauth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package bake

import (
"testing"

"github.com/docker/buildx/util/buildflags"
"github.com/moby/buildkit/client/llb"
"github.com/stretchr/testify/require"
)

func TestGitAuthSecretsFromEnviron(t *testing.T) {
t.Run("empty without remote url", func(t *testing.T) {
secrets := gitAuthSecretsFromEnviron([]string{
bakeGitAuthTokenEnv + "=token",
bakeGitAuthHeaderEnv + "=basic",
}, "")
require.Empty(t, secrets)
})
t.Run("derives host from remote url", func(t *testing.T) {
secrets := gitAuthSecretsFromEnviron([]string{
bakeGitAuthTokenEnv + "=token",
bakeGitAuthHeaderEnv + "=basic",
}, "https://example.com/org/repo.git")
require.Equal(t, []string{
llb.GitAuthTokenKey + ".example.com|" + bakeGitAuthTokenEnv,
llb.GitAuthHeaderKey + ".example.com|" + bakeGitAuthHeaderEnv,
}, secretPairs(secrets))
})
t.Run("ignores host suffixed keys", func(t *testing.T) {
secrets := gitAuthSecretsFromEnviron([]string{
bakeGitAuthTokenEnv + ".example.com=token",
bakeGitAuthHeaderEnv + ".example.com=basic",
}, "https://example.com/org/repo.git")
require.Empty(t, secrets)
})
}

func TestShouldAttachGitAuthSecrets(t *testing.T) {
tests := []struct {
name string
inputURL string
contextPath string
want bool
}{
{
name: "same host http urls",
inputURL: "https://example.com/org/repo.git",
contextPath: "https://example.com/another/repo.git",
want: true,
},
{
name: "same host mixed git url styles",
inputURL: "https://example.com/org/repo.git",
contextPath: "git@example.com:another/repo.git",
want: true,
},
{
name: "different hosts",
inputURL: "https://example.com/org/repo.git",
contextPath: "https://other.example.com/org/repo.git",
want: false,
},
{
name: "non remote context",
inputURL: "https://example.com/org/repo.git",
contextPath: "cwd://src",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, shouldAttachGitAuthSecrets(tt.inputURL, tt.contextPath))
})
}
}

func secretPairs(secrets buildflags.Secrets) []string {
out := make([]string, 0, len(secrets))
for _, s := range secrets {
out = append(out, s.ID+"|"+s.Env)
}
return out
}
15 changes: 1 addition & 14 deletions bake/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,7 @@ func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, name
}}); err == nil {
sessions = append(sessions, ssh)
}
var gitAuthSecrets []*buildflags.Secret
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_TOKEN"); ok {
gitAuthSecrets = append(gitAuthSecrets, &buildflags.Secret{
ID: llb.GitAuthTokenKey,
Env: "BUILDX_BAKE_GIT_AUTH_TOKEN",
})
}
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_HEADER"); ok {
gitAuthSecrets = append(gitAuthSecrets, &buildflags.Secret{
ID: llb.GitAuthHeaderKey,
Env: "BUILDX_BAKE_GIT_AUTH_HEADER",
})
}
if len(gitAuthSecrets) > 0 {
if gitAuthSecrets := gitAuthSecretsFromEnv(url); len(gitAuthSecrets) > 0 {
if secrets, err := build.CreateSecrets(gitAuthSecrets); err == nil {
sessions = append(sessions, secrets)
}
Expand Down