Unverified Commit 9f4e43fe authored by Darren Shepherd's avatar Darren Shepherd Committed by GitHub

Merge pull request #506 from AkihiroSuda/bump-up-rootlesskit

rootless: use built-in port driver
parents 46cf31d2 5a51a8de
......@@ -13,7 +13,7 @@ import (
"github.com/rootless-containers/rootlesskit/pkg/copyup/tmpfssymlink"
"github.com/rootless-containers/rootlesskit/pkg/network/slirp4netns"
"github.com/rootless-containers/rootlesskit/pkg/parent"
"github.com/rootless-containers/rootlesskit/pkg/port/socat"
portbuiltin "github.com/rootless-containers/rootlesskit/pkg/port/builtin"
"github.com/sirupsen/logrus"
)
......@@ -103,7 +103,7 @@ func createParentOpt(stateDir string) (*parent.Opt, error) {
return nil, err
}
opt.NetworkDriver = slirp4netns.NewParentDriver(binary, mtu, ipnet, disableHostLoopback, "")
opt.PortDriver, err = socat.NewParentDriver(&logrusDebugWriter{})
opt.PortDriver, err = portbuiltin.NewParentDriver(&logrusDebugWriter{}, stateDir)
if err != nil {
return nil, err
}
......@@ -127,6 +127,7 @@ func createChildOpt() (*child.Opt, error) {
opt.TargetCmd = os.Args
opt.PipeFDEnvKey = pipeFD
opt.NetworkDriver = slirp4netns.NewChildDriver()
opt.PortDriver = portbuiltin.NewChildDriver(&logrusDebugWriter{})
opt.CopyUpDirs = []string{"/etc", "/run"}
opt.CopyUpDriver = tmpfssymlink.NewChildDriver()
return opt, nil
......
......@@ -237,7 +237,7 @@ import:
- package: github.com/robfig/cron
version: v1-53-gdf38d32658d878
- package: github.com/rootless-containers/rootlesskit
version: 893c1c3de71f54c301fdb85a7c0dd15c1933c159
version: v0.4.1
- package: github.com/russross/blackfriday
version: v1.4-2-g300106c228d52c
- package: github.com/seccomp/libseccomp-golang
......
......@@ -126,7 +126,7 @@ gopkg.in/yaml.v2 v2.2.1
github.com/ibuildthecloud/kvsql 1afc2d8ad7d7e263c1971b05cb37e83aa5562561 https://github.com/erikwilson/rancher-kvsql.git
# rootless
github.com/rootless-containers/rootlesskit 893c1c3de71f54c301fdb85a7c0dd15c1933c159
github.com/rootless-containers/rootlesskit v0.4.1
github.com/theckman/go-flock v0.7.1
github.com/morikuni/aec 39771216ff4c63d11f5e604076f9c45e8be1067b
......
......@@ -55,6 +55,19 @@
[[projects]]
branch = "master"
digest = "1:a1d58d11ad642c9760251e0846ee37ad1237e83f8245c8d310e3eba0d76bb7f4"
name = "github.com/insomniacslk/dhcp"
packages = [
"dhcpv4",
"dhcpv4/client4",
"iana",
"rfc1035label",
]
pruneopts = "UT"
revision = "625d653f51917b167cc2e53ef8fe595e85dd5fa4"
[[projects]]
branch = "master"
digest = "1:dd1e851f4e3a5ee3f51613c79a01666e04e5e9289e2da3f6f815c008010fc02f"
name = "github.com/jamescun/tuntap"
packages = ["."]
......@@ -129,6 +142,18 @@
version = "v0.7.1"
[[projects]]
branch = "master"
digest = "1:35bdf197f9a11e01b3e2d9a35af57cd2b0ed0023d7c4d63a3e1bd7762275a17a"
name = "github.com/u-root/u-root"
packages = [
"pkg/rand",
"pkg/ubinary",
"pkg/uio",
]
pruneopts = "UT"
revision = "34b144e97033ea76860cc0fa6a69256cd5fe2133"
[[projects]]
digest = "1:b24d38b282bacf9791408a080f606370efa3d364e4b5fd9ba0f7b87786d3b679"
name = "github.com/urfave/cli"
packages = ["."]
......@@ -138,9 +163,15 @@
[[projects]]
branch = "master"
digest = "1:7ccb2dbb79f60b4e530c7dc3a0b3681b3869cf7ea91c645735e4dd4e6e3264fd"
digest = "1:19df0dab53c3fdea922472b4199990146f3db473881fdc59317d8b4800df145a"
name = "golang.org/x/net"
packages = ["context/ctxhttp"]
packages = [
"bpf",
"context/ctxhttp",
"internal/iana",
"internal/socket",
"ipv4",
]
pruneopts = "UT"
revision = "74de082e2cca95839e88aa0aeee5aadf6ce7710f"
......@@ -162,6 +193,8 @@
"github.com/docker/docker/pkg/idtools",
"github.com/google/uuid",
"github.com/gorilla/mux",
"github.com/insomniacslk/dhcp/dhcpv4",
"github.com/insomniacslk/dhcp/dhcpv4/client4",
"github.com/jamescun/tuntap",
"github.com/moby/vpnkit/go/pkg/vmnet",
"github.com/pkg/errors",
......
......@@ -39,3 +39,7 @@
[prune]
go-tests = true
unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/insomniacslk/dhcp"
......@@ -4,11 +4,13 @@ import (
"io/ioutil"
"os"
"os/exec"
"runtime"
"strconv"
"syscall"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"github.com/rootless-containers/rootlesskit/pkg/common"
"github.com/rootless-containers/rootlesskit/pkg/copyup"
......@@ -76,12 +78,12 @@ func activateLoopback() error {
return nil
}
func activateTap(tap, ip string, netmask int, gateway string, mtu int) error {
func activateDev(dev, ip string, netmask int, gateway string, mtu int) error {
cmds := [][]string{
{"ip", "link", "set", tap, "up"},
{"ip", "link", "set", "dev", tap, "mtu", strconv.Itoa(mtu)},
{"ip", "addr", "add", ip + "/" + strconv.Itoa(netmask), "dev", tap},
{"ip", "route", "add", "default", "via", gateway, "dev", tap},
{"ip", "link", "set", dev, "up"},
{"ip", "link", "set", "dev", dev, "mtu", strconv.Itoa(mtu)},
{"ip", "addr", "add", ip + "/" + strconv.Itoa(netmask), "dev", dev},
{"ip", "route", "add", "default", "via", gateway, "dev", dev},
}
if err := common.Execs(os.Stderr, os.Environ(), cmds); err != nil {
return errors.Wrapf(err, "executing %v", cmds)
......@@ -119,11 +121,11 @@ func setupNet(msg common.Message, etcWasCopied bool, driver network.ChildDriver)
if err := activateLoopback(); err != nil {
return err
}
tap, err := driver.ConfigureTap(msg.Network)
dev, err := driver.ConfigureNetworkChild(&msg.Network)
if err != nil {
return err
}
if err := activateTap(tap, msg.Network.IP, msg.Network.Netmask, msg.Network.Gateway, msg.Network.MTU); err != nil {
if err := activateDev(dev, msg.Network.IP, msg.Network.Netmask, msg.Network.Gateway, msg.Network.MTU); err != nil {
return err
}
if etcWasCopied {
......@@ -187,6 +189,14 @@ func Child(opt Opt) error {
if msg.Stage != 1 {
return errors.Errorf("expected stage 1, got stage %d", msg.Stage)
}
// The parent calls child with Pdeathsig, but it is cleared when newuidmap SUID binary is called
// https://github.com/rootless-containers/rootlesskit/issues/65#issuecomment-492343646
runtime.LockOSThread()
err = unix.Prctl(unix.PR_SET_PDEATHSIG, uintptr(unix.SIGKILL), 0, 0, 0)
runtime.UnlockOSThread()
if err != nil {
return err
}
os.Unsetenv(opt.PipeFDEnvKey)
if err := pipeR.Close(); err != nil {
return errors.Wrapf(err, "failed to close fd %d", pipeFD)
......
......@@ -22,6 +22,7 @@ type Message1 struct {
// NetworkMessage is empty for HostNetwork.
type NetworkMessage struct {
Dev string
IP string
Netmask int
Gateway string
......
......@@ -14,5 +14,7 @@ type ParentDriver interface {
// ChildDriver is called from the child namespace
type ChildDriver interface {
ConfigureTap(netmsg common.NetworkMessage) (tap string, err error)
// netmsg MAY be modified.
// devName is like "tap" or "eth0"
ConfigureNetworkChild(netmsg *common.NetworkMessage) (devName string, err error)
}
......@@ -41,8 +41,6 @@ func NewParentDriver(binary string, mtu int, ipnet *net.IPNet, disableHostLoopba
}
}
const opaqueTap = "slirp4netns.tap"
type parentDriver struct {
binary string
mtu int
......@@ -87,10 +85,8 @@ func (d *parentDriver) ConfigureNetwork(childPID int, stateDir string) (*common.
return nil, common.Seq(cleanups), errors.Wrapf(err, "executing %v", cmd)
}
netmsg := common.NetworkMessage{
Dev: tap,
MTU: d.mtu,
Opaque: map[string]string{
opaqueTap: tap,
},
}
if d.ipnet != nil {
// TODO: get the actual configuration via slirp4netns API?
......@@ -126,8 +122,8 @@ func NewChildDriver() network.ChildDriver {
type childDriver struct {
}
func (d *childDriver) ConfigureTap(netmsg common.NetworkMessage) (string, error) {
tap := netmsg.Opaque[opaqueTap]
func (d *childDriver) ConfigureNetworkChild(netmsg *common.NetworkMessage) (string, error) {
tap := netmsg.Dev
if tap == "" {
return "", errors.New("could not determine the preconfigured tap")
}
......
package socat
import (
"context"
"fmt"
"io"
"net"
"os"
"os/exec"
"sync"
"syscall"
"time"
"github.com/pkg/errors"
"github.com/rootless-containers/rootlesskit/pkg/port"
"github.com/rootless-containers/rootlesskit/pkg/port/portutil"
)
func NewParentDriver(logWriter io.Writer) (port.ParentDriver, error) {
if _, err := exec.LookPath("socat"); err != nil {
return nil, err
}
if _, err := exec.LookPath("nsenter"); err != nil {
return nil, err
}
d := driver{
logWriter: logWriter,
ports: make(map[int]*port.Status, 0),
stoppers: make(map[int]func() error, 0),
nextID: 1,
}
return &d, nil
}
type driver struct {
logWriter io.Writer
mu sync.Mutex
childPID int
ports map[int]*port.Status
stoppers map[int]func() error
nextID int
}
func (d *driver) OpaqueForChild() map[string]string {
// NOP, as this driver does not have child-side logic.
return nil
}
func (d *driver) RunParentDriver(initComplete chan struct{}, quit <-chan struct{}, cctx *port.ChildContext) error {
if cctx == nil || cctx.PID <= 0 {
return errors.New("child PID not set")
}
d.childPID = cctx.PID
initComplete <- struct{}{}
<-quit
return nil
}
func (d *driver) AddPort(ctx context.Context, spec port.Spec) (*port.Status, error) {
if d.childPID <= 0 {
return nil, errors.New("child PID not set")
}
d.mu.Lock()
err := portutil.ValidatePortSpec(spec, d.ports)
d.mu.Unlock()
if err != nil {
return nil, err
}
cf := func() (*exec.Cmd, error) {
return createSocatCmd(ctx, spec, d.logWriter, d.childPID)
}
routineErrorCh := make(chan error)
routineStopCh := make(chan struct{})
routineStop := func() error {
close(routineStopCh)
return <-routineErrorCh
}
go portRoutine(cf, routineStopCh, routineErrorCh, d.logWriter)
d.mu.Lock()
id := d.nextID
st := port.Status{
ID: id,
Spec: spec,
}
d.ports[id] = &st
d.stoppers[id] = routineStop
d.nextID++
d.mu.Unlock()
return &st, nil
}
func (d *driver) ListPorts(ctx context.Context) ([]port.Status, error) {
var ports []port.Status
d.mu.Lock()
for _, p := range d.ports {
ports = append(ports, *p)
}
d.mu.Unlock()
return ports, nil
}
func (d *driver) RemovePort(ctx context.Context, id int) error {
d.mu.Lock()
defer d.mu.Unlock()
stop, ok := d.stoppers[id]
if !ok {
return errors.Errorf("unknown port id: %d", id)
}
err := stop()
delete(d.stoppers, id)
delete(d.ports, id)
return err
}
func createSocatCmd(ctx context.Context, spec port.Spec, logWriter io.Writer, childPID int) (*exec.Cmd, error) {
if spec.Proto != "tcp" && spec.Proto != "udp" {
return nil, errors.Errorf("unsupported proto: %s", spec.Proto)
}
ipStr := "0.0.0.0"
if spec.ParentIP != "" {
ip := net.ParseIP(spec.ParentIP)
if ip == nil {
return nil, errors.Errorf("unsupported parentIP: %s", spec.ParentIP)
}
ip = ip.To4()
if ip == nil {
return nil, errors.Errorf("unsupported parentIP (v6?): %s", spec.ParentIP)
}
ipStr = ip.String()
}
if spec.ParentPort < 1 || spec.ParentPort > 65535 {
return nil, errors.Errorf("unsupported parentPort: %d", spec.ParentPort)
}
if spec.ChildPort < 1 || spec.ChildPort > 65535 {
return nil, errors.Errorf("unsupported childPort: %d", spec.ChildPort)
}
var cmd *exec.Cmd
switch spec.Proto {
case "tcp":
cmd = exec.CommandContext(ctx,
"socat",
fmt.Sprintf("TCP-LISTEN:%d,bind=%s,reuseaddr,fork,rcvbuf=65536,sndbuf=65536", spec.ParentPort, ipStr),
fmt.Sprintf("EXEC:\"%s\",nofork",
fmt.Sprintf("nsenter -U -n --preserve-credentials -t %d socat STDIN TCP4:127.0.0.1:%d", childPID, spec.ChildPort)))
case "udp":
cmd = exec.CommandContext(ctx,
"socat",
fmt.Sprintf("UDP-LISTEN:%d,bind=%s,reuseaddr,fork,rcvbuf=65536,sndbuf=65536", spec.ParentPort, ipStr),
fmt.Sprintf("EXEC:\"%s\",nofork",
fmt.Sprintf("nsenter -U -n --preserve-credentials -t %d socat STDIN UDP4:127.0.0.1:%d", childPID, spec.ChildPort)))
}
cmd.Env = os.Environ()
cmd.Stdout = logWriter
cmd.Stderr = logWriter
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGKILL,
}
return cmd, nil
}
type cmdFactory func() (*exec.Cmd, error)
func portRoutine(cf cmdFactory, stopCh <-chan struct{}, errWCh chan error, logWriter io.Writer) {
retry := 0
doneCh := make(chan error)
for {
cmd, err := cf()
if err != nil {
errWCh <- err
return
}
cmdDesc := fmt.Sprintf("%s %v", cmd.Path, cmd.Args)
fmt.Fprintf(logWriter, "[exec] starting cmd %s\n", cmdDesc)
if err := cmd.Start(); err != nil {
errWCh <- err
return
}
pid := cmd.Process.Pid
go func() {
err := cmd.Wait()
doneCh <- err
}()
select {
case err := <-doneCh:
// even if err == nil (unexpected for socat), continue the loop
retry++
sleepDuration := time.Duration((retry*100)%(30*1000)) * time.Millisecond
fmt.Fprintf(logWriter, "[exec] retrying cmd %s after sleeping %v, count=%d, err=%v\n",
cmdDesc, sleepDuration, retry, err)
select {
case <-time.After(sleepDuration):
case <-stopCh:
errWCh <- err
return
}
case <-stopCh:
fmt.Fprintf(logWriter, "[exec] killing cmd %s pid %d\n", cmdDesc, pid)
syscall.Kill(pid, syscall.SIGKILL)
fmt.Fprintf(logWriter, "[exec] killed cmd %s pid %d\n", cmdDesc, pid)
close(errWCh)
return
}
}
}
func NewChildDriver() port.ChildDriver {
return &childDriver{}
}
type childDriver struct {
}
func (d *childDriver) RunChildDriver(opaque map[string]string, quit <-chan struct{}) error {
// NOP
<-quit
return nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment