mirror of https://github.com/containers/podman.git
Merge pull request #23446 from Luap99/bind-ports
libpod: bind ports before network setup
This commit is contained in:
commit
aa077cdcaa
|
@ -118,6 +118,10 @@ type Container struct {
|
||||||
rootlessPortSyncR *os.File
|
rootlessPortSyncR *os.File
|
||||||
rootlessPortSyncW *os.File
|
rootlessPortSyncW *os.File
|
||||||
|
|
||||||
|
// reservedPorts contains the fds for the bound ports when using the
|
||||||
|
// bridge network mode as root.
|
||||||
|
reservedPorts []*os.File
|
||||||
|
|
||||||
// perNetworkOpts should be set when you want to use special network
|
// perNetworkOpts should be set when you want to use special network
|
||||||
// options when calling network setup/teardown. This should be used for
|
// options when calling network setup/teardown. This should be used for
|
||||||
// container restore or network reload for example. Leave this nil if
|
// container restore or network reload for example. Leave this nil if
|
||||||
|
|
|
@ -50,6 +50,10 @@ func (c *Container) prepare() error {
|
||||||
// Set up network namespace if not already set up
|
// Set up network namespace if not already set up
|
||||||
noNetNS := c.state.NetNS == ""
|
noNetNS := c.state.NetNS == ""
|
||||||
if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS {
|
if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS {
|
||||||
|
c.reservedPorts, createNetNSErr = c.bindPorts()
|
||||||
|
if createNetNSErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
ctrNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c)
|
ctrNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c)
|
||||||
if createNetNSErr != nil {
|
if createNetNSErr != nil {
|
||||||
return
|
return
|
||||||
|
@ -112,6 +116,11 @@ func (c *Container) prepare() error {
|
||||||
logrus.Errorf("Preparing container %s: %v", c.ID(), createErr)
|
logrus.Errorf("Preparing container %s: %v", c.ID(), createErr)
|
||||||
createErr = fmt.Errorf("cleaning up container %s network after setup failure: %w", c.ID(), err)
|
createErr = fmt.Errorf("cleaning up container %s network after setup failure: %w", c.ID(), err)
|
||||||
}
|
}
|
||||||
|
for _, f := range c.reservedPorts {
|
||||||
|
// make sure to close all ports again on errors
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
c.reservedPorts = nil
|
||||||
return createErr
|
return createErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,11 @@ func (c *Container) prepare() error {
|
||||||
// Set up network namespace if not already set up
|
// Set up network namespace if not already set up
|
||||||
noNetNS := c.state.NetNS == ""
|
noNetNS := c.state.NetNS == ""
|
||||||
if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS {
|
if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS {
|
||||||
|
c.reservedPorts, createNetNSErr = c.bindPorts()
|
||||||
|
if createNetNSErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c)
|
netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c)
|
||||||
if createNetNSErr != nil {
|
if createNetNSErr != nil {
|
||||||
return
|
return
|
||||||
|
@ -148,6 +153,11 @@ func (c *Container) prepare() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if createErr != nil {
|
if createErr != nil {
|
||||||
|
for _, f := range c.reservedPorts {
|
||||||
|
// make sure to close all ports again on errors
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
c.reservedPorts = nil
|
||||||
return createErr
|
return createErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ package libpod
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -21,6 +22,16 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// bindPorts ports to keep them open via conmon so no other process can use them and we can check if they are in use.
|
||||||
|
// Note in all cases it is important that we bind before setting up the network to avoid issues were we add firewall
|
||||||
|
// rules before we even "own" the port.
|
||||||
|
func (c *Container) bindPorts() ([]*os.File, error) {
|
||||||
|
if !c.runtime.config.Engine.EnablePortReservation || rootless.IsRootless() || !c.config.NetMode.IsBridge() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return bindPorts(c.convertPortMappings())
|
||||||
|
}
|
||||||
|
|
||||||
// convertPortMappings will remove the HostIP part from the ports when running inside podman machine.
|
// convertPortMappings will remove the HostIP part from the ports when running inside podman machine.
|
||||||
// This is needed because a HostIP of 127.0.0.1 would now allow the gvproxy forwarder to reach to open ports.
|
// This is needed because a HostIP of 127.0.0.1 would now allow the gvproxy forwarder to reach to open ports.
|
||||||
// For machine the HostIP must only be used by gvproxy and never in the VM.
|
// For machine the HostIP must only be used by gvproxy and never in the VM.
|
||||||
|
|
|
@ -1216,17 +1216,22 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
|
||||||
cmd.Env = append(cmd.Env, conmonEnv...)
|
cmd.Env = append(cmd.Env, conmonEnv...)
|
||||||
cmd.ExtraFiles = append(cmd.ExtraFiles, childSyncPipe, childStartPipe)
|
cmd.ExtraFiles = append(cmd.ExtraFiles, childSyncPipe, childStartPipe)
|
||||||
|
|
||||||
if r.reservePorts && !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() {
|
if ctr.config.PostConfigureNetNS {
|
||||||
ports, err := bindPorts(ctr.convertPortMappings())
|
// netns was not setup yet but we have to bind ports now so we can leak the fd to conmon
|
||||||
|
ports, err := ctr.bindPorts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
filesToClose = append(filesToClose, ports...)
|
filesToClose = append(filesToClose, ports...)
|
||||||
|
|
||||||
// Leak the port we bound in the conmon process. These fd's won't be used
|
// 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
|
// by the container and conmon will keep the ports busy so that another
|
||||||
// process cannot use them.
|
// process cannot use them.
|
||||||
cmd.ExtraFiles = append(cmd.ExtraFiles, ports...)
|
cmd.ExtraFiles = append(cmd.ExtraFiles, ports...)
|
||||||
|
} else {
|
||||||
|
// ports were bound in ctr.prepare() as we must do it before the netns setup
|
||||||
|
filesToClose = append(filesToClose, ctr.reservedPorts...)
|
||||||
|
cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.reservedPorts...)
|
||||||
|
ctr.reservedPorts = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctr.config.NetMode.IsSlirp4netns() || rootless.IsRootless() {
|
if ctr.config.NetMode.IsSlirp4netns() || rootless.IsRootless() {
|
||||||
|
|
|
@ -46,7 +46,12 @@ func bindPorts(ports []types.PortMapping) ([]*os.File, error) {
|
||||||
for i := uint16(0); i < port.Range; i++ {
|
for i := uint16(0); i < port.Range; i++ {
|
||||||
f, err := bindPort(protocol, port.HostIP, port.HostPort+i, isV6, &sctpWarning)
|
f, err := bindPort(protocol, port.HostIP, port.HostPort+i, isV6, &sctpWarning)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return files, err
|
// close all open ports in case of early error so we do not
|
||||||
|
// rely garbage collector to close them
|
||||||
|
for _, f := range files {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
if f != nil {
|
if f != nil {
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
|
|
|
@ -54,6 +54,15 @@ load helpers.network
|
||||||
$IMAGE /bin/busybox-extras httpd -f -p 80
|
$IMAGE /bin/busybox-extras httpd -f -p 80
|
||||||
cid=$output
|
cid=$output
|
||||||
|
|
||||||
|
# Try to bind the same port again, this must fail.
|
||||||
|
# regression test for https://issues.redhat.com/browse/RHEL-50746
|
||||||
|
# which caused this command to overwrite the firewall rules as root
|
||||||
|
# causing the curl commands below to fail
|
||||||
|
run_podman 126 run --rm -p "$HOST_PORT:80" $IMAGE true
|
||||||
|
# Note error messages differ between root/rootless, so only check port
|
||||||
|
# and the part of the error text that is common.
|
||||||
|
assert "$output" =~ "$HOST_PORT.*ddress already in use" "port in use"
|
||||||
|
|
||||||
# In that container, create a second file, using exec and redirection
|
# In that container, create a second file, using exec and redirection
|
||||||
run_podman exec -i myweb sh -c "cat > index2.txt" <<<"$random_2"
|
run_podman exec -i myweb sh -c "cat > index2.txt" <<<"$random_2"
|
||||||
# ...verify its contents as seen from container.
|
# ...verify its contents as seen from container.
|
||||||
|
@ -61,9 +70,9 @@ load helpers.network
|
||||||
is "$output" "$random_2" "exec cat index2.txt"
|
is "$output" "$random_2" "exec cat index2.txt"
|
||||||
|
|
||||||
# Verify http contents: curl from localhost
|
# Verify http contents: curl from localhost
|
||||||
run curl -s -S $SERVER/index.txt
|
run curl --max-time 3 -s -S $SERVER/index.txt
|
||||||
is "$output" "$random_1" "curl 127.0.0.1:/index.txt"
|
is "$output" "$random_1" "curl 127.0.0.1:/index.txt"
|
||||||
run curl -s -S $SERVER/index2.txt
|
run curl --max-time 3 -s -S $SERVER/index2.txt
|
||||||
is "$output" "$random_2" "curl 127.0.0.1:/index2.txt"
|
is "$output" "$random_2" "curl 127.0.0.1:/index2.txt"
|
||||||
|
|
||||||
# Verify http contents: wget from a second container
|
# Verify http contents: wget from a second container
|
||||||
|
|
Loading…
Reference in New Issue