Skip to content

Commit ac54d55

Browse files
committed
bake: derive git auth host from remote URL
Signed-off-by: CrazyMax <[email protected]>
1 parent 53882ae commit ac54d55

File tree

5 files changed

+162
-30
lines changed

5 files changed

+162
-30
lines changed

bake/bake.go

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,14 +1330,14 @@ func updateContext(t *build.Inputs, inp *Input) {
13301330
t.ContextPath = inp.URL
13311331
}
13321332

1333-
func isRemoteContext(t build.Inputs, inp *Input) bool {
1333+
func remoteContextURL(t build.Inputs, inp *Input) string {
13341334
if urlutil.IsRemoteURL(t.ContextPath) {
1335-
return true
1335+
return t.ContextPath
13361336
}
13371337
if inp != nil && urlutil.IsRemoteURL(inp.URL) && !strings.HasPrefix(t.ContextPath, "cwd://") {
1338-
return true
1338+
return inp.URL
13391339
}
1340-
return false
1340+
return ""
13411341
}
13421342

13431343
func collectLocalPaths(t build.Inputs) []string {
@@ -1509,19 +1509,8 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
15091509
bo.Platforms = platforms
15101510

15111511
secrets := t.Secrets
1512-
if isRemoteContext(bi, inp) {
1513-
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_TOKEN"); ok {
1514-
secrets = append(secrets, &buildflags.Secret{
1515-
ID: llb.GitAuthTokenKey,
1516-
Env: "BUILDX_BAKE_GIT_AUTH_TOKEN",
1517-
})
1518-
}
1519-
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_HEADER"); ok {
1520-
secrets = append(secrets, &buildflags.Secret{
1521-
ID: llb.GitAuthHeaderKey,
1522-
Env: "BUILDX_BAKE_GIT_AUTH_HEADER",
1523-
})
1524-
}
1512+
if remoteURL := remoteContextURL(bi, inp); remoteURL != "" {
1513+
secrets = append(secrets, gitAuthSecretsFromEnv(remoteURL)...)
15251514
}
15261515
bo.SecretSpecs = secrets.Normalize()
15271516
secretAttachment, err := build.CreateSecrets(bo.SecretSpecs)

bake/bake_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010
"testing"
1111

12+
"github.com/docker/buildx/build"
1213
"github.com/docker/buildx/util/buildflags"
1314
"github.com/moby/buildkit/util/entitlements"
1415
"github.com/stretchr/testify/assert"
@@ -2415,6 +2416,31 @@ target "mtx" {
24152416
}
24162417
}
24172418

2419+
func TestRemoteContextURL(t *testing.T) {
2420+
t.Run("context path has priority when remote", func(t *testing.T) {
2421+
url := remoteContextURL(build.Inputs{
2422+
ContextPath: "https://context.example.com/org/repo.git",
2423+
}, &Input{URL: "https://definition.example.com/org/repo.git"})
2424+
require.Equal(t, "https://context.example.com/org/repo.git", url)
2425+
})
2426+
t.Run("uses input url when context path is not remote", func(t *testing.T) {
2427+
url := remoteContextURL(build.Inputs{
2428+
ContextPath: ".",
2429+
}, &Input{URL: "https://definition.example.com/org/repo.git"})
2430+
require.Equal(t, "https://definition.example.com/org/repo.git", url)
2431+
})
2432+
t.Run("returns empty when context path is cwd prefixed", func(t *testing.T) {
2433+
url := remoteContextURL(build.Inputs{
2434+
ContextPath: "cwd://.",
2435+
}, &Input{URL: "https://definition.example.com/org/repo.git"})
2436+
require.Empty(t, url)
2437+
})
2438+
t.Run("returns empty without remote url", func(t *testing.T) {
2439+
require.Empty(t, remoteContextURL(build.Inputs{}, nil))
2440+
require.Empty(t, remoteContextURL(build.Inputs{}, &Input{URL: "local-path"}))
2441+
})
2442+
}
2443+
24182444
func stringify[V fmt.Stringer](values []V) []string {
24192445
s := make([]string, len(values))
24202446
for i, v := range values {

bake/gitauth.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package bake
2+
3+
import (
4+
"os"
5+
"sort"
6+
"strings"
7+
8+
"github.com/docker/buildx/util/buildflags"
9+
"github.com/moby/buildkit/client/llb"
10+
"github.com/moby/buildkit/util/gitutil"
11+
)
12+
13+
const (
14+
bakeGitAuthTokenEnv = "BUILDX_BAKE_GIT_AUTH_TOKEN" // #nosec G101 -- environment variable key, not a credential
15+
bakeGitAuthHeaderEnv = "BUILDX_BAKE_GIT_AUTH_HEADER"
16+
)
17+
18+
func gitAuthSecretsFromEnv(remoteURLs ...string) buildflags.Secrets {
19+
return gitAuthSecretsFromEnviron(os.Environ(), remoteURLs...)
20+
}
21+
22+
func gitAuthSecretsFromEnviron(environ []string, remoteURLs ...string) buildflags.Secrets {
23+
hosts := gitAuthHostsFromURLs(remoteURLs)
24+
secrets := make(buildflags.Secrets, 0, 2)
25+
secrets = append(secrets, gitAuthSecretsForEnv(llb.GitAuthTokenKey, bakeGitAuthTokenEnv, environ, hosts)...)
26+
secrets = append(secrets, gitAuthSecretsForEnv(llb.GitAuthHeaderKey, bakeGitAuthHeaderEnv, environ, hosts)...)
27+
return secrets
28+
}
29+
30+
func gitAuthSecretsForEnv(secretIDPrefix, envPrefix string, environ []string, hosts []string) buildflags.Secrets {
31+
envKey, ok := findGitAuthEnvKey(envPrefix, environ)
32+
if !ok {
33+
return nil
34+
}
35+
secrets := make(buildflags.Secrets, 0, len(hosts)+1)
36+
secrets = append(secrets, &buildflags.Secret{
37+
ID: secretIDPrefix,
38+
Env: envKey,
39+
})
40+
for _, host := range hosts {
41+
secrets = append(secrets, &buildflags.Secret{
42+
ID: secretIDPrefix + "." + host,
43+
Env: envKey,
44+
})
45+
}
46+
return secrets
47+
}
48+
49+
func gitAuthHostsFromURLs(remoteURLs []string) []string {
50+
if len(remoteURLs) == 0 {
51+
return nil
52+
}
53+
hosts := make(map[string]struct{}, len(remoteURLs))
54+
for _, remoteURL := range remoteURLs {
55+
gitURL, err := gitutil.ParseURL(remoteURL)
56+
if err != nil || gitURL.Host == "" {
57+
continue
58+
}
59+
hosts[gitURL.Host] = struct{}{}
60+
}
61+
out := make([]string, 0, len(hosts))
62+
for host := range hosts {
63+
out = append(out, host)
64+
}
65+
sort.Strings(out)
66+
return out
67+
}
68+
69+
func findGitAuthEnvKey(envKey string, environ []string) (string, bool) {
70+
for _, env := range environ {
71+
key, _, ok := strings.Cut(env, "=")
72+
if !ok {
73+
continue
74+
}
75+
if strings.EqualFold(key, envKey) {
76+
return key, true
77+
}
78+
}
79+
return "", false
80+
}

bake/gitauth_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package bake
2+
3+
import (
4+
"testing"
5+
6+
"github.com/docker/buildx/util/buildflags"
7+
"github.com/moby/buildkit/client/llb"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestGitAuthSecretsFromEnviron(t *testing.T) {
12+
t.Run("base keys", func(t *testing.T) {
13+
secrets := gitAuthSecretsFromEnviron([]string{
14+
bakeGitAuthTokenEnv + "=token",
15+
bakeGitAuthHeaderEnv + "=basic",
16+
})
17+
require.Equal(t, []string{
18+
llb.GitAuthTokenKey + "|" + bakeGitAuthTokenEnv,
19+
llb.GitAuthHeaderKey + "|" + bakeGitAuthHeaderEnv,
20+
}, secretPairs(secrets))
21+
})
22+
t.Run("derives host from remote url", func(t *testing.T) {
23+
secrets := gitAuthSecretsFromEnviron([]string{
24+
bakeGitAuthTokenEnv + "=token",
25+
bakeGitAuthHeaderEnv + "=basic",
26+
}, "https://example.com/org/repo.git")
27+
require.Equal(t, []string{
28+
llb.GitAuthTokenKey + "|" + bakeGitAuthTokenEnv,
29+
llb.GitAuthTokenKey + ".example.com|" + bakeGitAuthTokenEnv,
30+
llb.GitAuthHeaderKey + "|" + bakeGitAuthHeaderEnv,
31+
llb.GitAuthHeaderKey + ".example.com|" + bakeGitAuthHeaderEnv,
32+
}, secretPairs(secrets))
33+
})
34+
t.Run("ignores host suffixed keys", func(t *testing.T) {
35+
secrets := gitAuthSecretsFromEnviron([]string{
36+
bakeGitAuthTokenEnv + ".example.com=token",
37+
bakeGitAuthHeaderEnv + ".example.com=basic",
38+
})
39+
require.Empty(t, secrets)
40+
})
41+
}
42+
43+
func secretPairs(secrets buildflags.Secrets) []string {
44+
out := make([]string, 0, len(secrets))
45+
for _, s := range secrets {
46+
out = append(out, s.ID+"|"+s.Env)
47+
}
48+
return out
49+
}

bake/remote.go

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,7 @@ func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, name
4444
}}); err == nil {
4545
sessions = append(sessions, ssh)
4646
}
47-
var gitAuthSecrets []*buildflags.Secret
48-
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_TOKEN"); ok {
49-
gitAuthSecrets = append(gitAuthSecrets, &buildflags.Secret{
50-
ID: llb.GitAuthTokenKey,
51-
Env: "BUILDX_BAKE_GIT_AUTH_TOKEN",
52-
})
53-
}
54-
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_HEADER"); ok {
55-
gitAuthSecrets = append(gitAuthSecrets, &buildflags.Secret{
56-
ID: llb.GitAuthHeaderKey,
57-
Env: "BUILDX_BAKE_GIT_AUTH_HEADER",
58-
})
59-
}
47+
gitAuthSecrets := gitAuthSecretsFromEnv(url)
6048
if len(gitAuthSecrets) > 0 {
6149
if secrets, err := build.CreateSecrets(gitAuthSecrets); err == nil {
6250
sessions = append(sessions, secrets)

0 commit comments

Comments
 (0)