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

bake: git auth support for remote definitions #2363

Merged
merged 1 commit into from
Apr 4, 2024
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
33 changes: 28 additions & 5 deletions bake/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"archive/tar"
"bytes"
"context"
"os"
"strings"

"github.com/docker/buildx/builder"
controllerapi "github.com/docker/buildx/controller/pb"
Expand All @@ -23,13 +25,34 @@ type Input struct {
}

func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, names []string, pw progress.Writer) ([]File, *Input, error) {
var session []session.Attachable
var sessions []session.Attachable
var filename string

st, ok := dockerui.DetectGitContext(url, false)
if ok {
ssh, err := controllerapi.CreateSSH([]*controllerapi.SSH{{ID: "default"}})
if err == nil {
session = append(session, ssh)
if ssh, err := controllerapi.CreateSSH([]*controllerapi.SSH{{
ID: "default",
Paths: strings.Split(os.Getenv("BUILDX_BAKE_GIT_SSH"), ","),
}}); err == nil {
sessions = append(sessions, ssh)
}
var gitAuthSecrets []*controllerapi.Secret
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_TOKEN"); ok {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably out of scope, but unclear to me how the controller takes Env keys as input. Doesn't the controller have its own env if it is running in another process?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum good point, it seems to work when connecting to local controller. I don't think there is another process except when using --invoke and setting envs through invoke config:

repeated string Env = 3;

gitAuthSecrets = append(gitAuthSecrets, &controllerapi.Secret{
ID: llb.GitAuthTokenKey,
Env: "BUILDX_BAKE_GIT_AUTH_TOKEN",
})
}
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_HEADER"); ok {
gitAuthSecrets = append(gitAuthSecrets, &controllerapi.Secret{
ID: llb.GitAuthHeaderKey,
Env: "BUILDX_BAKE_GIT_AUTH_HEADER",
})
}
if len(gitAuthSecrets) > 0 {
if secrets, err := controllerapi.CreateSecrets(gitAuthSecrets); err == nil {
sessions = append(sessions, secrets)
}
}
} else {
st, filename, ok = dockerui.DetectHTTPContext(url)
Expand Down Expand Up @@ -59,7 +82,7 @@ func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, name

ch, done := progress.NewChannel(pw)
defer func() { <-done }()
_, err = c.Build(ctx, client.SolveOpt{Session: session, Internal: true}, "buildx", func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) {
_, err = c.Build(ctx, client.SolveOpt{Session: sessions, Internal: true}, "buildx", func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) {
def, err := st.Marshal(ctx)
if err != nil {
return nil, err
Expand Down
36 changes: 36 additions & 0 deletions tests/bake.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
testBakeLocal,
testBakeLocalMulti,
testBakeRemote,
testBakeRemoteAuth,
testBakeRemoteCmdContext,
testBakeRemoteLocalOverride,
testBakeLocalCwdOverride,
Expand Down Expand Up @@ -144,6 +145,41 @@ EOT
require.FileExists(t, filepath.Join(dirDest, "foo"))
}

func testBakeRemoteAuth(t *testing.T, sb integration.Sandbox) {
bakefile := []byte(`
target "default" {
dockerfile-inline = <<EOT
FROM scratch
COPY foo /foo
EOT
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
dirDest := t.TempDir()

git, err := gitutil.New(gitutil.WithWorkingDir(dir))
require.NoError(t, err)

gitutil.GitInit(git, t)
gitutil.GitAdd(git, t, "docker-bake.hcl", "foo")
gitutil.GitCommit(git, t, "initial commit")

token := identity.NewID()
addr := gitutil.GitServeHTTP(git, t, gitutil.WithAccessToken(token))

out, err := bakeCmd(sb, withDir(dir),
withEnv("BUILDX_BAKE_GIT_AUTH_TOKEN="+token),
withArgs(addr, "--set", "*.output=type=local,dest="+dirDest),
)
require.NoError(t, err, out)

require.FileExists(t, filepath.Join(dirDest, "foo"))
}

func testBakeRemoteLocalOverride(t *testing.T, sb integration.Sandbox) {
remoteBakefile := []byte(`
target "default" {
Expand Down
39 changes: 37 additions & 2 deletions util/gitutil/testutilserve.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,28 @@ import (
"github.com/stretchr/testify/require"
)

func GitServeHTTP(c *Git, t testing.TB) (url string) {
type gitServe struct {
token string
}

type GitServeOpt func(*gitServe)

func WithAccessToken(token string) GitServeOpt {
return func(s *gitServe) {
s.token = token
}
}

func GitServeHTTP(c *Git, t testing.TB, opts ...GitServeOpt) (url string) {
t.Helper()
gitUpdateServerInfo(c, t)
ctx, cancel := context.WithCancel(context.TODO())

gs := &gitServe{}
for _, opt := range opts {
opt(gs)
}

ready := make(chan struct{})
done := make(chan struct{})

Expand All @@ -28,7 +45,25 @@ func GitServeHTTP(c *Git, t testing.TB) (url string) {
go func() {
mux := http.NewServeMux()
prefix := fmt.Sprintf("/%s/", name)
mux.Handle(prefix, http.StripPrefix(prefix, http.FileServer(http.Dir(dir))))

handler := func(next http.Handler) http.Handler {
var tokenChecked bool
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if gs.token != "" && !tokenChecked {
t.Logf("git access token to check: %q", gs.token)
user, pass, _ := r.BasicAuth()
t.Logf("basic auth: user=%q pass=%q", user, pass)
if pass != gs.token {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
tokenChecked = true
}
next.ServeHTTP(w, r)
})
}

mux.Handle(prefix, handler(http.StripPrefix(prefix, http.FileServer(http.Dir(dir)))))
l, err := net.Listen("tcp", "localhost:0")
if err != nil {
panic(err)
Expand Down