oci: keep exposed ports busy and leak the fd into conmon

Bind all the specified TCP and UDP ports so that another process
cannot reuse them.  The fd of the listener is then leaked into conmon
so that the socket is kept busy until the container exits.

Closes: https://github.com/projectatomic/libpod/issues/210

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>

Closes: #1100
Approved by: mheon
This commit is contained in:
Giuseppe Scrivano 2018-07-16 13:40:59 +02:00 committed by Atomic Bot
parent 4a6f79b62b
commit 9ae7b1a5b1
2 changed files with 62 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"os/exec"
"path/filepath"
@ -15,6 +16,7 @@ import (
"time"
"github.com/coreos/go-systemd/activation"
"github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux"
"github.com/opencontainers/selinux/go-selinux/label"
@ -177,6 +179,51 @@ func waitPidsStop(pids []int, timeout time.Duration) error {
}
}
func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) {
var files []*os.File
for _, i := range ports {
switch i.Protocol {
case "udp":
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort))
if err != nil {
return nil, errors.Wrapf(err, "cannot resolve the UDP address")
}
server, err := net.ListenUDP("udp", addr)
if err != nil {
return nil, errors.Wrapf(err, "cannot listen on the UDP port")
}
f, err := server.File()
if err != nil {
return nil, errors.Wrapf(err, "cannot get file for UDP socket")
}
files = append(files, f)
break
case "tcp":
addr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort))
if err != nil {
return nil, errors.Wrapf(err, "cannot resolve the TCP address")
}
server, err := net.ListenTCP("tcp4", addr)
if err != nil {
return nil, errors.Wrapf(err, "cannot listen on the TCP port")
}
f, err := server.File()
if err != nil {
return nil, errors.Wrapf(err, "cannot get file for TCP socket")
}
files = append(files, f)
break
default:
return nil, fmt.Errorf("unknown protocol %s", i.Protocol)
}
}
return files, nil
}
func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string) (err error) {
var stderrBuf bytes.Buffer
@ -259,6 +306,17 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string) (er
cmd.Env = append(r.conmonEnv, fmt.Sprintf("_OCI_SYNCPIPE=%d", 3))
cmd.Env = append(cmd.Env, fmt.Sprintf("_OCI_STARTPIPE=%d", 4))
cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir))
ports, err := bindPorts(ctr.config.PortMappings)
if err != nil {
return err
}
// Leak the port we bound in the conmon process. These fd's won't be used
// by the container and conmon will keep the ports busy so that another
// process cannot use them.
cmd.ExtraFiles = append(cmd.ExtraFiles, ports...)
if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok {
cmd.Env = append(cmd.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notify))
}

View File

@ -65,6 +65,10 @@ var _ = Describe("Podman rmi", func() {
results.Wait(30)
Expect(results.ExitCode()).To(Equal(0))
Expect(results.OutputToString()).To(ContainSubstring("8000"))
ncBusy := podmanTest.SystemExec("nc", []string{"-l", "-p", "80"})
ncBusy.Wait(10)
Expect(ncBusy.ExitCode()).ToNot(Equal(0))
})
It("podman run network expose ports in image metadata", func() {