Skip to content
This repository was archived by the owner on Mar 9, 2022. It is now read-only.

Commit a115cad

Browse files
committed
Using netns to perform socat
This commit removes the usage of nsenter and uses netns to perform socat operation. Signed-off-by: abhi <[email protected]>
1 parent 246ffa3 commit a115cad

File tree

2 files changed

+47
-46
lines changed

2 files changed

+47
-46
lines changed

pkg/server/sandbox_portforward.go

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ import (
2323
"os/exec"
2424
"strings"
2525

26+
"github.com/containernetworking/plugins/pkg/ns"
2627
"github.com/pkg/errors"
2728
"github.com/sirupsen/logrus"
2829
"golang.org/x/net/context"
2930
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
3031

31-
ctrdutil "github.com/containerd/cri/pkg/containerd/util"
3232
sandboxstore "github.com/containerd/cri/pkg/store/sandbox"
3333
)
3434

@@ -46,19 +46,17 @@ func (c *criService) PortForward(ctx context.Context, r *runtime.PortForwardRequ
4646
return c.streamServer.GetPortForward(r)
4747
}
4848

49-
// portForward requires `nsenter` and `socat` on the node, it uses `nsenter` to enter the
49+
// portForward requires `socat` on the node, it uses netns to enter the
5050
// sandbox namespace, and run `socat` inside the namespace to forward stream for a specific
5151
// port. The `socat` command keeps running until it exits or client disconnect.
5252
func (c *criService) portForward(id string, port int32, stream io.ReadWriteCloser) error {
5353
s, err := c.sandboxStore.Get(id)
5454
if err != nil {
5555
return errors.Wrapf(err, "failed to find sandbox %q in store", id)
5656
}
57-
t, err := s.Container.Task(ctrdutil.NamespacedContext(), nil)
58-
if err != nil {
59-
return errors.Wrap(err, "failed to get sandbox container task")
57+
if s.NetNS == nil {
58+
return errors.Errorf("failed to find network namespace fo sandbox %q in store", id)
6059
}
61-
pid := t.Pid()
6260

6361
socat, err := exec.LookPath("socat")
6462
if err != nil {
@@ -67,48 +65,44 @@ func (c *criService) portForward(id string, port int32, stream io.ReadWriteClose
6765

6866
// Check following links for meaning of the options:
6967
// * socat: https://linux.die.net/man/1/socat
70-
// * nsenter: http://man7.org/linux/man-pages/man1/nsenter.1.html
71-
args := []string{"-t", fmt.Sprintf("%d", pid), "-n", socat,
72-
"-", fmt.Sprintf("TCP4:localhost:%d", port)}
73-
74-
nsenter, err := exec.LookPath("nsenter")
75-
if err != nil {
76-
return errors.Wrap(err, "failed to find nsenter")
77-
}
78-
79-
logrus.Infof("Executing port forwarding command: %s %s", nsenter, strings.Join(args, " "))
80-
81-
cmd := exec.Command(nsenter, args...)
82-
cmd.Stdout = stream
83-
84-
stderr := new(bytes.Buffer)
85-
cmd.Stderr = stderr
86-
87-
// If we use Stdin, command.Run() won't return until the goroutine that's copying
88-
// from stream finishes. Unfortunately, if you have a client like telnet connected
89-
// via port forwarding, as long as the user's telnet client is connected to the user's
90-
// local listener that port forwarding sets up, the telnet session never exits. This
91-
// means that even if socat has finished running, command.Run() won't ever return
92-
// (because the client still has the connection and stream open).
93-
//
94-
// The work around is to use StdinPipe(), as Wait() (called by Run()) closes the pipe
95-
// when the command (socat) exits.
96-
in, err := cmd.StdinPipe()
97-
if err != nil {
98-
return errors.Wrap(err, "failed to create stdin pipe")
99-
}
100-
go func() {
101-
if _, err := io.Copy(in, stream); err != nil {
102-
logrus.WithError(err).Errorf("Failed to copy port forward input for %q port %d", id, port)
68+
args := []string{"-", fmt.Sprintf("TCP4:localhost:%d", port)}
69+
logrus.Infof("Executing port forwarding command: %s %s", socat, strings.Join(args, " "))
70+
err = s.NetNS.GetNs().Do(func(_ ns.NetNS) error {
71+
cmd := exec.Command(socat, args...)
72+
cmd.Stdout = stream
73+
74+
stderr := new(bytes.Buffer)
75+
cmd.Stderr = stderr
76+
77+
// If we use Stdin, command.Run() won't return until the goroutine that's copying
78+
// from stream finishes. Unfortunately, if you have a client like telnet connected
79+
// via port forwarding, as long as the user's telnet client is connected to the user's
80+
// local listener that port forwarding sets up, the telnet session never exits. This
81+
// means that even if socat has finished running, command.Run() won't ever return
82+
// (because the client still has the connection and stream open).
83+
//
84+
// The work around is to use StdinPipe(), as Wait() (called by Run()) closes the pipe
85+
// when the command (socat) exits.
86+
in, err := cmd.StdinPipe()
87+
if err != nil {
88+
return errors.Wrap(err, "failed to create stdin pipe")
10389
}
104-
in.Close()
105-
logrus.Debugf("Finish copy port forward input for %q port %d: %v", id, port)
106-
}()
107-
108-
if err := cmd.Run(); err != nil {
109-
return errors.Errorf("nsenter command returns error: %v, stderr: %q", err, stderr.String())
90+
go func() {
91+
if _, err := io.Copy(in, stream); err != nil {
92+
logrus.WithError(err).Errorf("Failed to copy port forward input for %q port %d", id, port)
93+
}
94+
in.Close()
95+
logrus.Debugf("Finish copy port forward input for %q port %d: %v", id, port)
96+
}()
97+
98+
if err := cmd.Run(); err != nil {
99+
return errors.Wrapf(err, "socat command returns error, stderr: %q", stderr.String())
100+
}
101+
return nil
102+
})
103+
if err != nil {
104+
return errors.Wrapf(err, "failed to execute portforward in network namespace %s", s.NetNS.GetPath())
110105
}
111-
112106
logrus.Infof("Finish port forwarding for %q port %d", id, port)
113107

114108
return nil

pkg/store/sandbox/netns.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,10 @@ func (n *NetNS) GetPath() string {
124124
defer n.Unlock()
125125
return n.ns.Path()
126126
}
127+
128+
// GetNs returns the network namespace handle
129+
func (n *NetNS) GetNs() cnins.NetNS {
130+
n.Lock()
131+
defer n.Unlock()
132+
return n.ns
133+
}

0 commit comments

Comments
 (0)