Signed-off-by: dawn dawn@tangled.org
+41
-5
Diff
Round #0
+41
-5
spindle/engines/microvm/qemu.go
+41
-5
spindle/engines/microvm/qemu.go
···
9
9
"errors"
10
10
"fmt"
11
11
"log/slog"
12
+
"net"
12
13
"os"
13
14
"os/exec"
14
15
"path/filepath"
···
123
124
return filepath.Join(base, hex.EncodeToString(sum[:8])+".qmp.sock")
124
125
}
125
126
127
+
// the child sees ExtraFiles at fd 3 and up (stdio owns 0-2), and the
128
+
// qmp socket is our only entry
129
+
const qemuExtraFD = 3
130
+
131
+
func listenQMP(path string) (*net.UnixListener, error) {
132
+
_ = os.Remove(path)
133
+
l, err := net.ListenUnix("unix", &net.UnixAddr{Name: path, Net: "unix"})
134
+
if err != nil {
135
+
return nil, fmt.Errorf("listen qmp socket: %w", err)
136
+
}
137
+
if err := os.Chmod(path, 0o600); err != nil {
138
+
_ = l.Close()
139
+
return nil, fmt.Errorf("chmod qmp socket: %w", err)
140
+
}
141
+
return l, nil
142
+
}
143
+
126
144
func StartQEMU(ctx context.Context, cfg QEMUConfig, logger *slog.Logger) (VMHandle, error) {
127
145
if logger == nil {
128
146
logger = slog.Default()
···
186
204
}
187
205
handle.QMPPath = qmpPath
188
206
207
+
// qmp is full control of a tenant vm, so we bind it at 0600 and hand
208
+
// qemu the inherited fd. a qemu-bound socket would sit at umask perms
209
+
// in a shared dir, so if a job ever escaped its vm it could read and
210
+
// drive every other job's vm too
211
+
qmpListener, err := listenQMP(qmpPath)
212
+
if err != nil {
213
+
return nil, err
214
+
}
215
+
defer qmpListener.Close()
216
+
qmpFile, err := qmpListener.File()
217
+
if err != nil {
218
+
return nil, fmt.Errorf("get qmp socket file: %w", err)
219
+
}
220
+
defer qmpFile.Close()
221
+
189
222
qemuCmd := cfg.Image.RunnerCmd()
190
223
qemuBinary, err := exec.LookPath(qemuCmd)
191
224
if err != nil {
···
196
229
Image: cfg.Image,
197
230
CID: cid,
198
231
EnableKVM: cfg.EnableKVM,
199
-
QMPPath: qmpPath,
200
232
SerialLogPath: serialLogPath,
201
233
VolumePaths: volumePaths,
202
234
})
···
204
236
return nil, err
205
237
}
206
238
207
-
cmd, slirpNet, err := qemuCommand(ctx, qemuBinary, args, cfg.Image, workDir, cfg.Dev)
239
+
cmd, slirpNet, err := qemuCommand(ctx, qemuBinary, args, cfg.Image, workDir, cfg.Dev, qmpFile)
208
240
if err != nil {
209
241
return nil, err
210
242
}
···
512
544
spec ImageSpec,
513
545
workDir string,
514
546
dev bool,
547
+
qmpFile *os.File,
515
548
) (*exec.Cmd, *slirpNamespace, error) {
516
549
if len(spec.NetworkInterfaces) == 0 {
517
-
return exec.CommandContext(ctx, qemuBinary, args...), nil, nil
550
+
cmd := exec.CommandContext(ctx, qemuBinary, args...)
551
+
cmd.ExtraFiles = []*os.File{qmpFile}
552
+
return cmd, nil, nil
518
553
}
519
554
520
555
ipPath, err := exec.LookPath("ip")
···
551
586
}, args...)
552
587
553
588
cmd := exec.CommandContext(ctx, unsharePath, cmdArgs...)
589
+
cmd.ExtraFiles = []*os.File{qmpFile}
554
590
555
591
return cmd, &slirpNamespace{
556
592
spec: spec,
···
582
618
Image ImageSpec
583
619
CID uint32
584
620
EnableKVM bool
585
-
QMPPath string
586
621
SerialLogPath string
587
622
VolumePaths map[string]string
588
623
}
···
650
685
b.Opt("-append", cfg.Image.BootArgs)
651
686
652
687
b.Opt("-sandbox", "on")
653
-
b.Optf("-qmp", "unix:%s,server,nowait", cfg.QMPPath)
688
+
b.Optf("-chardev", "socket,id=qmp0,fd=%d,server=on,wait=off", qemuExtraFD)
689
+
b.Opt("-mon", "chardev=qmp0,mode=control")
654
690
}
655
691
656
692
func addQEMUStoreArgs(b *argBuilder, cfg qemuArgsConfig) {
History
1 round
0 comments
ptr.pet
submitted
#0
1 commit
Expand
Collapse
spindle/microvm: bind qmp sockets ourselves at 0600 and pass qemu the fd
Signed-off-by: dawn <dawn@tangled.org>