Files
podman/libpod/oci_util.go
Paul Holzinger d3f0f09ad9 libpod: rootful close binded ports
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>
2021-09-14 11:13:28 +02:00

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"))
}