mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add [simple] network support to 'runsc do'
Sandbox always runsc with IP 192.168.10.2 and the peer network adds 1 to the address (192.168.10.3). Sandbox IP can be changed using --ip flag. Here a few examples: sudo runsc do curl www.google.com sudo runsc do --ip=10.10.10.2 bash -c "echo 123 | netcat -l -p 8080" PiperOrigin-RevId: 246421277 Change-Id: I7b3dce4af46a57300350dab41cb27e04e4b6e9da
This commit is contained in:
committed by
Shentubot
parent
2c1c1c9917
commit
bbb6539114
+154
-6
@@ -21,7 +21,10 @@ import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"flag"
|
||||
@@ -38,6 +41,7 @@ import (
|
||||
type Do struct {
|
||||
root string
|
||||
cwd string
|
||||
ip string
|
||||
}
|
||||
|
||||
// Name implements subcommands.Command.Name.
|
||||
@@ -65,7 +69,8 @@ used for testing only.
|
||||
// SetFlags implements subcommands.Command.SetFlags.
|
||||
func (c *Do) SetFlags(f *flag.FlagSet) {
|
||||
f.StringVar(&c.root, "root", "/", `path to the root directory, defaults to "/"`)
|
||||
f.StringVar(&c.cwd, "cwd", ".", `path to the current directory, defaults to the current directory`)
|
||||
f.StringVar(&c.cwd, "cwd", ".", "path to the current directory, defaults to the current directory")
|
||||
f.StringVar(&c.ip, "ip", "192.168.10.2", "IPv4 address for the sandbox")
|
||||
}
|
||||
|
||||
// Execute implements subcommands.Command.Execute.
|
||||
@@ -112,6 +117,15 @@ func (c *Do) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) su
|
||||
|
||||
specutils.LogSpec(spec)
|
||||
|
||||
cid := fmt.Sprintf("runsc-%06d", rand.Int31n(1000000))
|
||||
if conf.Network != boot.NetworkNone {
|
||||
clean, err := c.setupNet(cid, spec)
|
||||
if err != nil {
|
||||
Fatalf("Error setting up network: %v", err)
|
||||
}
|
||||
defer clean()
|
||||
}
|
||||
|
||||
out, err := json.Marshal(spec)
|
||||
if err != nil {
|
||||
Fatalf("Error to marshal spec: %v", err)
|
||||
@@ -130,11 +144,7 @@ func (c *Do) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) su
|
||||
Fatalf("Error write spec: %v", err)
|
||||
}
|
||||
|
||||
// No network support yet.
|
||||
conf.Network = boot.NetworkNone
|
||||
|
||||
id := fmt.Sprintf("runcs-do-%06d", rand.Int31n(1000000))
|
||||
ws, err := container.Run(id, spec, conf, tmpDir, "", "", "")
|
||||
ws, err := container.Run(cid, spec, conf, tmpDir, "", "", "")
|
||||
if err != nil {
|
||||
Fatalf("running container: %v", err)
|
||||
}
|
||||
@@ -155,3 +165,141 @@ func resolvePath(path string) (string, error) {
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (c *Do) setupNet(cid string, spec *specs.Spec) (func(), error) {
|
||||
dev, err := defaultDevice()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
peerIP, err := calculatePeerIP(c.ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
veth, peer := deviceNames(cid)
|
||||
|
||||
cmds := []string{
|
||||
fmt.Sprintf("ip link add %s type veth peer name %s", veth, peer),
|
||||
|
||||
// Setup device outside the namespace.
|
||||
fmt.Sprintf("ip addr add %s/24 dev %s", peerIP, peer),
|
||||
fmt.Sprintf("ip link set %s up", peer),
|
||||
|
||||
// Setup device inside the namespace.
|
||||
fmt.Sprintf("ip netns add %s", cid),
|
||||
fmt.Sprintf("ip link set %s netns %s", veth, cid),
|
||||
fmt.Sprintf("ip netns exec %s ip addr add %s/24 dev %s", cid, c.ip, veth),
|
||||
fmt.Sprintf("ip netns exec %s ip link set %s up", cid, veth),
|
||||
fmt.Sprintf("ip netns exec %s ip link set lo up", cid),
|
||||
fmt.Sprintf("ip netns exec %s ip route add default via %s", cid, peerIP),
|
||||
|
||||
// Enable network access.
|
||||
"sysctl -w net.ipv4.ip_forward=1",
|
||||
fmt.Sprintf("iptables -t nat -A POSTROUTING -s %s -o %s -j MASQUERADE", c.ip, dev),
|
||||
fmt.Sprintf("iptables -A FORWARD -i %s -o %s -j ACCEPT", dev, peer),
|
||||
fmt.Sprintf("iptables -A FORWARD -o %s -i %s -j ACCEPT", dev, peer),
|
||||
}
|
||||
|
||||
for _, cmd := range cmds {
|
||||
log.Debugf("Run %q", cmd)
|
||||
args := strings.Split(cmd, " ")
|
||||
c := exec.Command(args[0], args[1:]...)
|
||||
if err := c.Run(); err != nil {
|
||||
return nil, fmt.Errorf("failed to run %q: %v", cmd, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := makeFile("/etc/resolv.conf", "nameserver 8.8.8.8\n", spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := makeFile("/etc/hostname", cid+"\n", spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hosts := fmt.Sprintf("127.0.0.1\tlocalhost\n%s\t%s\n", c.ip, cid)
|
||||
if err := makeFile("/etc/hosts", hosts, spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if spec.Linux == nil {
|
||||
spec.Linux = &specs.Linux{}
|
||||
}
|
||||
netns := specs.LinuxNamespace{
|
||||
Type: specs.NetworkNamespace,
|
||||
Path: filepath.Join("/var/run/netns", cid),
|
||||
}
|
||||
spec.Linux.Namespaces = append(spec.Linux.Namespaces, netns)
|
||||
|
||||
return func() { c.cleanNet(cid, dev) }, nil
|
||||
}
|
||||
|
||||
func (c *Do) cleanNet(cid, dev string) {
|
||||
veth, peer := deviceNames(cid)
|
||||
|
||||
cmds := []string{
|
||||
fmt.Sprintf("ip link delete %s", peer),
|
||||
fmt.Sprintf("ip netns delete %s", cid),
|
||||
|
||||
fmt.Sprintf("iptables -t nat -D POSTROUTING -s %s/24 -o %s -j MASQUERADE", c.ip, dev),
|
||||
fmt.Sprintf("iptables -D FORWARD -i %s -o %s -j ACCEPT", dev, veth),
|
||||
fmt.Sprintf("iptables -D FORWARD -o %s -i %s -j ACCEPT", dev, veth),
|
||||
}
|
||||
|
||||
for _, cmd := range cmds {
|
||||
log.Debugf("Run %q", cmd)
|
||||
args := strings.Split(cmd, " ")
|
||||
c := exec.Command(args[0], args[1:]...)
|
||||
if err := c.Run(); err != nil {
|
||||
log.Warningf("Failed to run %q: %v", cmd, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func deviceNames(cid string) (string, string) {
|
||||
// Device name is limited to 15 letters.
|
||||
return "ve-" + cid, "vp-" + cid
|
||||
|
||||
}
|
||||
|
||||
func defaultDevice() (string, error) {
|
||||
out, err := exec.Command("ip", "route", "list", "default").CombinedOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
parts := strings.Split(string(out), " ")
|
||||
if len(parts) < 5 {
|
||||
return "", fmt.Errorf("malformed %q output: %q", "ip route list default", string(out))
|
||||
}
|
||||
return parts[4], nil
|
||||
}
|
||||
|
||||
func makeFile(dest, content string, spec *specs.Spec) error {
|
||||
tmpFile, err := ioutil.TempFile("", filepath.Base(dest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tmpFile.WriteString(content); err != nil {
|
||||
return err
|
||||
}
|
||||
spec.Mounts = append(spec.Mounts, specs.Mount{
|
||||
Source: tmpFile.Name(),
|
||||
Destination: dest,
|
||||
Type: "bind",
|
||||
Options: []string{"ro"},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func calculatePeerIP(ip string) (string, error) {
|
||||
parts := strings.Split(ip, ".")
|
||||
if len(parts) != 4 {
|
||||
return "", fmt.Errorf("invalid IP format %q", ip)
|
||||
}
|
||||
n, err := strconv.Atoi(parts[3])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid IP format %q: %v", ip, err)
|
||||
}
|
||||
n++
|
||||
if n > 255 {
|
||||
n = 1
|
||||
}
|
||||
return fmt.Sprintf("%s.%s.%s.%d", parts[0], parts[1], parts[2], n), nil
|
||||
}
|
||||
|
||||
@@ -906,7 +906,7 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *boot.Config, bund
|
||||
// Start the gofer in the given namespace.
|
||||
log.Debugf("Starting gofer: %s %v", binPath, args)
|
||||
if err := specutils.StartInNS(cmd, nss); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("Gofer: %v", err)
|
||||
}
|
||||
log.Infof("Gofer started, PID: %d", cmd.Process.Pid)
|
||||
c.GoferPid = cmd.Process.Pid
|
||||
|
||||
@@ -601,7 +601,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund
|
||||
log.Debugf("Starting sandbox: %s %v", binPath, cmd.Args)
|
||||
log.Debugf("SysProcAttr: %+v", cmd.SysProcAttr)
|
||||
if err := specutils.StartInNS(cmd, nss); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("Sandbox: %v", err)
|
||||
}
|
||||
s.child = true
|
||||
s.Pid = cmd.Process.Pid
|
||||
|
||||
Reference in New Issue
Block a user