@@ -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.
5252func (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
0 commit comments