docker socket forwarding support
- This PR adds a new mount type: `docker.sock` (in addition to `cache` and `tmp`) - It's then able to mount the LOCAL (as in, from the machine running dagger) docker socket inside the container by pretending to be an SSH Agent (hijacking the SSH agent forwarding support of buildkit) Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
parent
04cd927006
commit
ac34df319a
@ -114,7 +114,11 @@ func (c *Client) buildfn(ctx context.Context, st *state.State, env *environment.
|
||||
// Setup solve options
|
||||
opts := bk.SolveOpt{
|
||||
LocalDirs: localdirs,
|
||||
Session: []session.Attachable{auth, secrets},
|
||||
Session: []session.Attachable{
|
||||
auth,
|
||||
secrets,
|
||||
solver.NewDockerSocketProvider(),
|
||||
},
|
||||
}
|
||||
|
||||
// Call buildkit solver
|
||||
|
@ -468,6 +468,11 @@ func (p *Pipeline) mount(ctx context.Context, dest string, mnt *compiler.Value)
|
||||
llb.Scratch(),
|
||||
llb.Tmpfs(),
|
||||
), nil
|
||||
case "docker.sock":
|
||||
return llb.AddSSHSocket(
|
||||
llb.SSHID(solver.DockerSocketID),
|
||||
llb.SSHSocketTarget(dest),
|
||||
), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid mount source: %q", s)
|
||||
}
|
||||
|
62
solver/dockersocketprovider.go
Normal file
62
solver/dockersocketprovider.go
Normal file
@ -0,0 +1,62 @@
|
||||
package solver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/session/sshforward"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
DockerSocketID = "docker.sock"
|
||||
DockerSocketPath = "/var/run/docker.sock"
|
||||
)
|
||||
|
||||
type DockerSocketProvider struct {
|
||||
}
|
||||
|
||||
func NewDockerSocketProvider() session.Attachable {
|
||||
return &DockerSocketProvider{}
|
||||
}
|
||||
|
||||
func (sp *DockerSocketProvider) Register(server *grpc.Server) {
|
||||
sshforward.RegisterSSHServer(server, sp)
|
||||
}
|
||||
|
||||
func (sp *DockerSocketProvider) CheckAgent(ctx context.Context, req *sshforward.CheckAgentRequest) (*sshforward.CheckAgentResponse, error) {
|
||||
id := sshforward.DefaultID
|
||||
if req.ID != "" {
|
||||
id = req.ID
|
||||
}
|
||||
if id != DockerSocketID {
|
||||
return &sshforward.CheckAgentResponse{}, fmt.Errorf("invalid socket forward key %s", id)
|
||||
}
|
||||
return &sshforward.CheckAgentResponse{}, nil
|
||||
}
|
||||
|
||||
func (sp *DockerSocketProvider) ForwardAgent(stream sshforward.SSH_ForwardAgentServer) error {
|
||||
id := sshforward.DefaultID
|
||||
|
||||
opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object
|
||||
|
||||
if v, ok := opts[sshforward.KeySSHID]; ok && len(v) > 0 && v[0] != "" {
|
||||
id = v[0]
|
||||
}
|
||||
|
||||
if id != DockerSocketID {
|
||||
return fmt.Errorf("invalid socket forward key %s", id)
|
||||
}
|
||||
|
||||
conn, err := net.DialTimeout("unix", DockerSocketPath, time.Second)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to %s: %w", DockerSocketPath, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
return sshforward.Copy(context.TODO(), conn, stream, nil)
|
||||
}
|
@ -157,7 +157,11 @@ func (s Solver) Export(ctx context.Context, st llb.State, img *dockerfile2llb.Im
|
||||
|
||||
opts := bk.SolveOpt{
|
||||
Exports: []bk.ExportEntry{output},
|
||||
Session: []session.Attachable{s.opts.Auth, s.opts.Secrets},
|
||||
Session: []session.Attachable{
|
||||
s.opts.Auth,
|
||||
s.opts.Secrets,
|
||||
NewDockerSocketProvider(),
|
||||
},
|
||||
}
|
||||
|
||||
ch := make(chan *bk.SolveStatus)
|
||||
|
@ -52,7 +52,7 @@ package op
|
||||
// `true` means also ignoring the mount cache volumes
|
||||
always?: true | *false
|
||||
dir: string | *"/"
|
||||
mount: [string]: "tmpfs" | "cache" | {from: _, path: string | *"/"} | {secret: _}
|
||||
mount: [string]: "tmpfs" | "cache" | "docker.sock" | {from: _, path: string | *"/"} | {secret: _}
|
||||
// Map of hostnames to ip
|
||||
hosts?: [string]: string
|
||||
// User to exec with (if left empty, will default to the set user in the image)
|
||||
|
@ -106,6 +106,10 @@ setup() {
|
||||
assert_output "secret=mySecret"
|
||||
}
|
||||
|
||||
@test "compute: docker socket" {
|
||||
run "$DAGGER" compute "$TESTDIR"/compute/dockersocket
|
||||
}
|
||||
|
||||
@test "compute: exclude" {
|
||||
"$DAGGER" up -w "$TESTDIR"/compute/exclude
|
||||
}
|
||||
|
18
tests/compute/dockersocket/main.cue
Normal file
18
tests/compute/dockersocket/main.cue
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"dagger.io/dagger/op"
|
||||
"dagger.io/docker"
|
||||
)
|
||||
|
||||
TestDockerSocket: #up: [
|
||||
op.#Load & {
|
||||
from: docker.#Client
|
||||
},
|
||||
|
||||
op.#Exec & {
|
||||
always: true
|
||||
mount: "/var/run/docker.sock": "docker.sock"
|
||||
args: ["docker", "info"]
|
||||
},
|
||||
]
|
Reference in New Issue
Block a user