This repository has no description
1package microvm
2
3import (
4 "bytes"
5 "context"
6 _ "embed"
7 "fmt"
8 "log/slog"
9 "os"
10 "os/exec"
11 "text/template"
12
13 "tangled.org/core/spindle/netguard"
14)
15
16var (
17 blockedNamespaceRoutes = netguard.BlockedRoutes
18 blockedNamespaceNets = netguard.BlockedNets
19)
20
21//go:embed netns_wrapper.sh.tmpl
22var netnsWrapperTemplate string
23
24type netnsWrapperData struct {
25 TapName string
26 BlockedRoutes []string
27}
28
29func writeNetnsWrapper(path string, dev bool) error {
30 tmpl, err := template.New("netns-wrapper").Parse(netnsWrapperTemplate)
31 if err != nil {
32 return fmt.Errorf("parse qemu network namespace wrapper template: %w", err)
33 }
34
35 var script bytes.Buffer
36
37 var routes []string
38 if !dev {
39 routes = blockedNamespaceRoutes
40 }
41
42 err = tmpl.Execute(&script, netnsWrapperData{
43 TapName: netnsTapName,
44 BlockedRoutes: routes,
45 })
46 if err != nil {
47 return fmt.Errorf("render qemu network namespace wrapper template: %w", err)
48 }
49
50 if err := os.WriteFile(path, script.Bytes(), 0o700); err != nil {
51 return fmt.Errorf("write qemu network namespace wrapper: %w", err)
52 }
53
54 return nil
55}
56
57type slirpNamespace struct {
58 spec ImageSpec
59 pidFile string
60 dev bool
61}
62
63func (n *slirpNamespace) Start(ctx context.Context, logFile *os.File, logger *slog.Logger) (*exec.Cmd, *os.File, error) {
64 pid, err := waitForPIDFile(ctx, n.pidFile)
65 if err != nil {
66 return nil, nil, err
67 }
68
69 exitR, exitW, err := os.Pipe()
70 if err != nil {
71 return nil, nil, fmt.Errorf("create slirp4netns exit pipe: %w", err)
72 }
73 defer exitR.Close() // always close our read end; child gets it via ExtraFiles dup
74
75 var ok bool
76 defer func() {
77 if !ok {
78 _ = exitW.Close()
79 }
80 }()
81
82 slirpPath, err := exec.LookPath("slirp4netns")
83 if err != nil {
84 return nil, nil, fmt.Errorf("slirp4netns command not found in PATH: %w", err)
85 }
86
87 args := slirpArgs(n.dev, pid)
88
89 cmd := exec.CommandContext(ctx, slirpPath, args...)
90 cmd.ExtraFiles = []*os.File{exitR}
91 cmd.Stdout = logFile
92 cmd.Stderr = logFile
93 if err := cmd.Start(); err != nil {
94 return nil, nil, fmt.Errorf("start slirp4netns: %w", err)
95 }
96 logger.Info("started slirp4netns network namespace", "pid", pid, "cidr", outerSlirpCIDR, "tap", netnsTapName)
97
98 ok = true
99 return cmd, exitW, nil
100}
101
102func slirpArgs(dev bool, pid string) []string {
103 args := []string{
104 "--configure",
105 "--mtu=" + netnsMTU,
106 }
107 if !dev {
108 args = append(args, "--disable-host-loopback")
109 }
110 args = append(args,
111 "--disable-dns",
112 "--enable-seccomp",
113 "--exit-fd=3",
114 "--cidr="+outerSlirpCIDR,
115 pid,
116 netnsTapName,
117 )
118 return args
119}