mirror of
https://github.com/containers/podman.git
synced 2025-07-15 03:02:52 +08:00

For rootful users ports are forwarded via iptables. To make sure no other process tries to use them, libpod will bind the ports and pass the fds to conmon. There seems to be race when a container is restarted because libpod tries to bind the port before the conmon process exited. The problem only hapens with the podman service because it keeps the connection open. Once we have the fd and passed it to conmon the podman service should close the connection. To verify run `sudo ss -tulpn` and check that only the conmon process keeps the port open. Previously you would also see the podman server process listed. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
155 lines
4.3 KiB
Go
155 lines
4.3 KiB
Go
package libpod
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/containers/podman/v3/libpod/define"
|
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// Timeout before declaring that runtime has failed to kill a given
|
|
// container
|
|
const killContainerTimeout = 5 * time.Second
|
|
|
|
// ociError is used to parse the OCI runtime JSON log. It is not part of the
|
|
// OCI runtime specifications, it follows what runc does
|
|
type ociError struct {
|
|
Level string `json:"level,omitempty"`
|
|
Time string `json:"time,omitempty"`
|
|
Msg string `json:"msg,omitempty"`
|
|
}
|
|
|
|
// Create systemd unit name for cgroup scopes
|
|
func createUnitName(prefix string, name string) string {
|
|
return fmt.Sprintf("%s-%s.scope", prefix, name)
|
|
}
|
|
|
|
// Bind ports to keep them closed on the host
|
|
func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) {
|
|
var files []*os.File
|
|
notifySCTP := false
|
|
for _, i := range ports {
|
|
isV6 := net.ParseIP(i.HostIP).To4() == nil
|
|
if i.HostIP == "" {
|
|
isV6 = false
|
|
}
|
|
switch i.Protocol {
|
|
case "udp":
|
|
var (
|
|
addr *net.UDPAddr
|
|
err error
|
|
)
|
|
if isV6 {
|
|
addr, err = net.ResolveUDPAddr("udp6", fmt.Sprintf("[%s]:%d", i.HostIP, i.HostPort))
|
|
} else {
|
|
addr, err = net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort))
|
|
}
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "cannot resolve the UDP address")
|
|
}
|
|
|
|
proto := "udp4"
|
|
if isV6 {
|
|
proto = "udp6"
|
|
}
|
|
server, err := net.ListenUDP(proto, 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)
|
|
// close the listener
|
|
// note that this does not affect the fd, see the godoc for server.File()
|
|
err = server.Close()
|
|
if err != nil {
|
|
logrus.Warnf("failed to close connection: %v", err)
|
|
}
|
|
|
|
case "tcp":
|
|
var (
|
|
addr *net.TCPAddr
|
|
err error
|
|
)
|
|
if isV6 {
|
|
addr, err = net.ResolveTCPAddr("tcp6", fmt.Sprintf("[%s]:%d", i.HostIP, i.HostPort))
|
|
} else {
|
|
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")
|
|
}
|
|
|
|
proto := "tcp4"
|
|
if isV6 {
|
|
proto = "tcp6"
|
|
}
|
|
server, err := net.ListenTCP(proto, 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)
|
|
// close the listener
|
|
// note that this does not affect the fd, see the godoc for server.File()
|
|
err = server.Close()
|
|
if err != nil {
|
|
logrus.Warnf("failed to close connection: %v", err)
|
|
}
|
|
|
|
case "sctp":
|
|
if !notifySCTP {
|
|
notifySCTP = true
|
|
logrus.Warnf("port reservation for SCTP is not supported")
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("unknown protocol %s", i.Protocol)
|
|
}
|
|
}
|
|
return files, nil
|
|
}
|
|
|
|
func getOCIRuntimeError(runtimeMsg string) error {
|
|
includeFullOutput := logrus.GetLevel() == logrus.DebugLevel
|
|
|
|
if match := regexp.MustCompile("(?i).*permission denied.*|.*operation not permitted.*").FindString(runtimeMsg); match != "" {
|
|
errStr := match
|
|
if includeFullOutput {
|
|
errStr = runtimeMsg
|
|
}
|
|
return errors.Wrapf(define.ErrOCIRuntimePermissionDenied, "%s", strings.Trim(errStr, "\n"))
|
|
}
|
|
if match := regexp.MustCompile("(?i).*executable file not found in.*|.*no such file or directory.*").FindString(runtimeMsg); match != "" {
|
|
errStr := match
|
|
if includeFullOutput {
|
|
errStr = runtimeMsg
|
|
}
|
|
return errors.Wrapf(define.ErrOCIRuntimeNotFound, "%s", strings.Trim(errStr, "\n"))
|
|
}
|
|
if match := regexp.MustCompile("`/proc/[a-z0-9-].+/attr.*`").FindString(runtimeMsg); match != "" {
|
|
errStr := match
|
|
if includeFullOutput {
|
|
errStr = runtimeMsg
|
|
}
|
|
if strings.HasSuffix(match, "/exec`") {
|
|
return errors.Wrapf(define.ErrSetSecurityAttribute, "%s", strings.Trim(errStr, "\n"))
|
|
} else if strings.HasSuffix(match, "/current`") {
|
|
return errors.Wrapf(define.ErrGetSecurityAttribute, "%s", strings.Trim(errStr, "\n"))
|
|
}
|
|
return errors.Wrapf(define.ErrSecurityAttribute, "%s", strings.Trim(errStr, "\n"))
|
|
}
|
|
return errors.Wrapf(define.ErrOCIRuntime, "%s", strings.Trim(runtimeMsg, "\n"))
|
|
}
|