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

Commit 77a33b1

Browse files
authored
Merge pull request #729 from Random-Liu/fix-portforward
Add socat back.
2 parents b09489d + 69b3f3a commit 77a33b1

File tree

5 files changed

+43
-23
lines changed

5 files changed

+43
-23
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ install:
2121
- sudo apt-get install btrfs-tools
2222
- sudo apt-get install libseccomp2/trusty-backports
2323
- sudo apt-get install libseccomp-dev/trusty-backports
24+
- sudo apt-get install socat
2425

2526
before_script:
2627
- export PATH=$HOME/gopath/bin:$PATH

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ specifications as appropriate.
6767
(Fedora, CentOS, RHEL). On releases of Ubuntu <=Trusty and Debian <=jessie a
6868
backport version of `libseccomp-dev` is required. See [travis.yml](.travis.yml) for an example on trusty.
6969
* **btrfs development library.** Required by containerd btrfs support. `btrfs-tools`(Ubuntu, Debian) / `btrfs-progs-devel`(Fedora, CentOS, RHEL)
70+
2. Install **`socat`** (required by portforward).
7071
2. Install and setup a go 1.10 development environment.
7172
3. Make a local clone of this repository.
7273
4. Install binary dependencies by running the following command from your cloned `cri/` project directory:

contrib/ansible/tasks/bootstrap_centos.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
- tar
99
- btrfs-progs
1010
- libseccomp
11-
- util-linux
11+
- util-linux
12+
- socat
1213
- libselinux-python

contrib/ansible/tasks/bootstrap_ubuntu.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
- apt-transport-https
1010
- btrfs-tools
1111
- libseccomp2
12+
- socat
1213
- util-linux

pkg/server/sandbox_portforward.go

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ limitations under the License.
1717
package server
1818

1919
import (
20+
"bytes"
2021
"fmt"
2122
"io"
22-
"net"
23-
"sync"
23+
"os/exec"
24+
"strings"
2425

2526
"github.com/containernetworking/plugins/pkg/ns"
2627
"github.com/pkg/errors"
@@ -44,8 +45,9 @@ func (c *criService) PortForward(ctx context.Context, r *runtime.PortForwardRequ
4445
return c.streamServer.GetPortForward(r)
4546
}
4647

47-
// portForward requires it uses netns to enter the sandbox namespace,
48-
// and forward stream for a specific port.
48+
// portForward requires `socat` on the node. It uses netns to enter the sandbox namespace,
49+
// and run `socat` insidethe namespace to forward stream for a specific port. The `socat`
50+
// command keeps running until it exits or client disconnect.
4951
func (c *criService) portForward(id string, port int32, stream io.ReadWriteCloser) error {
5052
s, err := c.sandboxStore.Get(id)
5153
if err != nil {
@@ -55,32 +57,46 @@ func (c *criService) portForward(id string, port int32, stream io.ReadWriteClose
5557
return errors.Errorf("network namespace for sandbox %q is closed", id)
5658
}
5759

60+
socat, err := exec.LookPath("socat")
61+
if err != nil {
62+
return errors.Wrap(err, "failed to find socat")
63+
}
64+
65+
// Check https://linux.die.net/man/1/socat for meaning of the options.
66+
args := []string{socat, "-", fmt.Sprintf("TCP4:localhost:%d", port)}
67+
68+
logrus.Infof("Executing port forwarding command %q in network namespace %q", strings.Join(args, " "), s.NetNS.GetPath())
5869
err = s.NetNS.GetNs().Do(func(_ ns.NetNS) error {
59-
var wg sync.WaitGroup
60-
client, err := net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
70+
cmd := exec.Command(args[0], args[1:]...)
71+
cmd.Stdout = stream
72+
73+
stderr := new(bytes.Buffer)
74+
cmd.Stderr = stderr
75+
76+
// If we use Stdin, command.Run() won't return until the goroutine that's copying
77+
// from stream finishes. Unfortunately, if you have a client like telnet connected
78+
// via port forwarding, as long as the user's telnet client is connected to the user's
79+
// local listener that port forwarding sets up, the telnet session never exits. This
80+
// means that even if socat has finished running, command.Run() won't ever return
81+
// (because the client still has the connection and stream open).
82+
//
83+
// The work around is to use StdinPipe(), as Wait() (called by Run()) closes the pipe
84+
// when the command (socat) exits.
85+
in, err := cmd.StdinPipe()
6186
if err != nil {
62-
return errors.Wrapf(err, "failed to dial %q", port)
87+
return errors.Wrap(err, "failed to create stdin pipe")
6388
}
64-
wg.Add(1)
6589
go func() {
66-
defer client.Close()
67-
if _, err := io.Copy(client, stream); err != nil {
90+
if _, err := io.Copy(in, stream); err != nil {
6891
logrus.WithError(err).Errorf("Failed to copy port forward input for %q port %d", id, port)
6992
}
70-
logrus.Infof("Finish copy port forward input for %q port %d", id, port)
71-
wg.Done()
93+
in.Close()
94+
logrus.Debugf("Finish copying port forward input for %q port %d", id, port)
7295
}()
73-
wg.Add(1)
74-
go func() {
75-
defer stream.Close()
76-
if _, err := io.Copy(stream, client); err != nil {
77-
logrus.WithError(err).Errorf("Failed to copy port forward output for %q port %d", id, port)
78-
}
79-
logrus.Infof("Finish copy port forward output for %q port %d", id, port)
80-
wg.Done()
81-
}()
82-
wg.Wait()
8396

97+
if err := cmd.Run(); err != nil {
98+
return errors.Errorf("nsenter command returns error: %v, stderr: %q", err, stderr.String())
99+
}
84100
return nil
85101
})
86102
if err != nil {

0 commit comments

Comments
 (0)