Skip to content
Open
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
8 changes: 7 additions & 1 deletion driver/remote/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,13 @@ func (d *Driver) Client(ctx context.Context, opts ...client.ClientOpt) (*client.
}),
client.WithTracerDelegate(delegated.DefaultExporter),
}, opts...)
c, err := client.New(ctx, "", opts...)
// Pass the configured endpoint address (rather than an empty string) so
// the buildkit client derives the gRPC ":authority" pseudo-header from
// the remote endpoint hostname. An empty address falls back to the
// system-default buildkit address, which makes the authority resolve to
// "localhost". The connection itself still goes through the custom
// dialer above, so the actual dial target is unaffected.
c, err := client.New(ctx, d.EndpointAddr, opts...)
d.client = c
d.err = err
})
Expand Down
70 changes: 70 additions & 0 deletions driver/remote/driver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package remote

import (
"context"
"net"
"testing"
"time"

"github.com/docker/buildx/driver"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)

// TestClientAuthority verifies that the remote driver derives the gRPC
// ":authority" pseudo-header from the configured endpoint address instead of
// defaulting to "localhost" (see docker/buildx#3880). It stands up an
// in-process gRPC server on a loopback listener and asserts the authority of
// the request it receives matches the endpoint host.
func TestClientAuthority(t *testing.T) {
ctx, cancel := context.WithTimeoutCause(context.Background(), 10*time.Second, context.DeadlineExceeded)
defer cancel()

lc := net.ListenConfig{}
lis, err := lc.Listen(ctx, "tcp", "127.0.0.1:0")
require.NoError(t, err)
defer lis.Close()

authorityCh := make(chan string, 1)
srv := grpc.NewServer(grpc.UnknownServiceHandler(func(_ any, stream grpc.ServerStream) error {
authority := ""
if md, ok := metadata.FromIncomingContext(stream.Context()); ok {
if a := md.Get(":authority"); len(a) > 0 {
authority = a[0]
}
}
select {
case authorityCh <- authority:
default:
}
return status.Error(codes.Unimplemented, "unimplemented")
}))
go func() {
_ = srv.Serve(lis)
}()
defer srv.Stop()

d := &Driver{
InitConfig: driver.InitConfig{
EndpointAddr: "tcp://" + lis.Addr().String(),
},
}

c, err := d.Client(ctx)
require.NoError(t, err)
defer c.Close()

// Any RPC will do: it fails server-side with Unimplemented, but the server
// records the ":authority" it received from the client before responding.
_, _ = c.ListWorkers(ctx)

select {
case authority := <-authorityCh:
require.Equal(t, lis.Addr().String(), authority)
case <-ctx.Done():
t.Fatal("timed out waiting for request to reach the server")
}
}
Loading