Files
podman/pkg/namespaces/namespaces.go
Stefano Brivio aa47e05ae4 libpod: Add pasta networking mode
Conceptually equivalent to networking by means of slirp4netns(1),
with a few practical differences:

- pasta(1) forks to background once networking is configured in the
  namespace and quits on its own once the namespace is deleted:
  file descriptor synchronisation and PID tracking are not needed

- port forwarding is configured via command line options at start-up,
  instead of an API socket: this is taken care of right away as we're
  about to start pasta

- there's no need for further selection of port forwarding modes:
  pasta behaves similarly to containers-rootlessport for local binds
  (splice() instead of read()/write() pairs, without L2-L4
  translation), and keeps the original source address for non-local
  connections like slirp4netns does

- IPv6 is not an experimental feature, and enabled by default. IPv6
  port forwarding is supported

- by default, addresses and routes are copied from the host, that is,
  container users will see the same IP address and routes as if they
  were in the init namespace context. The interface name is also
  sourced from the host upstream interface with the first default
  route in the routing table. This is also configurable as documented

- sandboxing and seccomp(2) policies cannot be disabled

- only rootless mode is supported.

See https://passt.top for more details about pasta.

Also add a link to the maintained build of pasta(1) manual as valid
in the man page cross-reference checks: that's where the man page
for the latest build actually is -- it's not on Github and it doesn't
match any existing pattern, so add it explicitly.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
2022-11-08 00:16:35 +01:00

472 lines
13 KiB
Go

package namespaces
import (
"fmt"
"strconv"
"strings"
"github.com/containers/storage/types"
)
const (
bridgeType = "bridge"
containerType = "container"
defaultType = "default"
hostType = "host"
noneType = "none"
nsType = "ns"
podType = "pod"
privateType = "private"
shareableType = "shareable"
slirpType = "slirp4netns"
pastaType = "pasta"
)
// KeepIDUserNsOptions defines how to keepIDmatically create a user namespace.
type KeepIDUserNsOptions struct {
// UID is the target uid in the user namespace.
UID *uint32
// GID is the target uid in the user namespace.
GID *uint32
}
// CgroupMode represents cgroup mode in the container.
type CgroupMode string
// IsHost indicates whether the container uses the host's cgroup.
func (n CgroupMode) IsHost() bool {
return n == hostType
}
// IsDefaultValue indicates whether the cgroup namespace has the default value.
func (n CgroupMode) IsDefaultValue() bool {
return n == "" || n == defaultType
}
// IsNS indicates a cgroup namespace passed in by path (ns:<path>)
func (n CgroupMode) IsNS() bool {
return strings.HasPrefix(string(n), nsType)
}
// NS gets the path associated with a ns:<path> cgroup ns
func (n CgroupMode) NS() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
}
// IsContainer indicates whether the container uses a new cgroup namespace.
func (n CgroupMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
}
// Container returns the name of the container whose cgroup namespace is going to be used.
func (n CgroupMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
}
// IsPrivate indicates whether the container uses the a private cgroup.
func (n CgroupMode) IsPrivate() bool {
return n == privateType
}
// Valid indicates whether the Cgroup namespace is valid.
func (n CgroupMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", hostType, privateType, nsType:
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
}
default:
return false
}
return true
}
// UsernsMode represents userns mode in the container.
type UsernsMode string
// IsHost indicates whether the container uses the host's userns.
func (n UsernsMode) IsHost() bool {
return n == hostType
}
// IsKeepID indicates whether container uses a mapping where the (uid, gid) on the host is kept inside of the namespace.
func (n UsernsMode) IsKeepID() bool {
parts := strings.Split(string(n), ":")
return parts[0] == "keep-id"
}
// IsNoMap indicates whether container uses a mapping where the (uid, gid) on the host is not present in the namespace.
func (n UsernsMode) IsNoMap() bool {
return n == "nomap"
}
// IsAuto indicates whether container uses the "auto" userns mode.
func (n UsernsMode) IsAuto() bool {
parts := strings.Split(string(n), ":")
return parts[0] == "auto"
}
// IsDefaultValue indicates whether the user namespace has the default value.
func (n UsernsMode) IsDefaultValue() bool {
return n == "" || n == defaultType
}
// GetAutoOptions returns a AutoUserNsOptions with the settings to automatically set up
// a user namespace.
func (n UsernsMode) GetAutoOptions() (*types.AutoUserNsOptions, error) {
parts := strings.SplitN(string(n), ":", 2)
if parts[0] != "auto" {
return nil, fmt.Errorf("wrong user namespace mode")
}
options := types.AutoUserNsOptions{}
if len(parts) == 1 {
return &options, nil
}
for _, o := range strings.Split(parts[1], ",") {
v := strings.SplitN(o, "=", 2)
if len(v) != 2 {
return nil, fmt.Errorf("invalid option specified: %q", o)
}
switch v[0] {
case "size":
s, err := strconv.ParseUint(v[1], 10, 32)
if err != nil {
return nil, err
}
options.Size = uint32(s)
case "uidmapping":
mapping, err := types.ParseIDMapping([]string{v[1]}, nil, "", "")
if err != nil {
return nil, err
}
options.AdditionalUIDMappings = append(options.AdditionalUIDMappings, mapping.UIDMap...)
case "gidmapping":
mapping, err := types.ParseIDMapping(nil, []string{v[1]}, "", "")
if err != nil {
return nil, err
}
options.AdditionalGIDMappings = append(options.AdditionalGIDMappings, mapping.GIDMap...)
default:
return nil, fmt.Errorf("unknown option specified: %q", v[0])
}
}
return &options, nil
}
// GetKeepIDOptions returns a KeepIDUserNsOptions with the settings to keepIDmatically set up
// a user namespace.
func (n UsernsMode) GetKeepIDOptions() (*KeepIDUserNsOptions, error) {
parts := strings.SplitN(string(n), ":", 2)
if parts[0] != "keep-id" {
return nil, fmt.Errorf("wrong user namespace mode")
}
options := KeepIDUserNsOptions{}
if len(parts) == 1 {
return &options, nil
}
for _, o := range strings.Split(parts[1], ",") {
v := strings.SplitN(o, "=", 2)
if len(v) != 2 {
return nil, fmt.Errorf("invalid option specified: %q", o)
}
switch v[0] {
case "uid":
s, err := strconv.ParseUint(v[1], 10, 32)
if err != nil {
return nil, err
}
v := uint32(s)
options.UID = &v
case "gid":
s, err := strconv.ParseUint(v[1], 10, 32)
if err != nil {
return nil, err
}
v := uint32(s)
options.GID = &v
default:
return nil, fmt.Errorf("unknown option specified: %q", v[0])
}
}
return &options, nil
}
// IsPrivate indicates whether the container uses the a private userns.
func (n UsernsMode) IsPrivate() bool {
return !(n.IsHost() || n.IsContainer())
}
// Valid indicates whether the userns is valid.
func (n UsernsMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", privateType, hostType, "keep-id", nsType, "auto", "nomap":
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
}
default:
return false
}
return true
}
// IsNS indicates a userns namespace passed in by path (ns:<path>)
func (n UsernsMode) IsNS() bool {
return strings.HasPrefix(string(n), "ns:")
}
// NS gets the path associated with a ns:<path> userns ns
func (n UsernsMode) NS() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
}
// IsContainer indicates whether container uses a container userns.
func (n UsernsMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
}
// Container is the id of the container which network this container is connected to.
func (n UsernsMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
}
// UTSMode represents the UTS namespace of the container.
type UTSMode string
// IsPrivate indicates whether the container uses its private UTS namespace.
func (n UTSMode) IsPrivate() bool {
return !(n.IsHost())
}
// IsHost indicates whether the container uses the host's UTS namespace.
func (n UTSMode) IsHost() bool {
return n == hostType
}
// IsContainer indicates whether the container uses a container's UTS namespace.
func (n UTSMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
}
// Container returns the name of the container whose uts namespace is going to be used.
func (n UTSMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
}
// Valid indicates whether the UTS namespace is valid.
func (n UTSMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", privateType, hostType:
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
}
default:
return false
}
return true
}
// IpcMode represents the container ipc stack.
type IpcMode string
// IsPrivate indicates whether the container uses its own private ipc namespace which cannot be shared.
func (n IpcMode) IsPrivate() bool {
return n == privateType
}
// IsHost indicates whether the container shares the host's ipc namespace.
func (n IpcMode) IsHost() bool {
return n == hostType
}
// IsShareable indicates whether the container uses its own shareable ipc namespace which can be shared.
func (n IpcMode) IsShareable() bool {
return n == shareableType
}
// IsContainer indicates whether the container uses another container's ipc namespace.
func (n IpcMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
}
// IsNone indicates whether container IpcMode is set to "none".
func (n IpcMode) IsNone() bool {
return n == noneType
}
// IsEmpty indicates whether container IpcMode is empty
func (n IpcMode) IsEmpty() bool {
return n == ""
}
// Valid indicates whether the ipc mode is valid.
func (n IpcMode) Valid() bool {
return n.IsEmpty() || n.IsNone() || n.IsPrivate() || n.IsHost() || n.IsShareable() || n.IsContainer()
}
// Container returns the name of the container ipc stack is going to be used.
func (n IpcMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
}
// PidMode represents the pid namespace of the container.
type PidMode string
// IsPrivate indicates whether the container uses its own new pid namespace.
func (n PidMode) IsPrivate() bool {
return !(n.IsHost() || n.IsContainer())
}
// IsHost indicates whether the container uses the host's pid namespace.
func (n PidMode) IsHost() bool {
return n == hostType
}
// IsContainer indicates whether the container uses a container's pid namespace.
func (n PidMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
}
// Valid indicates whether the pid namespace is valid.
func (n PidMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", privateType, hostType:
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
}
default:
return false
}
return true
}
// Container returns the name of the container whose pid namespace is going to be used.
func (n PidMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
}
// NetworkMode represents the container network stack.
type NetworkMode string
// IsNone indicates whether container isn't using a network stack.
func (n NetworkMode) IsNone() bool {
return n == noneType
}
// IsHost indicates whether the container uses the host's network stack.
func (n NetworkMode) IsHost() bool {
return n == hostType
}
// IsDefault indicates whether container uses the default network stack.
func (n NetworkMode) IsDefault() bool {
return n == defaultType
}
// IsPrivate indicates whether container uses its private network stack.
func (n NetworkMode) IsPrivate() bool {
return !(n.IsHost() || n.IsContainer())
}
// IsContainer indicates whether container uses a container network stack.
func (n NetworkMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == containerType
}
// Container is the id of the container which network this container is connected to.
func (n NetworkMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
}
// UserDefined indicates user-created network
func (n NetworkMode) UserDefined() string {
if n.IsUserDefined() {
return string(n)
}
return ""
}
// IsBridge indicates whether container uses the bridge network stack
func (n NetworkMode) IsBridge() bool {
return n == bridgeType
}
// IsSlirp4netns indicates if we are running a rootless network stack
func (n NetworkMode) IsSlirp4netns() bool {
return n == slirpType || strings.HasPrefix(string(n), slirpType+":")
}
// IsPasta indicates if we are running a rootless network stack using pasta
func (n NetworkMode) IsPasta() bool {
return n == pastaType || strings.HasPrefix(string(n), pastaType+":")
}
// IsNS indicates a network namespace passed in by path (ns:<path>)
func (n NetworkMode) IsNS() bool {
return strings.HasPrefix(string(n), nsType)
}
// NS gets the path associated with a ns:<path> network ns
func (n NetworkMode) NS() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
}
// IsPod returns whether the network refers to pod networking
func (n NetworkMode) IsPod() bool {
return n == podType
}
// IsUserDefined indicates user-created network
func (n NetworkMode) IsUserDefined() bool {
return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() && !n.IsSlirp4netns() && !n.IsPasta() && !n.IsNS()
}