Skip to content

Commit 70e4d02

Browse files
Merge pull request #24426 from TomSweeneyRedHat/dev/tsweeney/v4.9-rhel_accel302
[v4.9-rhel] libpod: bind ports before network setup
2 parents 1866072 + f0ec38a commit 70e4d02

File tree

7 files changed

+59
-6
lines changed

7 files changed

+59
-6
lines changed

libpod/container.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ type Container struct {
120120
rootlessPortSyncR *os.File
121121
rootlessPortSyncW *os.File
122122

123+
// reservedPorts contains the fds for the bound ports when using the
124+
// bridge network mode as root.
125+
reservedPorts []*os.File
126+
123127
// perNetworkOpts should be set when you want to use special network
124128
// options when calling network setup/teardown. This should be used for
125129
// container restore or network reload for example. Leave this nil if

libpod/container_internal_freebsd.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ func (c *Container) prepare() error {
5151
// Set up network namespace if not already set up
5252
noNetNS := c.state.NetNS == ""
5353
if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS {
54+
c.reservedPorts, createNetNSErr = c.bindPorts()
55+
if createNetNSErr != nil {
56+
return
57+
}
5458
ctrNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c)
5559
if createNetNSErr != nil {
5660
return
@@ -113,6 +117,11 @@ func (c *Container) prepare() error {
113117
logrus.Errorf("Preparing container %s: %v", c.ID(), createErr)
114118
createErr = fmt.Errorf("cleaning up container %s network after setup failure: %w", c.ID(), err)
115119
}
120+
for _, f := range c.reservedPorts {
121+
// make sure to close all ports again on errors
122+
f.Close()
123+
}
124+
c.reservedPorts = nil
116125
return createErr
117126
}
118127

libpod/container_internal_linux.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ func (c *Container) prepare() error {
7676
// Set up network namespace if not already set up
7777
noNetNS := c.state.NetNS == ""
7878
if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS {
79+
c.reservedPorts, createNetNSErr = c.bindPorts()
80+
if createNetNSErr != nil {
81+
return
82+
}
83+
7984
netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c)
8085
if createNetNSErr != nil {
8186
return
@@ -142,6 +147,11 @@ func (c *Container) prepare() error {
142147
}
143148

144149
if createErr != nil {
150+
for _, f := range c.reservedPorts {
151+
// make sure to close all ports again on errors
152+
f.Close()
153+
}
154+
c.reservedPorts = nil
145155
return createErr
146156
}
147157

libpod/networking_common.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package libpod
77
import (
88
"errors"
99
"fmt"
10+
"os"
1011
"regexp"
1112
"sort"
1213

@@ -23,6 +24,16 @@ import (
2324
"github.com/sirupsen/logrus"
2425
)
2526

27+
// bindPorts ports to keep them open via conmon so no other process can use them and we can check if they are in use.
28+
// Note in all cases it is important that we bind before setting up the network to avoid issues were we add firewall
29+
// rules before we even "own" the port.
30+
func (c *Container) bindPorts() ([]*os.File, error) {
31+
if !c.runtime.config.Engine.EnablePortReservation || rootless.IsRootless() || !c.config.NetMode.IsBridge() {
32+
return nil, nil
33+
}
34+
return bindPorts(c.convertPortMappings())
35+
}
36+
2637
// convertPortMappings will remove the HostIP part from the ports when running inside podman machine.
2738
// This is needed because a HostIP of 127.0.0.1 would now allow the gvproxy forwarder to reach to open ports.
2839
// For machine the HostIP must only be used by gvproxy and never in the VM.

libpod/oci_conmon_common.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,17 +1205,22 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
12051205
cmd.Env = append(cmd.Env, conmonEnv...)
12061206
cmd.ExtraFiles = append(cmd.ExtraFiles, childSyncPipe, childStartPipe)
12071207

1208-
if r.reservePorts && !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() {
1209-
ports, err := bindPorts(ctr.convertPortMappings())
1208+
if ctr.config.PostConfigureNetNS {
1209+
// netns was not setup yet but we have to bind ports now so we can leak the fd to conmon
1210+
ports, err := ctr.bindPorts()
12101211
if err != nil {
12111212
return 0, err
12121213
}
12131214
filesToClose = append(filesToClose, ports...)
1214-
12151215
// Leak the port we bound in the conmon process. These fd's won't be used
12161216
// by the container and conmon will keep the ports busy so that another
12171217
// process cannot use them.
12181218
cmd.ExtraFiles = append(cmd.ExtraFiles, ports...)
1219+
} else {
1220+
// ports were bound in ctr.prepare() as we must do it before the netns setup
1221+
filesToClose = append(filesToClose, ctr.reservedPorts...)
1222+
cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.reservedPorts...)
1223+
ctr.reservedPorts = nil
12191224
}
12201225

12211226
if ctr.config.NetMode.IsSlirp4netns() || rootless.IsRootless() {

libpod/oci_util.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ func bindPorts(ports []types.PortMapping) ([]*os.File, error) {
4747
for i := uint16(0); i < port.Range; i++ {
4848
f, err := bindPort(protocol, port.HostIP, port.HostPort+i, isV6, &sctpWarning)
4949
if err != nil {
50-
return files, err
50+
// close all open ports in case of early error so we do not
51+
// rely garbage collector to close them
52+
for _, f := range files {
53+
f.Close()
54+
}
55+
return nil, err
5156
}
5257
if f != nil {
5358
files = append(files, f)

test/system/500-networking.bats

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,25 @@ load helpers.network
5252
$IMAGE /bin/busybox-extras httpd -f -p 80
5353
cid=$output
5454

55+
# Try to bind the same port again, this must fail.
56+
# regression test for https://issues.redhat.com/browse/RHEL-50746
57+
# which caused this command to overwrite the firewall rules as root
58+
# causing the curl commands below to fail
59+
run_podman 126 run --rm -p "$HOST_PORT:80" $IMAGE true
60+
# Note error messages differ between root/rootless, so only check port
61+
# and the part of the error text that is common.
62+
assert "$output" =~ "$HOST_PORT.*ddress already in use" "port in use"
63+
5564
# In that container, create a second file, using exec and redirection
5665
run_podman exec -i myweb sh -c "cat > index2.txt" <<<"$random_2"
5766
# ...verify its contents as seen from container.
5867
run_podman exec -i myweb cat /var/www/index2.txt
5968
is "$output" "$random_2" "exec cat index2.txt"
6069

6170
# Verify http contents: curl from localhost
62-
run curl -s -S $SERVER/index.txt
71+
run curl --max-time 3 -s -S $SERVER/index.txt
6372
is "$output" "$random_1" "curl 127.0.0.1:/index.txt"
64-
run curl -s -S $SERVER/index2.txt
73+
run curl --max-time 3 -s -S $SERVER/index2.txt
6574
is "$output" "$random_2" "curl 127.0.0.1:/index2.txt"
6675

6776
# Verify http contents: wget from a second container

0 commit comments

Comments
 (0)